V8通用利用链
(咕咕咕了好久……)基础pwn暂时告一段落了,本来打算开kernel的,但XCTF见到了V8所以研究一下。配环境真坐牢……本来都配好了虚拟机不小心被我玩崩了还没备份(╯▔皿▔)╯还因为一些很屑的原因(少加了双引号(ˉ▽ˉ;)…)又花了好久再配了一遍……
ps:目前的学习按照从“0开始学V8漏洞利用”系列进行
WASM
做pwn题的时候我们的目标往往就是执行system(‘/bin/sh’),V8利用的目标就是执行任意shellcode,要达成这个目标我们就需要读写相关漏洞+一段rwx的内存
wasm可以为我们制造rwx的内存
ps:wasm无法执行shellcode(还不清楚为啥,之后再研究),所以直接写shellcode什么的别想了o(  ̄▽ ̄)ブ
pps:在别的博客里还有用常规堆题思路做的,之后再研究
所以我们的思路就是先生成一段合法的wasm再写入非法的shellcode
test.js如下:
1 |
|
第二个断点时vmmap一下有一段rwx的内存
变量f(函数对象)的相关信息:
经调试我们可以发现f.shared_info.data.instance=&wasmInstance(先不纠结这些属性到底是啥)
shared_info:
data:
wasmInstance:
instance:
可见rwx的地址写在instance+0x68的位置,我们要把shellcode写在这
任意读写
内存布局
test.js如下:
1 |
|
首先来看a的内存布局:
0x142508049970-0x142508049980的部分是a的结构体
V8将最后一位置1表示指针,置0表示SMI当前版本的V8对地址进行了压缩,因为高32bit的地址值是一样的
所以a的结构如下:
1
|32 bit map addr|32 bit properties addr|32 bit elements addr|32 bit length|
elements结构内存布局如下:
0x142508049960-0x142508049970的部分是elements结构
elements的结构如下:
1
|32 bit map addr|32 bit length|value|
发现elements结构之后是紧接着就是a的结构,如果让elements溢出我们就能控制a的map和length结构
b的内存:
c的内存:
c的内存和a的内存分布基本一致
任意变量地址读
JSArray用map结构来区分储存的数据类型(elements kind),如果我们将c的map地址改成a的,那么执行c[0]时就会将b的地址当成浮点数来解析(类型混淆),可以用来泄露变量地址,步骤如下:
- 将c[0]的值设置为想要获取地址的变量,比如a
- 通过漏洞将c的map地址改成a的
- 读取c[0]的值,该值为a的低32bit地址
上述步骤可被封装为addressOf函数
伪造对象
同理我们也可以把浮点型数组变成对象型数组,步骤如下:
- 将a[0]的值设为构造的对象地址+1
- 通过漏洞将a的map地址修改成c的
- 获取a[0]的值
这个过程可以被封装成fakeObj函数
任意读
构造这样一个变量:
1 |
|
变量结构如下:
1 |
|
可以用addressOf获取fake_array地址,-0x10得到fake_object地址(用double_array_map伪造map和properties,用itof(0x4141414141414141)伪造elements和length),然后使用fakeObj函数将浮点数组伪造成对象数组
以上过程可以打包成任意读函数read64:
1 |
|
任意写
同理也能构造任意写函数write64:
1 |
|
写shellcode
但上述任意写没法把shellcode写入rwx区域,因为写入的地址=实际地址-0x8+0x1,还需要伪造64bit的map和length,但需要写入shellcode的地址是rwx地址段的起始位置,所以我们无法伪造map和length(我的理解是正常的elements结构有map和length且正常改写需要合法的这两个结构),需要另辟蹊径
测试代码test.js如下:
1 |
|
data_buf变量的结构如下:
再看看backing_store字段的值:
double型的2.0的十六进制表示就是0x4000000000000000,没有map和length,我们可以利用此类型将shellcode写入rwx内存
看看data_buf的内存布局:
backing_store字段在data_buf+0x1c
将上述步骤封装成copy_shellcode_to_rwx函数:
1 |
|
类型混淆类漏洞模板
1 |
|