v8漏洞练习笔记_starctf2019 oob
v8内存结构
v8变量内存结构,新版v8使用了指针压缩,基址存于R13寄存器,指针通过32为存储,最后一位表示tag,为1时会与R13中的基址进行运算
1 | DebugPrint: 0x101f080c5e09: [JSArray] |
其中elements是实际存储数据的对象元素,所以实际上是v8对变量做了2层封装,data->elements->JSArrayObject,通过map结构存储.
1 | pwndbg> x/10gx 0x101f080c5e08 |
两个结构的length均做了*2处理.
其中描述了这个变量的属性信息,包括类型,长度,element等
1 | 0x101f08281891: [Map] |
实际内存结构 { map:32bit properties:32bit elements } elements结构 { map, } 变量在内存中的结构是一致的,并且大小为64bit,指针类型变量被压缩,占32bit.
starctf 2019 oob
diff片段,增加了一个oob函数,函数逻辑为当参数为1,也就是不传参,只有一个this参数时,会读取数组length索引的值,不为1,则会将传参数据写入length索引处,两种方式都会造成数组访问越界.
1 | @@ -1668,6 +1668,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, |
构造这样一个JS文件,对oob进行测试.
1 | var f64 = new Float64Array(1); |
通过越界访问读取到的数据,刚好是a的map.
1 | job 0x062a0214e041 |
在a.oob(2)执行后,a对象的map字段被修改为2的浮点数表示.
1 | pwndbg> x/8gx 0x062a0214e040 |
现在可以通过oob函数对变量的map字段进行任意读取写入,然后对变量的类型进行随意的更改. 可以混淆对象数组和浮点数组的类型,用于读取对象内存地址或者将一个浮点数伪造成一个js对象.因为v8依赖map字段判断变量类型. 实现任意对象内存地址读取,与fake js对象的函数.
1 | //leak obj memory address |
测试
1 | var f64 = new Float64Array(1); |
成功泄露 testobj地址
1 | ➜ v8 git:(6dc88c191f) ✗ ./out/x64_startctf.release/d8 --allow-natives-syntax ./test.js |
实现任意地址读写
实现任意地址读写可以通过fake一个对象,这个对象的任何属性我们都是可控的,然后将elements字段修改为我们要读取或修改的内存地址,就可以实现任意地址读写. 但是有几个问题需要解决,需要查找elements的内存地址,可以通过elements字段与map字段的偏移计算. 可以看出,elements字段为0x2b131b9cec19,位于map字段的低地址处,与存储map字段的地址:0x2b131b9cec48,偏移为0x10.
1 | job 0x2b131b9cec49 |
知道了变量的内存结构,下面开始手动构造一个arr,通过addressof获取这个变量elements元素值的地址,并将其fake为一个对象.然后通过fake_arr修改fake对象的elements字段,并通过fake对象读取或写入,可以实现任意地址的读写操作.
1 | //leak obj memory address |
通过wasm执行shellcode
wasm可以将js生成为机器码执行,运行时会开辟一段具有rwxp属性的内存空间用于执行wasm code,但是本身无法调用一些系统库,因为做了限制,调用一些系统库会报错,但是我们可以找到这个内存空间,将自己的shellcode写入其中,覆盖wasm code,然后调用wasm执行. wasm demo,可以将c 转换为wasm code的网站:https://wasdk.github.io/WasmFiddle/
1 | int main() { |
使用vmmap命令可以看到这个内存空间
1 | 0x2b7465e6a000 0x2b7465e6b000 rwxp 1000 0 [anon_2b7465e6a] |
这片内存空间肯定是跟我们输出的函数地址是有关联的,因为wasm会在这片空间中执行.
1 | pwndbg> job 0x07a6bbc611f9 |
通过fun_addr->share_info->data->instance+0x88这个结构,最终找到的这块内存的起始地址, 或者通过wasmInstance+0x88也可以.
1 | job 0x07a6bbc611c1 |
编写泄露这块内存页的代码
1 | var instance_addr = addressOf(wasmInstance); |
通过ArrayBuffer和DataView可以很方便的处理shellcode,将ArrayBuffer中存储数据的backing_store修改为rwx的内存页
1 | job 0x3d5eba7d0121 |
完整exp
1 | var f64 = new Float64Array(1); |
1 | $ df -h |