深入解析CVE-2018-5002漏洞利用技术

发表于:2019-2-02 11:35

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:jq0904    来源:360核心安全技术博客

  前言
  2018年6月1号,360高级威胁应对团队捕获到一个在野flash 0day。上周,国外分析团队Unit 42公布了关于该次行动的进一步细节。随后,卡巴斯基在twitter指出此次攻击背后的APT团伙是FruityArmor APT。
  在本文中,我们将披露该漏洞利用的进一步细节。
  漏洞利用
  原始样本需要与云端交互触发,存在诸多不便,所以我们花了一些时间完整逆向了整套利用代码,以下分析中出现的代码片段为均为逆向后的代码。原始利用支持xp/win7/win8/win8.1/win10 x86/x64全平台。以下分析环境为windows 7 sp1 x86 + Flash 29.0.0.171。64位下的利用过程会在最后一小节简要提及。
  1. 通过栈越界读写实现类型混淆
  原样本中首先定义两个很相似的类class_5和class_7,并且class_7的第一个成员变量是一个class_5对象指针,如下:
 
  紧接着调用replace方法尝试触发漏洞,可以看到在replace函数内定义了一个class_5对象和一个class_7对象,并将这两个对象作为参数交替传入trigger_vul函数()。
  
  从下图可以看到,trigger_vul方法一共有256个参数,分别为交替出现的128个class_5对象和128个class_7对象。这是为了后面的类型混淆做准备。
  在trigger_vul内部,首先创建一个class_6对象用于触发漏洞,
  
  在class_6类内调用li(123456)触发RangeError,通过修改ByteCode后可以导致进入如下的catch逻辑(伪代码),可以看到在catch内越界交换了两个栈上的变量(local_448和local_449)。而攻击者通过精确布控jit栈,导致交换的两个栈变量恰好为先前压入的一个cls5对象指针和一个cls7对象指针。从而实现了类型混淆。
  成功交换指针后,将修改完后的栈上数据(256个参数)分别回赋给一个cls5_vec对象和一个cls7_vec对象,最后返回cls5_vec对象,这时cls5_vec里面存在一个cls7对象,其余均为为cls5。
  
  在windbg中看到上述过程如下: 
  根据着色分布可以看到栈上的一个cls5对象指针和一个cls7指针在漏洞触发后发生了互换:
  返回到trigger_vul之后,遍历cls5_vec中的成员,找出m_p1不为0x11111111的cls_5对象,此对象即为被混淆的cls_7。随后保存有问题的“cls_5”对象和cls_7对象到静态成员。
  trigger_vul返回之后,通过_cls5.m_p6成员是否为0来确定当前环境为x86还是x64,并借助两个混淆的对象(cls5和cls7)去初始化一个class_8对象,该对象用于实现任意地址读写。
  
  2. 任意地址读写
  class_8类是攻击者构造的一个工具类,用来实现任意地址读写,并在此基础上实现了x86/x64下的一系列读写功能函数。我们重点来看一下readDWORD32和writeDWORD32的实现。
  2.1 readDWORD32
  由于cls7的第一个成员(var_114)是一个cls5对象,所以在cls5被混淆成cls7后,表面上对cls5.m_p1的修改实质是对cls7.var_114的修改。现在假设我们有一个需要读取的32位地址addr,只需要把addr-0x10的值赋值给cls5.m_p1,这样相当于把cls7.var_114设为了addr-0x10。然后去读取cls7.var_114.m_p1, 此语句会将cls7.var_114.m_p1处的值当做一个class_5对象,并读取它的第一个成员变量,也即将addr-0x10当作一个class_5对象,并读取addr-0x10+0x10处的四个字节。 
  下图解释了为什么32位下需要addr-0x10,由于继承关系,每一个as3对象的前16个字节结构是固定的(其中,“pvtbl”是C++虚表指针,“composite”、“ vtable”和“delegate”成员可以参考avmplus源码中的ScriptObject实现),一个类对象的第一个成员变量位于对象首地址+0x10处(64位下类推为addr-0x20):
  图:从内存来看,混淆后,对cls5的操作实际上影响了cls7对应内存处的值,随后可以通过访问cls7.var_114.m_p1去读取任意addr处的值。
  2.2 writeDWORD32
  writeDWORD32原理和readDWORD32类似,此处不再赘述。
  
  在clsss_8类中,攻击者在上述两个函数的基础上实现了一系列功能函数,全部如下:
  3. 定位ByteArray相关成员偏移
  虽然攻击者并未借助ByteArray来实现任意地址读写,但为方便利用编写,他必须知道当前Flash版本中ByteArray相关成员的内存偏移。为此,攻击者定义了一个class_15类,用来借助任意地址写实现对特定成员的偏移搜索并,保存。以供后面使用。
 
  setOffset32的部分逻辑:
 
  以下class_15的成员用来保存动态搜索到的内存偏移。
  4. 1st shellcode
  找到相关偏移后,攻击者立即开始构造shellcode并执行。 1阶段的shellcode为内置,但有7个DWORD32字段需要动态填充。而2阶段的shellcode通过一个ByteArray动态传入,即上面setOffset函数中的_bArr成员。由于并未得到攻击者的2阶段shellcode,我们使用的2阶段shellcode来自HackingTeam泄漏的代码,功能为弹一个计算器。
  攻击者先借助ByteArray(ba)存储了一个1阶段shellcode模板,反汇编后如下,其中紫色区域是需要动态填充的字段,这些字段代表的含义如注释所示:
  
  5. Bypass ROP
  为了构造ROP,攻击者专门定义了一个辅助类class_25,在里面实现了如下功能函数:
  攻击者先借助flash模块的IAT找到User32.dll的GetDC地址,再借助User32.dll的IAT找到ntdll.dll的RtlUnWind地址,
  
  随后从ntdll.dll的EAT的AddressOfFunctions数组中找到NtProtectVirtualMemory和NtPrivilegedServiceAuditAlarm的函数偏移并计算得到对应的函数地址。
  攻击者这里的思路是取出NtProtectVirtualMemory的SSDT索引,和NtPrivilegedServiceAuditAlarm+0x5的地址,供后面使用。
  后面会通过call NtPrivilegedServiceAuditAlarm+0x5并传入NtProtectVirtualMemory的SSDT索引的方式来Bypass ROP的检测。由于ROP检测并未Hook NtPrivilegedServiceAuditAlarm作为关键函数,所以并不会进入ROP检测逻辑中,因此绕过了ROP的所有检测。
  随后搜索以下的ROP部件并保存,供后面使用

  随后将上述信息返回给上层调用者:
  
  随后部分值被填充到1st shellcode的前5个pattern。 
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号