Author:dge
我从分析这个漏洞的过程中重新认识到一些东西,写下来分享给像我一样的人们。
在分析文件格式漏洞时只用调试工具跟踪有时候很费劲。如果POC可以顺利执行shellcode,跟踪比较简单,但如果POC执行不到shellcode,就很难去解决,当然我们可以通过补丁比较确定出几个可疑函数进行重点跟踪,当然前提是你得能确定出来,如果补丁对代码的改动很大,要确定出可疑函数实非易事,即使能确定出几个来,对他们跟踪也是非常麻烦的事情,让复杂的问题变的简单,那肯定是最理想的。
认识PaiMei这个工具是在failwest的书里,虽然读完那本书已经很久了,但一直没用过这个工具,直到前几天分析这个漏洞,在分析未果的情况下,我想起了PaiMei,这才体会到了它的强大,这也是我想写这篇文章的原因。
MS08-021有两个漏洞,这是其中的一个,这个漏洞的POC在很久之前就有人公布了,这是POC的地址:http://www.milw0rm.com/exploits/5442,有了POC和PaiMei,分析这个漏洞就变得简单了,这个漏洞发生在gdi32.dll中,是典型的栈溢出。
用PaiMei和od我们可以轻松地确定出漏洞触发路径。
PlayEnhMetaFileRecord
|__MRCOLORMATCHTOTARGET::bPlay
|__IcmCreateColorSpaceByName
|__lstrcpyW
先简单了解下EMF文件格式
Header结构:
typedef struct tagENHMETAHEADER {
DWORD iType;
DWORD nSize;
RECTL rclBounds;
RECTL rclFrame;
DWORD dSignature;
DWORD nVersion;
DWORD nBytes;
DWORD nRecords;
WORD nHandles;
WORD sReserved;
DWORD nDescription;
DWORD offDescription;
DWORD nPalEntries;
SIZEL szlDevice;
SIZEL szlMillimeters;
#if (WINVER >= 0x0400)
DWORD cbPixelFormat;
DWORD offPixelFormat;
DWORD bOpenGL;
#endif /* WINVER >= 0x0400 */
#if (WINVER >= 0x0500)
SIZEL szlMicrometers;
#endif /* WINVER >= 0x0500 */
} ENHMETAHEADER, *PENHMETAHEADER;
RECORD结构
typedef struct tagENHMETARECORD
{
DWORD iType; // Record type EMR_XXX
DWORD nSize; // Record size in bytes
DWORD dParm[1]; // DWORD Array of parameters
} ENHMETARECORD;
函数原型:
BOOL PlayEnhMetaFileRecord(
HDC hdc, // handle to DC
LPHANDLETABLE lpHandletable, // metafile handle table
CONST ENHMETARECORD *lpEnhMetaRecord, // metafile record
UINT nHandles // count of handles
);
POC截图:
下边分析下这个漏洞:
.text:77EFEF66 __stdcall PlayEnhMetaFileRecord(x, x, x, x) proc near
.text:77EFEF66 ; CODE XREF: bInternalPlayEMF(x,x,x,x,x)+6A2 p
.text:77EFEF66 ; bInternalPlayEMF(x,x,x,x,x)+13B2 p ...
.text:77EFEF66
.text:77EFEF66 var_58 = dword ptr -58h
.text:77EFEF66 var_54 = dword ptr -54h
.text:77EFEF66 var_50 = dword ptr -50h
.text:77EFEF66 var_4C = dword ptr -4Ch
.text:77EFEF66 var_48 = dword ptr -48h
.text:77EFEF66 var_44 = dword ptr -44h
.text:77EFEF66 var_40 = dword ptr -40h
.text:77EFEF66 var_3C = dword ptr -3Ch
.text:77EFEF66 var_38 = dword ptr -38h
.text:77EFEF66 var_34 = dword ptr -34h
.text:77EFEF66 var_30 = dword ptr -30h
.text:77EFEF66 var_2C = dword ptr -2Ch
.text:77EFEF66 var_28 = dword ptr -28h
.text:77EFEF66 var_24 = dword ptr -24h
.text:77EFEF66 var_20 = dword ptr -20h
.text:77EFEF66 var_1C = dword ptr -1Ch
.text:77EFEF66 var_18 = dword ptr -18h
.text:77EFEF66 var_14 = dword ptr -14h
.text:77EFEF66 var_10 = dword ptr -10h
.text:77EFEF66 var_C = dword ptr -0Ch
.text:77EFEF66 var_8 = dword ptr -8
.text:77EFEF66 var_4 = dword ptr -4
.text:77EFEF66 hdc = dword ptr 8
.text:77EFEF66 lphandletable = dword ptr 0Ch
.text:77EFEF66 lpenhmetarecord = dword ptr 10h
.text:77EFEF66 nhandles = dword ptr 14h
.text:77EFEF66
.text:77EFEF66 ; FUNCTION CHUNK AT .text:77F18A61 SIZE 0000028A BYTES
.text:77EFEF66
.text:77EFEF66 mov edi, edi
.text:77EFEF68 push ebp
.text:77EFEF69 mov ebp, esp
.text:77EFEF6B sub esp, 58h
.text:77EFEF6E push ebx
.text:77EFEF6F mov ebx, [ebp+lpenhmetarecord]
.text:77EFEF72 mov eax, [ebx]
.text:77EFEF74 cmp eax, 1
.text:77EFEF77 push esi
.text:77EFEF78 push edi
.text:77EFEF79 jb short loc_77EFEF99
.text:77EFEF7B cmp eax, 122
.text:77EFEF7E ja short loc_77EFEF99
.text:77EFEF80 push [ebp+nhandles]
.text:77EFEF83 mov ecx, ebx
.text:77EFEF85 push [ebp+lphandletable]
.text:77EFEF88 push [ebp+hdc]
.text:77EFEF8B call _fpStartDocDlgW[eax*4] ; eax必须是79,这样才可以调用虚函数MRCOLORMATCHTOTARGET::bPlay
.text:77EFEF92
.text:77EFEF92 loc_77EFEF92: ; CODE XREF: PlayEnhMetaFileRecord(x,x,x,x)+19D72 j
.text:77EFEF92 ; PlayEnhMetaFileRecord(x,x,x,x)+19D80 j
.text:77EFEF92 pop edi
.text:77EFEF93 pop esi
.text:77EFEF94 pop ebx
.text:77EFEF95 leave
.text:77EFEF96 retn 10h
.text:77EFEF99 ; ---------------------------------------------------------------------------
.text:77EFEF99
.text:77EFEF99 loc_77EFEF99: ; CODE XREF: PlayEnhMetaFileRecord(x,x,x,x)+13 j
.text:77EFEF99 ; PlayEnhMetaFileRecord(x,x,x,x)+18 j
.text:77EFEF99 test eax, eax
.text:77EFEF9B js loc_77F18CD5
.text:77EFEFA1 jmp loc_77F18A61
.text:77EFEFA1 __stdcall PlayEnhMetaFileRecord(x, x, x, x) endp
上图中的6Ch偏移处的四字节必须是79000000,这样才可以让数据流到达MRCOLORMATCHTOTARGET::bPlay。
text:77F2EBB5 public: int __thiscall MRCOLORMATCHTOTARGET::bPlay(void *, struct tagHANDLETABLE *, unsigned int) proc near
.text:77F2EBB5 ; CODE XREF: PlayEnhMetaFileRecord(x,x,x,x)+25 P
.text:77F2EBB5 ; DATA XREF: .data:77F327C0 o
.text:77F2EBB5
.text:77F2EBB5 var_264 = dword ptr -264h
.text:77F2EBB5 var_260 = dword ptr -260h
.text:77F2EBB5 var_25C = dword ptr -25Ch
.text:77F2EBB5 var_258 = dword ptr -258h
.text:77F2EBB5 var_254 = dword ptr -254h
.text:77F2EBB5 var_250 = dword ptr -250h
.text:77F2EBB5 var_24C = dword ptr -24Ch
.text:77F2EBB5 var_248 = dword ptr -248h
.text:77F2EBB5 var_244 = dword ptr -244h
.text:77F2EBB5 var_240 = dword ptr -240h
.text:77F2EBB5 lp_buff = dword ptr -20Ch
.text:77F2EBB5 var_4 = dword ptr -4
.text:77F2EBB5 hdc = dword ptr 8
.text:77F2EBB5 lphandletable = dword ptr 0Ch
.text:77F2EBB5
.text:77F2EBB5 mov edi, edi
.text:77F2EBB7 push ebp
.text:77F2EBB8 mov ebp, esp
.text:77F2EBBA sub esp, 264h
.text:77F2EBC0 mov eax, ___security_cookie
.text:77F2EBC5 push ebx
.text:77F2EBC6 mov ebx, [ebp+hdc]
.text:77F2EBC9 mov [ebp+var_4], eax
.text:77F2EBCC mov eax, [ebp+lphandletable]
.text:77F2EBCF push esi
.text:77F2EBD0 push eax
.text:77F2EBD1 mov esi, ecx
.text:77F2EBD3 mov [ebp+var_258], ebx
.text:77F2EBD9 call MRCOLORMATCHTOTARGET::bCheckRecord(tagHANDLETABLE *) ;这个函数里规定[esi+0x14]+[esi+0x10]+0x1B==[esi+0x4]
.text:77F2EBDE test eax, eax
.text:77F2EBE0 jz loc_77F2ED19
.text:77F2EBE6 mov eax, [esi+8]
.text:77F2EBE9 xor ecx, ecx
.text:77F2EBEB inc ecx
.text:77F2EBEC cmp eax, ecx
.text:77F2EBEE push edi
.text:77F2EBEF mov [ebp+var_254], ecx
.text:77F2EBF5 jnz loc_77F2ED03 ; ENHMETARECORD.iType==EMR_HEADER ?
.text:77F2EBFB test [esi+0Ch], cl
.text:77F2EBFE jz loc_77F2ECB2 ; Record size是否是偶数
从上边的代码知道要触发漏洞必须符合的几个条件:
1) ENHMETARECORD.iType==EMR_HEADER,也就是第一个RECORD。
2) 当esi指向上图蓝色的位置时,([esi+0x14]+[esi+0x10]+0x1B)& 0FFFFFFFCh==[esi+0x4]。
3) Record size必须是偶数。
.text:77F2ECB2 loc_77F2ECB2: ; CODE XREF: MRCOLORMATCHTOTARGET::bPlay(void *,tagHANDLETABLE *,uint)+49 j
.text:77F2ECB2 mov edi, 10002h
.text:77F2ECB7 push edi ; const_10002
.text:77F2ECB8 push 4 ; const_4
.text:77F2ECBA lea eax, [esi+18h]
.text:77F2ECBD push eax ; lp_sz
.text:77F2ECBE push ebx ; hdc
.text:77F2ECBF call IcmGetColorSpaceByName(x,x,x,x)
.text:77F2ECC4 mov ebx, eax ;必须返回0
.text:77F2ECC6 test ebx, ebx
.text:77F2ECC8 jnz short loc_77F2ECE2
.text:77F2ECCA push edi ; int
.text:77F2ECCB push 4 ; int
.text:77F2ECCD lea eax, [esi+18h]
.text:77F2ECD0 push eax ; 指向RECORD的内容
.text:77F2ECD1 push [ebp+var_258] ; int
.text:77F2ECD7 call IcmCreateColorSpaceByName(x,x,x,x) ;有漏洞的函数
IcmGetColorSpaceByName必须返回0。
.text:77F0335B __stdcall IcmCreateColorSpaceByName(x, x, x, x) proc near
.text:77F0335B ; CODE XREF: IcmUpdateLocalDCColorSpace(x,x)+1043 p
.text:77F0335B ; IcmUpdateLocalDCColorSpace(x,x)+150B7 p ...
.text:77F0335B
.text:77F0335B var_250 = dword ptr -250h
.text:77F0335B var_24C = dword ptr -24Ch
.text:77F0335B var_248 = dword ptr -248h
.text:77F0335B var_244 = dword ptr -244h
.text:77F0335B var_240 = dword ptr -240h
.text:77F0335B String1 = word ptr -20Ch
.text:77F0335B var_4 = dword ptr -4
.text:77F0335B arg_0 = dword ptr 8
.text:77F0335B lpString2 = dword ptr 0Ch
.text:77F0335B arg_8 = dword ptr 10h
.text:77F0335B arg_C = dword ptr 14h
.text:77F0335B
.text:77F0335B mov edi, edi
.text:77F0335D push ebp
.text:77F0335E mov ebp, esp
.text:77F03360 sub esp, 250h
.text:77F03366 mov eax, ___security_cookie;使用了GS
.text:77F0336B mov edx, [ebp+lpString2]
.text:77F0336E push esi
.text:77F0336F mov esi, [ebp+arg_0]
.text:77F03372 mov [ebp+var_4], eax
.text:77F03375 push edi
.text:77F03376 xor eax, eax
.text:77F03378 mov ecx, 93h
.text:77F0337D lea edi, [ebp+var_250]
.text:77F03383 rep stosd
.text:77F03385 and [ebp+var_244], eax
.text:77F0338B mov eax, [ebp+arg_8]
.text:77F0338E mov [ebp+var_240], eax
.text:77F03394 push edx ; lpString2
.text:77F03395 lea eax, [ebp+String1]
.text:77F0339B push eax ; lpString1
.text:77F0339C mov [ebp+var_250], 50534F43h
.text:77F033A6 mov [ebp+var_24C], 400h
.text:77F033B0 mov [ebp+var_248], 24Ch
.text:77F033BA call ds:lstrcpyW(x,x) ; 如果第一个RECORD的内容足够大就可以溢出。
.text:77F033C0 push [ebp+arg_C]
.text:77F033C3 lea eax, [ebp+var_250]
.text:77F033C9 push 0
.text:77F033CB push eax
.text:77F033CC push esi
.text:77F033CD call IcmCreateColorSpaceByColorSpace(x,x,x,x)
.text:77F033D2 mov ecx, [ebp+var_4]
.text:77F033D5 pop edi
.text:77F033D6 pop esi
.text:77F033D7 call __security_check_cookie(x)
.text:77F033DC leave
.text:77F033DD retn 10h
.text:77F033DD __stdcall IcmCreateColorSpaceByName(x, x, x, x) endp
本人菜鸟一只,请指教,谢谢。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!