- 漏洞名称:Adobe Reader TTF字体SING表栈溢出漏洞
- 漏洞编号:CVE-2010-2883
- 漏洞类型:栈溢出
- 漏洞影响:信息泄露
- CVSS评分:9.3(High)
- 利用难度:Medium
- 基础权限:不需要
Adobe Reader是美国Adobe公司开发的非常流行的PDF文件阅读器。
pdf中带有TTF字体Smart INdependent Glyphlets (SING)表的UniqueName成员在CoolType.dll中执行一次strcat函数。
Adobe Reader 9.3.4之前的版本的CoolType.dll中存在基于栈的缓冲区溢出漏洞函数strcat,在执行时会将pdf文件的SING表的UniqueName字符串拷贝进栈中,并且不检查字符串长度。
Windows、Macintosh和UNIX系统下Adobe Reader 9.3.4 之前的版本
综合渗透测试-CVE-2010-2883-PDF漏洞分析
第一步,打开网络拓扑,启动实验虚拟机,分别查看虚拟机IP地址:
Kali Linux
Windows XP
第二步,通过msf产生样本,这里我们可以通过Kali渗透测试机器中的Metasploit帮助我们生成一个样本用于动态的调试。步骤如下:
首先,我们在Kali中使用命令msfconsole调出渗透测试平台Metasploit。使用search命令搜索cve-2010-2883漏洞编号可以列出所有可用的exploit,
使用命令locate来定位这个exploit的位置:
为方便接下来动态调试识别一些关键数据块,我们先修改一下该exploit的102行的位置,将下面这段代码修改为sing <<“A”*(0x254 - sing.length),修改之前记得先做好备份。
修改前:
修改后:
这里的rand_text的主要作用是取随机字符,目的是为了增加样本的随机性从而降低被杀毒软件识别的可能性,修改完成后使用命令wq进行保存。
第三步,使用命令use exploit/windows/fileformat/adobe_cooltype_sing,调用这个exp,并使用命令set payload windows/exec来设置有效载荷为执行命令。为方便查看漏洞执行效果,然后使用命令set cmd calc.exe将有效载荷的命令设置为启动计算器,最后设置一下生产的样本文件名cve20102883.pdf
执行成功后,样本就生成在了/root/.msf4/local/cve20102883.pdf,接下来我们从Kali中拷贝出来放到我们的Windows XP SP3靶机环境中来进行复现。(使用Kali渗透机搭建HTTP服务,注意提前删除/var/www/index.html,然后XP访问并下载cve20102883.pdf)
第四步,首先双击桌面上的Adobe Reader 9来启动程序Adobe Reader 9.3.4
接下来,打开桌面上的动态分析软件OllyDbg,并将Adobe Reader加载进OllyDbg,操作如下:选择文件(F)——附加(A)或选择打开找到文件AcroRd32.exe
接下来选择我们要附加的进程AcroRd32.exe
此时OllyDbg所显示的当前调试程序是运行状态下的,并且此时Adobe Reader已经加载了CoolType.dll文件,接下来我们通过OD来动态分析CoolType.dll文件,使用快捷键Alt + E来查看可执行模块
通过预备知识中的静态分析我们已经知道了aSing在地址0803DD74处被调用了,接下来对它进行分析使用快捷键Ctrl + G输入0x0803DD74回车,并使用F2快捷键在该地址处下断点。然后我们将实验的样本cve20102883.pdf拖入到Adobe Reader中,程序将会运行在刚才下的断点上面。接下来F7单步步入到下图中0803DD7A的位置
第五步,在执行完以上指令之后,我们接下来观察内存中ecx存下了什么内容。此时ecx = 0012E4B4,我们首先猜测这是一个指针地址,使用快捷键Ctrl + G定位到数据区域0012E4B4之后,取出32位的十六进制内容如下。(此处窗口值每次加载的地址会有所变化)
0012E4B4 04 6A B9 AC,这里应该就是ecx指针所指向的地址,右键选中该地址点击数据窗口中跟随,可以看到以下的数据
第六步,在对这段数据进行分析之前先来看一下TrueType字体格式文档是如何定义的
在TrueType字体文件中,从0字节偏移的位置开始有一个表目录,且这个表目录的第一个字段是sfnt version是用于表明所使用ttf格式版本的字段,我们通过能够清楚的了解,对于1.0版本的TTF字体文件开头要用0x00010000来表示版本。会发现开头正好是0x00010000,这就证明了ecx保存的是一个指向ttf对象的指针地址,并且在这里应该作为this指针。分析到这里我们继续动态调试。
第七步,回到我们刚才分析的0x0803DD74位置处的数据,接下来遇到一个call指令,意味着即将要调用一个函数。在调用函数前我们先来看看这个函数传入了哪些参数
很明显这里将SING字符串当作参数了。接下来我们单步F8不进入call循环的内部
此时我们在观察eax变成了什么,eax = 0x046B2B80
接下来我们选中eax的数据,然后右键选择数据窗口中跟随数值
数据窗口数值如下
我们发现这里大量的A原本都是随机字符,由于刚才我们修改了exploit代码的缘故,使得这一块的数据更容易辨识,实际上这些数据都是样本中SING表里构造好的恶意数据
因此我们总结一下,以上的指令主要就是将SING表的tag名称传入到08021B06函数中并通过表目录来获取到SING表的入口地址,而当前eax的值0x046B2B80即是SING表的入口地址。分析SING表的这些数据,我们就能知道样本在里做了什么操作。
第八步,继续往下调试将会找到关键的溢出点
此处我们发现程序经过“push eax”将Unicode字符串域入栈,即将刚获取到的SING表入口地址入栈,然后再次通过“push eax”来拷贝要连接字符串的目地地址(当前栈区的ebp地址),接下来我们使用F8调试单步过strcat之后,查看一下ebp开始的栈区数据0012E4D8(使用快捷键Ctrl + g直接跳转进去)
结束的位置为0012E710
此时我们可以发现栈溢出已经产生,栈区数据已经被修改为了SING表中所构造的恶意数据(实际是从uniqueName字段开始的数据)。
第九步,继续往下分析,我们希望了解程序到底是如何去读取栈区数据的。我们在0808B308处使用F2设置断点,然后直接使用F9直接运行,发现在执行到0808B308的时候,有一个值得注意的地方。即调用了[eax]地址指向的函数。此时的eax= 0012E6D0,这正好处在我们刚覆盖的栈区的数据范围内
且[eax] = 0x4A80CB38,使用Ctrl + G跳转进去
发现调整了ebp。原来的ebp = 0x0012DD48 ebp+0x794 = 0x0012E4DC
重新将ebp调整进了覆盖的栈区数据范围内。接下来执行leave:mov esp,ebp pop ebp,修改了esp,原本的esp = 0x0012DD24; esp =ebp=0x0012E4DC; [esp] = 0x41414141并且弹栈之后ebp = 0x41414141,最后retn时,esp = 0x0012E4E0 ; [esp] = 0x4A82A714
因此接下来EIP = 0x4A82A714。
这里原本的esp = 0x0012E4E4 [ebp] = 0x0C0C0C
pop esp之后esp = 0x0C0C0C
这里又到了一个关键的地方,跳转地址的稳定性其实主要依靠0x4A82A714和0x4A80CB38这两处的地址,他们都位于icucnv36.dll的地址空间,而在Adobe Reader的各版本上,这个dll上的这两处地址始终都是不变的,因此保持了个版本的兼容性和Exploit的稳定性。看到0x0C0C0C0C 我们很自然的会想到 HeapSpary技术,这是样本特意构造的。
第十步,在这个样本中确实利用到了堆喷射的技术,借助PDF本身支持执行JS的特性,将shellcode借助JS写入内存中。实际上这里也可以不借助堆喷射来实现任意代码执行,但是这样的话就会增大ROP链的构造难度,因此选择利用堆喷射的方法来写入shellcode是一种非常巧妙的做法。仔细观察可以发现接下来的ROP链调用的都是icucnv36.dll这个库中的地址,原因在于这个库是没有开启ASLR保护的。还有需要说明的一点是,之所以要借助堆喷射技术来执行代码的原因是为了绕过windows环境下的DEP保护。继续动态分析,此时即将执行到retn,而esp此时指向的地址是0x0C0C0C0C,
因此接下来执行的是4A8063A5,直接Ctrl + G 转入至此处的地址
设置断点F9执行至此处,执行的是
ecx =0x4A8A0000 [ecx] = “UTF-32”,这里原本存“UTF-32”字符串的地方保存eax = 0012E6D0的值
retn返回到4A801F90,直接定位过去
这里eax指向了CreateFileA函数用于创建文件。即eax = 0x4A84903C
这里直接跳转到eax保存的指针所指向的地址(0x7C801A28)处
第十一步,使用Ctrl + G定位到地址0x7C801A28处,并使用F2设置断点
这里应该是CreateFileA函数的实现逻辑,我们直接查看上各参数值情况
以上内容是CreateFileA的参数,来看看CreateFileA官方文档给出的结构
参照上面的结构我们对参数做一下简单解释
当程序执行到此处时,会创建了一个临时文件,文件名是iso88591。可以在当前样本pdf同目录下找到。
第十二步,继续分析下去,发现程序到7C801A54的位置,返回至4A801064,定位到改地址
这里跳转到0x4A8063A5(icuncnv36.4A8063A5)
ecx = 0x4A801064,继续单步跟进,发现程序返回至地址4A842DB2
这里eax = 0x000002B0 edi = 0x0012E718,通过这里的xchg指令交换了两个寄存器的值eax = 0x0012E718 edi = 0x000002B0
到这里ebx = 0x00000008,继续单步
这里程序指向了一个函数的实现代码块,我们猜测该代码块的作用是做斜杠和字母小写的检查。接续单步分析
这里让eax指向了一个CreateFileMappingA函数
此处利用相同的手法跳转到[eax]所在的函数CreateFileMapping处,该函数7C8094EE-7C809545地址区域,用于创建一个文件映射内核对象。
调用CreateFileMapping时的栈中各参数的分布情况如下
第十三步,继续调试跳转到4A8063A5
继续跳转4A842DB3
这里的eax = 0x00000160 edi = 000002B0,通过xchg交换两个寄存器的值
这里再一次跳转到了4A80A8A6
eax指向的是MapViewOfFile函数入口地址 eax = 0x4A849030
同样的原理借助jmp dword ptr ds:[eax] 跳转到eax所指向的地址:7C80B995
将一个文件映射对象映射到当前应用程序的地址空间
跳转到 0x4A8063A5
ecx = 0x4A801064
这里的eax = 00000160 edi = 01010000,通过xchg交换两个寄存器的值
ebx = 0x00000030,这里retn再一次跳到了0x4A80A8A6,接下来单步分析下去直到0x4A80AEDC
这里ecx = 0x4A801064
call ecx跳转到0x4A801064
这里[esp] = 0x4A80AEE
继续跟进
继续跟下去
此时这里的ecx = 0x4A801064
这里将memcpy函数地址保存在了eax寄存器中
此处使用到的memcpy函数将要执行的shellcode写入到MapViewOfFile返回的地址,构造CreateFileA函数地址的ROP指令
第十四步,再次通过类似方法调用memcpy函数,如下所示
其中目的地址就是前面MapViewOfFile返回的地址,而源地址就是真正的Shellcode代码,将它复制到一段可执行可读写的内存段,以此绕过DEP保护。由于构造ROP的指令均位于不受ASLR保护的icucnv36.dll模块,因此也可用于绕过ASLR保护。memcpy的参数如上,循环解密shellcode过程
最后这里的循环将shellcode解密,可以借助快捷键Ctrl + F9来返回到执行结果中来跳过循环,尝试多次后,最终执行到calc.exe命令处
并跳转到0x037900A3处继续执行
这里的eax = 0x039B00D3 指向的是一个字符串 “calc.exe”
call ebp之后执行calc.exe命令。总结一下这部分由堆喷射覆盖在栈上的数据都做了一些什么。主要做了新建临时文件,将文件映射到内存,将真正的shellcode拷贝到内存的某一块区域并且解码这些shellcode然后执行。