导语:卡巴斯基研究人员将VBScript编译成p代码,在字节码级别启用VBScript调试,这有助于分析漏洞利用并了解VBScript的运行方式。CVE-2018-8174的案例表明,当内存分配具有高度可预测性时,use-after-free漏洞很容易被利用。
4月下旬,我们在沙箱中发现了一个新的Internet Explorer 0 day漏洞CVE-2018-8174并写了一篇描述文章。该漏洞使用了CVE-2014-6332 poc中一种众所周知的技术,它基本上“破坏”了两个内存对象,并将一个对象的类型更改为Array(用于对地址空间的读/写访问),另一个对象更改为为Integer用于获取任意对象的地址。
CVE-2014-6332是针对写入任意内存位置的整数溢出利用,但我的兴趣在于如何调整此技术以利用use-after-free漏洞。要回答这个问题,让我们考虑一下VBScript解释器的内部结构。
无文档记录的平台
调试VBScript可执行文件是一项繁琐的工作。在脚本执行之前,它被编译成p代码,然后由虚拟机解释。没有找到关于此虚拟机的内部结构及其说明的开源信息。我花了很多精力来追踪几个网页,其中包含1999年和2004年的微软工程师报告,这些报告与p代码相关。那里有足够的信息让我完全逆向所有VM指令并写一个反汇编程序!在我们的Github存储库中可以找到用于在IDA Pro和WinDBG调试器的内存中反汇编VBScript p代码的最终脚本。
通过理解解释的代码,我们可以精确地监视脚本的执行:可以获得任何给定时刻执行代码的完整信息,并且可以观察脚本创建和引用的所有对象。所有这些都有利于分析。
运行反汇编脚本的最佳位置是CScriptRuntime :: RunNoEH函数,它直接解释p代码。
CScriptRuntime类中的重要字段
CScriptRuntime类包含有关解释器状态的所有信息:局部变量,函数参数,指向堆栈顶部和当前指令的指针,以及编译脚本的地址。
VBScript虚拟机是面向堆栈的,包含100多条指令。
所有变量(本地参数和堆栈上的变量)都表示为16个字节的VARIANT结构,其中高位字表示数据类型。某些类型值在相关的MSDN页面上给出。
CVE-2018-8174 利用
下面是类'Class1'的代码和反汇编的p代码:
函数34是类'Class1'的构造函数。
OP_CreateClass指令调用VBScriptClass :: Create函数来创建VBScriptClass对象。
OP_FnBindEx和OP_CreateVar指令尝试获取参数中传递的变量,这些参数尚不存在,它们由VBScriptClass :: CreateVar函数创建。
此图显示了如何从VBScriptClass对象获取变量。变量的值存储在VVAL结构中:
要了解利用,非常重要的一点是,了解变量在VBScriptClass结构中的表示方式。
当在function 36 ('SetProp')中执行OP_NamedSt'mem'指令时,它调用先前堆叠的类的实例的默认属性Getter,然后将返回的值存储在变量'mem'中。
***BOS(8292,8301)*** mem=Value ***** 0000OP_Bos1 0 0002OP_LocalAdr -1 <-------- put argument on stack 0005OP_NamedSt ‘mem’ <-------- if it's a class dispatcher with Default Property Getter, call and store returned value in mem
下面是function 30 (p)的代码和反汇编的p代码,在执行OP_NamedSt指令期间调用它:
该函数的第一个基本块是:
***BOS(8626,8656)*** P=CDbl(“174088534690791e-324”) ***** 0000OP_Bos1 0 0002OP_StrConst ‘174088534690791e-324’ 0007OP_CallNmdAdr’CDbl’ 1 000EOP_LocalSt 0
该块将字符串'174088534690791e-324'转换为VARIANT ,并将其存储在本地变量0中,为函数的返回值保留。
设置好返回值但返回之前,此函数执行:
For IIIl=0 To 6 IIIlI(IIIl)=0 Next
这会调用“Class1”实例的garbage collector,并导致悬空指针引用,就是我们之前讨论过的Class_Terminate()中的use-after-free漏洞。
***BOS(8855,8874)*** Set llII=New Class2 ***** 0047OP_Bos1 4 0049OP_InitClass ‘Class2’ 004EOP_LocalSet 1
OP_InitClass'Class2'指令在先前释放的VBScriptClass的位置创建类'Class1'的“evil twin”实例,该实例仍由function 36 ('SetProp')中的OP_NamedSt 'mem'指令引用。
“Class2”类是“Class1”类的“evil twin”:
内存中变量的位置是可预测的。VVAL结构占用的数据量等于0x32 + UTF-16变量名的长度。
下面的图表显示了在分配'Class2'代替'Class1'时,'Class1'变量相对于'Class2'变量的位置。
当function 36 ('SetProp')中的OP_NamedSt 'mem'指令执行完成时,function 30 (p)返回的值通过Class1中VVAL 'mem'的悬空指针写入存储器,覆盖 Class2中的VVAL 'mem'。
因此,String类型的对象被转换为Array类型的对象,之前被认为是字符串的数据被视为Array控件结构,允许访问进程的整个地址空间。
总结
我们的脚本将VBScript编译成p代码,在字节码级别启用VBScript调试,这有助于分析漏洞利用并了解VBScript的运行方式。此脚本位于我们的Github存储库。
CVE-2018-8174的案例表明,当内存分配具有高度可预测性时,use-after-free漏洞很容易被利用。野外攻击针对旧版Windows。在Windows 7和Windows 8.1中最有可能发生其利用所需的内存中对象的位置。
自动漏洞利用保护(Automatic Exploit Protection (AEP))是卡巴斯基实验室产品的一部分,它通过以下判决阻止漏洞利用的各个阶段:
· HEUR:Exploit.MSOffice.Generic
· HEUR:Exploit.Script.CVE-2018-8174.a
· HEUR:Exploit.Script.Generic
· HEUR:Trojan.Win32.Generic
· PDM:Exploit.Win32.Generic