• 本文作者: 漏洞应急响应中心
  • |
  • 2015年7月20日
  • |
  • 漏洞分析
  • |

初入flash漏洞的分析(一个Ring3下的字体解析漏洞)

截止到目前为止由hackting team泄密事件所衍生出来的已被修补或者即将被修补的0day已经达到6个。3个通过ValueOf函数诱发的flash漏洞,2个ring0的可导致提权的字体解析漏洞(adobe的atmfd.dll内核字体解析组件),还有一个从邮件数据泄漏出来的IE11的UAF的漏洞。

Flash的漏洞调试分析常见的基本有两类。一类是ActionScript Level级别的漏洞。比方说hackting team这次泄漏出来的3个flash漏洞。这类flash漏洞调试分析起来不掌握一定技巧很容易迷失JITed Code当中。再一类就是flash控件在解析flash文件中的某些元素的时候,解析代码处理不当会,导致漏洞的产生,可以归咎于文件格式漏洞的范畴。初入flash漏洞的分析,本篇文章记录的是后一类的flash漏洞。

 

黑盒分析漏洞过程

这是一个flash player在ring3下面读取附带了畸形字体的swf文件造成整数溢出的漏洞。该漏洞已经有CVE编号,CVE-2012-1535,需要安装附件里提供的对应的flash版本。Windbg attach浏览器进程,直接运行1.html就可以触发崩溃。

1

崩溃是发生在Flash32_11_2_202_233.ocx模块当中此时栈帧已经被破坏了。

3

崩溃前只做了3次push,所以我们从栈上找出返回地址是103EF640。崩溃的前一层函数是sub_103EF4A0。然后IDA rebase基地址在该函数相应位置做标记。Windbg重新attach IE进程。使用sxe ld:Flash32_11_2_202_233.ocx在flash加载的时候中断。然后在崩溃的前一层函数下断点。F5让代码执行到该处。然后使用VMWare做快照固定加载基地址。在后面的分析当中我的flash的ocx控件加载的基地址全都用快照固定在0×10000000。

最后出问题的一句代码是

4

追踪一下eax的来源下面列出相关联的代码

5

需要继续确定63A处 push  esi的 esi的来源。推测是来源于627处的指令

6

使用下面的命令来打印确认

bp 103EF627 ” .echo 103EF627 eax is ;r eax; gc;”

bp 103EF63A ” .echo 103EF63A esi is; r esi; gc;”

7

可以看到 esi的确是由627处的指令赋值过来的。并且固定是020befb0

8

需要对0x020befb0地址下写断点观察是谁把这里的数据污染了。

9

一共有3次中断在第二次中断的时候1e0d0008中的数据已经被污染了。

10

向1e0d0008写入数据的语句是这一条

11

可以看到污染源来自eax。而被污染的地址是esi+8.。

使用下面的命令观察脏数据和污染地址的变化。

12

可以看到被写入的内存是esi+8是以020ba060为初值以10h递增到020befc0。而eax初值是10h而后一直是固定值1e0cfff8。也就是说在这片内存区域全都是用了1e0cfff8去做了相关位置的填充。而最终call崩溃的地方是call  poi(poi(020befb0)+8)也就是call  ((1e0cfff8+8)+8)  (call  1e0d0008)。正好对应上了。

13

 

15

注意上面下ba写入断点的时候在下面的循环语句中

103ef5f6 83460808        add     dword ptr [esi+8],8

这条指令会将poi(020befb0)再做一次加8的操作。再看一下esi的源头。

17

esi的值是在527处的call 10034148后分配的一个内存地址。初始值是0x020ba058

16

call同样的函数在4FD处也存在同样的call 10034148的代码。返回到的值是0x020befb0

18

目前看到了这样的场景,通过两次call 10034148的操作。分别返回了两个内存地址0x020ba058和0x020befb0并且自103EF55A开始的循环函数中不断的从0x020ba060(0x020ba058+8)开始向020befc0写入1e0cfff8。而在最后call  poi(poi(020befb0)+8)  的过程中由于020befb0地址的内容被覆盖成了1e0cfff8然后 add     dword ptr [esi+8],8这条指令会将020befb0的内存地址再做一次加8的操作导致最后变成了call 1e0d0008。而1e0d0008内存地址中的内容是无效的最后触发异常。

目前依然有两个问题。

1 需要确认为何addr2上的数据被连续向addr1中写入的数据给覆盖了。

正常在堆上申请两片内存并返回两个地址之后比方说返回的两个地址(0x020ba058)addr1<(0x020befb0)addr2。我们在向addr1中写入数据的时候正常情况addr1中的数据是不会将addr2中的数据覆盖的。但是如果申请到的内存过小或者说写入的数据长度外界可控,可随意调整大小都会导致addr2中的数据被覆盖,造成最后本该call addr2里面的一个地址却call成了被覆盖的数据。

2 定位写入的数据的来源。

控制写入的数据从而可以控制eip。最终会调用call poi(poi(020befb0)+8)而020befb0中的数据会由这条命令污染需要定位eax的来源。

103EF5C7  mov [esi+8],eax

先从第一个问题开始

19

在分配第二个地址020ba058的call 10034148的时候,Eax 在逻辑左移的时候直接溢出变0,并且当作一个参数压入栈中。跟踪一下eax的数据来源。

20

可以看到在call 103efe8b的时候直接返回0×10000000。导致了下面shl eax,4逻辑左移的时候直接溢出。0×10000000是一个脏数据,被用来恶意触发溢出。追踪一下0×10000000脏数据的来源。跟进这里的call 103efe8b;103efe8b函数里最后的返回值eax与103EFECD处的指令有关。

21

此处断下查看eax的值。

22

正好是10000000的little endian的表示形式。103efe8b的这个函数在sub_103EF4A0一共有三次调用

23 24 25

可以看到只有最先入栈的参数是变动的为0,4,8。其他的参数都是固定的。由于0×10000000是一个脏数据,被用来恶意触发整数溢出,一定是从swf文件传递进来形成的。Swf中的恶意数据会被统一读入内存address1。然后103efe8b处的函数负责从address1处的偏移0,4,8分别获取相关数据。从swf文件中搜索0001 0000 1000 0000 1e0c ffe8应该是能够连续搜索到的。

26

果然可以搜索到连续的数据。至此确定了被用来恶意触发整数溢出的脏数据10000000的来源。

还需要确定第二个问题103EF5C7  mov [esi+8],eax追溯eax的来源。

27

依然使用windbg打印寄存器的命令。可以看到eax始终是1e0cffe8,并且位于swf可控,并且紧挨0001 0000 1000 0000。我们尝试将swf文件中的1e0cffe8修改为0c0c0bec然后再进行调试确定最后是不是会call到0c0c0c0c上面。果然最后就是call到了0c0c0c0c这个地址上面。

0c0c

此时配合堆喷射技术将恶意代码喷射到这块地址上面就可以了。此时已经能够控制传入恶意参数1000 0000。触发整数溢出漏洞分配极小内存返回addr1。也可以控制addr1覆盖addr2写入的内容,让他call到0c0c0c0c的地址上。但是还有一个细节就是这个循环顺序填充addr1填充的次数一定要足够大确保能够覆盖到addr2的位置。之前分析过写入的内存是esi+8是以020ba060为初值以10h递增到020befc0。所以还需要追踪一下这个循环的计数器的大小是如何确保向addr1中写入的次数大到能够覆盖addr2.

29

在箭头处下断,然后打印寄存器追踪。

bp 103EF629  “.echo 103EF629 ecx  is;r ecx ; .echo eax;”

30

继续追踪一下020befb8处是怎么被填0的ba w1 020befb8 “; dd 020befb8 L1;”windbg中断

103ef5b8 8906            mov     dword ptr [esi],eax

可以看到是在循环填充addr1内存当中充当计数器的数值也被改为0从而终止了循环可以看到计数器的停止并没有在swf文件内容中进行特意控制,只是在循环填充addr1内存的时候将计数器覆盖掉就可以了。至此只是单纯借助于调试器的反复耐心调试在汇编层面理解了整个漏洞的形成和触发经过。

 

漏洞的背景与开发层面的审视

 

我们从开发的层面看一下这个漏洞是在哪里出现的。Flash官方提供了一个工具swf investigator 需要安装adobe air才可以使用。可以从这里下载http://labs.adobe.com/technologies/swfinvestigator/。我们可以定位到swf文件的0x856D(34157)处。也就是0001 0000 1000 0000的位置。然后查看swf Investigator的tag标签处。

31

 

发现传入的10000000这个恶意触发整数溢出的脏数据是位于DefineFont4标签。这个标签主要是为了在Flash当中加强对OpenType字体的支持。Flash对这个标签的支持可以在adobe的官方博客找到。

http://blogs.adobe.com/tlf/2008/11/embedded-font-subsetting-using.html

是在08年11月之前加入了DefineFont4这个标签用于加强对于OpenType字体的支持。

 

字体简史和字体结构

TrueType的简短历史见于微软的这篇记录https://www.microsoft.com/typography/TrueTypeHistory.mspx。

最早期的时候adobe推出了PostScript打印机控制语言配套有自己的两种字体Type1 Font和Type3 Font。Type3是免费版本,Type1是收费版本。Adobe为他们的Type1版本征收高昂的技术授权费用。Apple也是adobe的客户之一。终于Apple忍受不了高昂授权费用,着手立项开发自己的字体系统,也就是现在的TrueType字体。这个项目一开始叫”Bass”,后来改名字为”Royal”。后来苹果同微软做了一次技术交换用自己的royal交换了微软开发的类似于PostScript的打印技术TrueImage。TrueImage当时bug频出苹果一直弃之角落没有用过。后来微软在Royal(TrueType)的基础上拓展出了TrueType Open的字体。之后又联合adobe推出了现在的OpenType兼容TrueType/TrueType Open和PostScript的存储信息。

先介绍下TrueType的字体文件的结构。

TrueType字体文件由一系列的连在一起的表组成。一般每个表是按照long型并用0补齐。第一个表是OffsetSubtable。C格式定义如下。

32

注意当为OTTO的时候TrueType中必须存在的glyf表会被CFF表代替。

offset subtable后面紧跟着多个font table index用于索引后面的FontTable。

33

再向下直接就是FontTable。

FontTable里面包含着字体数据。他们的顺序是任意的。下面的这些方框里面的FontTable对于苹果标准的TrueType字体是必须的。

34

还有一些FontTable是可选的如’cvt ‘    ‘fpgm’ ‘hdmx’ ‘kern’   ‘OS/2′  ‘prep’

着重看一下苹果在TrueType当中给出的kern Table的定义。

35

注意苹果的kern Table中的version 和 nTable全都是32位长度。

36

苹果专门对于自己的kerning table的 标准给出了一个重要注意。苹果新的kerning table的标准跟微软的不一样,请开发者注意区分

更多TrueType的字体的信息可以参考苹果的网站。

https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#Intro

再来看一下OpenType字体文件。

基本上OpenType是源自于微软的TrueType Open字体,而 TrueType Open 字体是源自于 TrueType 字体,所以这些字体可以说是系出同门,基本的 container 格式是相同的。OpenType font引进 CFF table 来存放 postscript outline数据,这相当于 TrueType 的 glyf table,这些都是主要的字体实体数据。OpenType font还引入了一些高级的表比方说Advanced Typographic Tables总共包含6个表GSUB(Glyph Substition), GPOS(Glyph Position), BASE(baseline), GDEF(Glyph Definition),JSTF(Justification data),MATH(Math layout data);所以你可以在adobe添加了DefineFont4标签增强了对OpenType的支持的博客中看到这样的话。

37

在 OpenType font 会有两种不同字体实体数据的字体出现:

TrueType format with TrueType outline(*.ttf, *.ttc)

TrueType format with PS outline(CFF)(*.otf) 或称为 CFF OpenType font。

OpenType字体文件依然以OffsetSubtable开始。当字体文件只包含一种字体的时候文件开始的第一个字节就是OffsetSubtable。如果是其他情况,OffsetSubtable的起始位置由TTC Header给出。OpenType的OffsetSubtable与TrueType的OffsetSubtable的定义是完全一样的只是在SFNT_ver字段微软特别注明OpenType不支持 0×4727565(true)  0×74797031(typ1)这两个取值。下面的FontTableIndex定义也都是一样的。

OpenType字体所必须的字体表有这些

38

我们着重来看一下微软OpenType对于kern Table的定义。

39

注意微软OpenType标准里面对于kern Table的version和nTables字段只有两个字节。

微软特意针对kern Table给出了一段声明

NOTE: Apple has extended the definition of the ‘kern’ table to provide additional functionality. The Apple extensions are not supported on Windows. Fonts intended for cross-platform use or for the Windows platform in general should conform to the ‘kern’ table format specified here。

意思就是说windows平台不支持apple对于kern table的扩展定义,要进行跨平台的字体使用在windows平台下面请一定要遵循微软这里的对于’kern’table的定义。

更多的关于OpenType字体的定义参考微软的网站。

http://www.microsoft.com/typography/otspec/otff.htm

前面已经分析过传入的恶意参数是在DefineFont4标签当中。40

比较明显的可以看到字体的开头是以OTTO开头的字体文件,我们使用winhex将字体文件提取出来保存成Font.otf。然后使用010editor的TTFTemplate.bt进行解析或者使用T2FAnalyzer进行字体的观察.。可以定位到恶意输入的1000 0000这个数据是位于kerning table的数据当中。Kerning table是用来做字距调整的一个表。

010editor的模版可以在这里下载。

http://www.219.me/posts/2855.html

41

分别对照苹果kern table和微软kern table的标准可以看到在Flash中DefineFont4标签中加入的对OpenType增强的支持是按照苹果的kern Table标准进行定义的。

42

00 01 00 00 这个代表版本号。

10 00 00 00代表subtables 在kerning table中的数量。

我们再来对照汇编代码看下这个漏洞究竟是怎么产生的。

43

首先call  103efe8b从内存中取出10000h,也就是kerning table的版本号。

44

判断如果版本号是苹果扩展标准规定的4字节的00 01 00 00那么就继续从内存中读取4字节的nTables。这里是被恶意嵌入的字体文件修改成了1000 0000。

接下来就是根据解析出来的nTables的数量x10h来分配相应大小的内存存储subTable.

45

这里就是问题的所在了,由于传入的了过大的恶意的nTables直接造成溢出。

并且由于之后的call地址可控。

46

配合堆喷射技术构造rop chain可利用该整数溢出漏洞。

 

 

附件链接:http://pan.baidu.com/s/1mgtKkWw
包含老版本flash和相关idb,触发漏洞使用的html和swf文件。

 

 

 

 

 

 

Written by 漏洞应急响应中心