[Pwnable.kr] passcode writeup – Toddler’s bottle

As the hint states, this level’s goal is to try and hack a passcode based login program. If we view the source code and/or execute the program, we’ll notice that it’s straightforward, it asks you for your name, then enter two passcodes, and then the program compares those two passcodes with the values 338150 and 13371337. So, why do we have to hack it, it’s easy right ? All we have to do is give these values to the program and that’s it, right ? Well, no, there’s a catch.

But just for the sake of it, let’s try and execute the program and see for ourselves :

We got a “segmentation fault”. Let’s look more closely at the source code, and pay attention to the login()  function :

Notice the two scanf()  function calls; as you may know, scanf()  actually takes the address of the buffer and not the variable itself, but here the author of the program passed the variable to it, instead of the address :

And that’s why we get the segmentation fault error.

Let’s analyse the situation we have here. The function scanf()  normally takes an address, and writes the input given by the user into the memory case pointed at by that address. Now, what if instead of the address, we give it the variable itself … ? Well, it’ll consider the content of the variable as an address and will act like it, meaning it’ll convert whatever integer value contained in the variable into an address (not really converting, just trying to explain the idea), and it’ll put the input of the user into the memory case pointed at by the “convert” address.

Let’s look at the disassembly to get a somewhat clearer idea :

Notice something in the lines I marked with (=>). The program is using the instruction mov , that copies the value pointed at by the address [ebp-0x10]. But if the program was well written, it should have been compiled to  lea -0xc(%ebp),%edx. The instruction lea , stands for Load Effective Address, and it will calculate the address [ebp-0x10] and loads the address into the register, NOT the value.

Let’s think how can we use this to our benefit…

The program we have, does give scanf()  a variable value instead of an address, which means, if we succeed in overwriting the initial value of the variable passcode1 , we’ll practically have the power to write any value, anywhere in the memory.

So, how can we overwrite the initial value of passcode1 ? One thought that comes to mind, is exploiting the first step of the program, when we write our names, and try to trigger a buffer overflow.

Let’s analyse this possibility. First of all let’s review the function welcome() :

Notice the %100s in the call  scanf("%100s", name); , which means the program will only consider the first 100 character we input.

Let’s look at welcome()  disassembly :

This time, scanf()  did receive an address, because the variable name is a pointer to an array, so as you may notice in the line I marked with (=>), the instruction used is indeed  lea -0x70(%ebp),%edx , so we can guess that the variable name  is placed at the address [ebp-0x70]. Since we want to overflow the variable name  to overwrite the variable passcode1 , we need to know the offset between these two variables, so we can exactly where we can put the address of the memory case we want to control (more on this soon).

Let’s go back to the disassembly of the function login() , and look at the instruction executed before the first scanf()  call, that concerns the variable passcode1  :

Here we can safely make a guess that passcode1  is located at [ebp-0x10].

We have what we need, let’s calculate the offset : 0x70-0x10 = 96

So we can control the content of passcode1, by feeding the variable name 96 characters, and the last 4 bytes will overwrite the initial value of passcode1 .

Now, we almost have everything we need, but what place of the memory should we try to control, and what should we put in it ?

After some thinking, it seems that the reasonable thing to do, that might work, is overwriting the address of some function that is used by the program, in the GOT table (Global Offset Table). The GOT table is used to resolve the addresses of functions that are not known during linking and are left to the dynamic linker at run time it is similar to the PLT table (Procedure Linkage Table).

We can see what are the function that are called before system() , we have fflush()  and printf() .

I decided to overwrite the address of the function fflush() . There are two ways to get the address of this function from the GOT table. Either we can follow the succession of jumps to it by looking in the disassembly of the function fflush()  in gdb :

Or we can use the command  objdump -R passcode to get the addresses in the table :

Both methods enabled us to get the address of fflush()  which is  0x804a004 .

After this, it seems obvious what value we’re going to put in the address we just got : we will give scanf()  the address of the system instruction in the program, that will read for us the content of the flag file:

It’s clear now that the value we have to put in the address we got is  0x80487af , so that  scanf()  will overwrite the address of fflush() with the address of the system instruction, and it’ll jump directly to it, skipping the part where the program verifies the passcodes.

Let’s sum up our findings so far, so we can write our payload :

  • We need to feed the variable name  with 96 characters to overwrite the initial value of passcode1
  • The address of fflush()  is 0x804a004
  • The address we need to jump to is  0x080485e3 = 134514147

Now we can write the payload, and it looks something like this :

Alright, let’s try it and see :

It worked !

See ya 😀

6 thoughts on “[Pwnable.kr] passcode writeup – Toddler’s bottle

  1. hey just a question here. Im new to reversing and exploiting and i have a question. i understand, i think, that you overwrite the passcode1 with the addres of fflush. i dont undersant how when fflush is executed it automatically go to 0x080485e3. I dont really know what you are overwriting with the int 134514147. How is disass fflush looks when u overwrite it? thanks !

    1. Hey fela,
      As I said, the int 134514147 is exactly 0x080485e3 in hex. So when we feed the program with that int, it’ll overwrite the address we specified (0x804a004) with the value 0x080485e3. So when the program will call fflush(), it’ll execute this instruction :
      jmp *0x804a004
      Which means it’ll jump to the address specified in the address 0x804a004 (notice the * which means it’s a pointer). Since we overwrote it with the address 0x080485e3, it’ll automatically jump to it.
      I hope I was clear,I can make more explanation if you need.

  2. Hello, nrjfl0w
    Amazing writeup, but some clauses are still not clear for me. I ran gdb no less than 30 times, got the main idea, but …
    Why overlapping is present? I mean, why the argument of the printf is located in that buf[100]? Disappointed.

Leave a Reply

Your email address will not be published. Required fields are marked *