春节期间学习了v8引擎exploit相关的知识,挑了几道经典题目练手:
- PlaidCTF2018:roll a d8
- *CTF2019:OOB
- GoogleCTF2018: Just-in-time
各路大神的writeup已经足够详细了,这里只记录一下解决v8题目比较关键的知识点。
首先比较关键的是一些基本对象的内存结构,v8题目的漏洞的落脚点基本都是数组越界,然后通过越界构造类型混淆,或者直接构造任意读写原语。因此数组相关结构体的布局将是把越界读写转换为任意读写的关键。这里采用asciiflow简单画了示意图。至于Tagged value和其他关键细节可以参考sakura事无巨细的博文v8 exploit.
JSObject
1 | JSObject |
JSFunction
1 | JSFunction |
这里特别标记js函数jit code的偏移地址,在6.7版本之前jit code的内存是RWX权限,所以完成任意读写原语以后直接把自定义js函数的jit code改写成shellcode就完成get shell。
JSArray
1 | FixedArray |
JSArrayBuffer
1 | JSTypedArray JSTypedArray |
左边的布局是用以下语句生成出的内存布局,一般适用是ArrayBuffer空间比较大的情况:
1 | var data_buf = new ArrayBuffer(24); |
这种情况可以通过改写JSArrayBuffer
->kBackingStoreOffset
指针来完成任意读写。
但是有时会出现内存布局不满足越界读写范围的情况,这时可以尝试另一种声明方式:
1 | var data_bf = new Float64Array(7); |
这种方式会生成右边的内存布局,不同之处在于数值存放的对象通过inline的方式直接存放在JSArrayBuffer
对象当中。利用方式是将JSArrayBuffer
->kElementOffset
指针改成addr - 0x20n | 1n
(addr为目标内存地址),随后即可通过对Float64Array
的操作来完成任意读写。
BigUint64 <-> Float64
另外一个js engine exploit需要常用到的小工具就是浮点数和64位整数之间的转换,之前都是用saleo exp中的Int64
对象操作,后来看到Google CTF的官方exp里面用到了新的BigUint64支持,让这种转换变得更加简单直接了。
1 | let conversion_buffer = new ArrayBuffer(8); |
本篇没有多少新贡献,仅是题目复现后的小小总结。
一拖又拖再拖的复工日期容易让人忘了初心,随便写点东西来振作精神好了。