7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
漏洞描述
Adobe Reader 和 Acrobat 9.4 之前版本的 CoolType.dll 中存在基于栈的缓冲区溢出漏洞。远程攻击者可借助带有 TTF 字体的 Smart INdependent Glyphlets (SING) 表格中超长字段的 PDF 文件执行任意代码或者导致拒绝服务。
漏洞复现
操作系统
Windows XP SP3
体系结构:32 位
调试器
OllyDbg
版本号:吾爱专版
反汇编器
IDA Pro
版本号:6.8
漏洞软件
Adobe Reader
版本号:9.3.4
我们利用 Metasploit 来生成攻击样本:
msf > search cve-2010-2883
Name Disclosure Date Rank Description
---- --------------- ---- -----------
exploit/windows/fileformat/adobe_cooltype_sing 2010-09-07 great Adobe CoolType SING Table "uniqueName" Stack Buffer Overflow
msf > use exploit/windows/fileformat/adobe_cooltype_sing
msf exploit(windows/fileformat/adobe_cooltype_sing) > show info
msf exploit(windows/fileformat/adobe_cooltype_sing) > set payload windows/exec
payload => windows/exec
msf exploit(windows/fileformat/adobe_cooltype_sing) > set cmd calc.exe
cmd => calc.exe
msf exploit(windows/fileformat/adobe_cooltype_sing) > set filename cve20102883.pdf
filename => cve20102883.pdf
msf exploit(windows/fileformat/adobe_cooltype_sing) > exploit
[*] Creating 'cve20102883.pdf' file...
[+] cve20102883.pdf stored at /home/firmy/.msf4/local/cve20102883.pdf使用漏洞版本的 Adobe Reader 打开样本,即可弹出计算器。
漏洞分析
PDF 文件格式
首先当然得知道 PDF 格式是怎样的。
由 4 个部分组成:
header:文件的第一行,指明了 PDF 文件的版本号,通常格式是
%PDF-1.x。body:文件的主体部分,通常由对象文件组成,包括文本、图片和其他的多媒体文件等。
xref table:包含了对文件中所有对象的引用,通过它可以知道文件中有多少对象、对象的偏移以及字节长度。
trailer:包含指向交叉引用表以及关键对象的指针,并以
%%EOF标记文件结束。
当我们对一个 PDF 文件执行 Save(保存)操作时,新添加的信息将会附加到原文件的末尾,即所谓的增量保存。这些信息主要由 3 部分(body changes, xref, trailer)组成,此时的 PDF 文件如下所示:
这样子虽然方便,但体积会越来越大。此时我们可以执行 Save as(另存为)操作,将所有的更新信息合并成一个完整的新的 PDF,格式回到一开始的结构,体积也相应的有所减小。
例如可以利用工具 PDFStreamDumper 解析我们的样本,其 xref 和 trailer 如下所示:
该节区的对象的起始编号为 0,包含的对象个数为 15 个,每个对象在交叉引用表中占据一行。我们看到每行分为三列,分别表示对象在 PDF 中的文件偏移、对象的生成号和是否使用标志(f 表示 free,n 表示 used)。第一行对应的对象 ID 为 0,生成号总是 65535,而最后一行的生成号总是 0。
TTF 文件格式
根据漏洞通告,我们知道是 TTF 字体的 SING 表引起的溢出。所以再来看一下 TTF 文件格式。
TTF 包含有一个表 TableDirectory,其中有一个 TableEntry 结构项,包含了资源标记、校验和、偏移量和每个表的大小:
另外,SING 表的结构如下:
还是利用 PDFStreamDumper,从样本里将 TTF 取出来,需要注意的是 TTF 采用大端序。
加粗部分即 SING 表目录项,其 offset 域为 0x0000011c。
于是找到 SING 表,其中加粗部分为 uniqueName 域:
栈溢出
我们已经知道栈溢出发生在 SING 表的处理中,于是在 IDA 中打开 CoolType.dll,搜索字符串 "SING":
对每个数据引用进行检查,发现 sub_803DCF9+7B↑o 的下方存在危险函数 strcat:
在调用 strcat 函数时,未对 uniqueName 的字符串长度进行检查,直接将其复制到固定大小的栈空间,造成溢出。strcat 函数原型如下:
下面打开 OllyDbg 调试一下,先来看看函数 sub_8021B06 做了什么,在 0803DD7D 设置断点,然后在 Reader 中打开样本,程序就断了下来:
此时的 this 指针指向 TTF 对象:
然后 F8 单步步过,eax 里是函数的返回值 0012E4B4,其值等于 this 指针的地址。
下一句给 eax 赋值为一个指向 SING 表的指针,即 this 指针的内容。
所以这个函数的作用已经清楚了,通过传入的 tag 字符串,在 this 指针指向的 TTF 对象里找到对应的表目录项,使用表地址重置 this 指针。
接下来就是 strcat 函数了。
根据上面的 SING 表可以看到,uniqueName 原本只应该有最多 0x1c 个字节,但 strcat 根据 "\x00" 来作为字符串的结束,将导致复制 0x22d 个字节到栈上,造成溢出。
ROP
我们对复制到栈上的这段数据(0012E4D8~0012E714)设置内存访问断点。并开启 run trace 进行函数跟踪。
继续运行,然后我们记录下函数调用:
CoolType.08016BDE:
CoolType.0801BB21:
最终来到 CoolType.0808B116 里的关键点:
通过最后的 call 指令,程序跳转到了 ROP 链。回忆一下 uniqueName 域从 0012E4D8 开始:
于是跳转到 4A82A714:
在进入下面的内容前,我们再来看一个东西,即 eax 是由 edi 控制的,通过对函数调用的回溯,可以看到程序对 edi 的处理,它的值在整个过程中都是不变的,而且 edi+0x3C 正好存放第一个 gadget 的地址。所以只要这个地址被覆盖,就可以控制 EIP 了。
Heap spray
上面的 gadget 返回后,堆栈就被转移到 heap spray 的地方了。
Heap spray 是在 shellcode 的前面加上大量的 slide code(滑板指令),组成一个注入代码段。然后向系统申请大量内存,并且反复用注入代码段来填充。这样就使得进程的地址空间被大量的注入代码所占据。然后结合其他的漏洞攻击技术控制程序流,使得程序执行到堆上,最终将导致 shellcode 的执行。
我们来实际看一下(加粗的地方是后面会用到的 gadgets 地址):
通过 PDFStreamDumper 可以看到内嵌的 JavaScript,将变量还原后代码如下:
接下来程序将依次执行下面的 gadgets:
调用函数 kernel32.CreateFileW 创建文件,各参数如下所示:
然后通过同样的方法调用 CreateFileMapping:
调用函数 kernel32.CreateFileMappingW 创建内存映射,各参数如下所示:
接下来是调用 MapViewOfFile 的过程:
调用函数 kernel32.MapViewOfFileEx 将文件映射到内存映射地址空间,各参数如下所示:
最后调用函数 memcpy 将真正的 shellcode 复制到 MapViewOfFile 返回的地址处。这是一段可读可写可执行的内存,从而绕过 DEP。另外由于所使用的 gadgets 都来自 icucnv36.dll 模块,该模块不受 ASLR 的影响,所以同时也相当于绕过了 ASLR。
调用函数 memcpy,各参数如下所示:
然后这段复制过去的 shellcode 会被解密,并跳到 03E900A3 执行:
最后弹出计算器:
补丁
利用 BinDiff 插件进行二进制比对,可以看到在修复漏洞时使用函数 sub_813391E 替换了 strcat:

跟进函数 sub_813391E:
使用更安全的 strncat 替代 strcat,限制字符串长度为 0x104 字节,并且根据字符串长度动态地调整栈空间。
参考资料
《漏洞战争》
https://www.cvedetails.com/cve/CVE-2010-2883/
Last updated
Was this helpful?