再谈 IE Object Type Property 溢出漏洞创建时间:2003-09-08 文章属性:原创 文章提交:eyas (eyas_at_xfocus.org) 再谈 IE Object Type Property 溢出漏洞 Author: ey4s Email : eyas#xfocus.org Site : www.xfocus.net Date : 2003-09-08 首先感谢Nanika在前段时间发布的关于此漏洞的研究心得,他的文章使我获益颇深。 我的测试环境为 windows 2000 server 英文版 + sp4(默认codepage是GB) IE 6.0 英文版 + sp1 urlmon.dll 6.0.2800.1106 假如html页面中包含如下字符: <object type="aaaa">xxxx</object> 那么IE的处理流程如下: 1.字符串"aaaa"(以后称这个字符串为m_buff_1)用MultiByteToWideChar函数转换成 unicode(以后称这个字符串为u_buff_1)。 2.u_buff_1被截短为0x100字节(截短后的字符串称为u_buff_2)。 3.u_buff_2作为函数urlmon!CoGetClassObjectFromURL的第5个参数,做进一步处理。 4.u_buff_2用WideCharToMultiByte函数转换成多字节形式(w_buff_2)。 5.w_buff_2作为函数urlmon!GetClassMime的第1个参数做进一步处理。 6.w_buff_2作为函数urlmon!ComposeHackClsidFromMime的参数做进一步处理。 处理流程函数调用关系如下: urlmon!CoGetClassObjectFromURL |_ urlmon!AsyncGetClassBitsEx |_ urlmon!AsyncGetClassBits2Ex |_ urlmon!GetClsidFromExtOrMime |_ WideCharToMultiByte |_ urlmon!GetClassMime(溢出) |_ urlmon!CCodeDownload::DoCodeDownload |_ urlmon!CCodeDownload::GetNextOnInternetSearchPath |_ urlmon!ComposeHackClsidFromMime(溢出) m_buff_1经过我们精心构造之后,可以分别使上述流程中的第5步和第6步都发生stack 溢出。网上现有的分析资料好像都只留意到了第6步的溢出,也许是我孤陋寡闻^_^。 ok!我们先来看第一个。 urlmon!GetClassMime: 702cc586 55 push ebp 702cc587 8d6c2494 lea ebp,[esp-0x6c] 702cc58b 81eca4030000 sub esp,0x3a4 //[ebp+0x74]即第一个参数,前面说的w_buff_2 702cc591 8b4574 mov eax,[ebp+0x74] .... //将0x7030a754处的"MIME\Database\Content Type\" copy到[ebp-0x38]中 702cc5b4 8a9154a73070 mov dl,[ecx+0x7030a754] 702cc5ba 88540dc8 mov [ebp+ecx-0x38],dl 702cc5be 41 inc ecx 702cc5bf 84d2 test dl,dl 702cc5c1 75f1 jnz urlmon!GetClassMime+0x2e (702cc5b4) //w_buff_2地址保存在edx 702cc5c3 8bd0 mov edx,eax //定位w_buff_2结束地址 702cc5c5 8a08 mov cl,[eax] 702cc5c7 40 inc eax 702cc5c8 84c9 test cl,cl 702cc5ca 75f9 jnz urlmon!GetClassMime+0x3f (702cc5c5) //计算dest buffer的结束地址 702cc5cc 8d7dc8 lea edi,[ebp-0x38]//dest buffer 702cc5cf 2bc2 sub eax,edx//计算w_buff_2长度 702cc5d1 4f dec edi 702cc5d2 8a4f01 mov cl,[edi+0x1] 702cc5d5 47 inc edi 702cc5d6 84c9 test cl,cl 702cc5d8 75f8 jnz urlmon!GetClassMime+0x4c (702cc5d2) //将w_buff_2 copy到堆栈,溢出! 702cc5da 8bc8 mov ecx,eax 702cc5dc c1e902 shr ecx,0x2//w_buff_2长度除于4 702cc5df 8bf2 mov esi,edx//source buffer 702cc5e1 f3a5 rep movsd //w_buff_2除于4后余下的继续copy到堆栈 702cc5e3 8bc8 mov ecx,eax 702cc5e5 8d4568 lea eax,[ebp+0x68] 702cc5e8 50 push eax 702cc5e9 83e103 and ecx,0x3 702cc5ec 6a01 push 0x1 702cc5ee f3a4 rep movsb ...... 702cc67d 83c56c add ebp,0x6c 702cc680 c9 leave 702cc681 c20c00 ret 0xc 从上述的汇编代码中我们可以看出,堆栈的buffer总长度是0x38+0x6c=0xA4。 还原成C代码的话,应该是类似如下吧: char *str = "MIME\Database\Content Type\"; GetClassMime(char *w_buff_2,...) { char buff[0xa4]; strcpy(buff, str); strcat(buff, w_buff_2); ...... } str长度固定为0x1B,0xA4-0x1B=0x89,所以我们提供的w_buff_2只要大于等于0x89, 便可使IE溢出,控制程序流程。0x100字节的u_buff_2转换成多字节的w_buff_2后, 长度 大于等于0x80,小于等于x100,具体长度取决于你构造的buff以及系统的codepage。 系统默认codepage是GB时,w_buff_1构造如下: [哈*68][a][AAAA][BBBB][CCCCDDDDEEEE][FFFF] 68怎么来的?68*2+1=137,即0x89。 IE溢出后,ebp=0x41414141,eip=0x42424242,esp指向的是"FFFF"起始的地方。溢出 时SEH离stack buffer比较远,覆盖不到,溢出后函数ret时也就只有esp指向我们的 buffer附近,所以大概只能用的jmp esp的方式来得到控制。jmp esp地址比较难找通用的, 所以exp的通用性会比较低。 我们可以这样构造w_buff_1: [sc(0x89+4 bytes)][jmp esp][nop * 0xC][jmp_back_to_sc] 要满足几个条件: 1)w_buff_1经过两次转换后不会被改变。 2)要保证w_buff_1转换成unicode后,长度小于等于0x100。 sc最长可以有0x89+4=0x8d,即141字节。141字节可以干些什么?写复杂功能的sc 可能不够,但用来搜索内存中真正发挥功能的shellcode便足够了。 另外,溢出发生后,函数ret之前,sc中有几个字节会被改变,这点也要注意到。 由以上的分析可以看出,利用这个溢出的exp要依赖于jmp esp地址以及系统语言平台。 如果你有更好的利用方法,请不吝赐教^_^。 接下来我们来看第2个发生溢出的地方。 ComposeHackClsidFromMime函数的原型类似为: ComposeHackClsidFromMime(char *pOutBuf, int iOutBufSize, char *w_buff_2) urlmon!ComposeHackClsidFromMime: 702f1b7a 55 push ebp 702f1b7b 8bec mov ebp,esp 702f1b7d 81ec04010000 sub esp,0x104 702f1b83 8b4d10 mov ecx,[ebp+0x10] 702f1b86 56 push esi 702f1b87 8bf1 mov esi,ecx 702f1b89 8a09 mov cl,[ecx] 702f1b8b 84c9 test cl,cl 702f1b8d 8d85fcfeffff lea eax,[ebp-0x104] 702f1b93 888dfcfeffff mov [ebp-0x104],cl 702f1b99 741e jz 702f1bb9 //判断是否为"/",是的话扩展为"_2F_",然后写入到stack buffer中 702f1b9b 80f92f cmp cl,0x2f 702f1b9e 750f jnz 702f1baf 702f1ba0 c6005f mov byte ptr [eax],0x5f 702f1ba3 40 inc eax 702f1ba4 c60032 mov byte ptr [eax],0x32 702f1ba7 40 inc eax 702f1ba8 c60046 mov byte ptr [eax],0x46 702f1bab 40 inc eax 702f1bac c6005f mov byte ptr [eax],0x5f 702f1baf 46 inc esi 702f1bb0 8a0e mov cl,[esi] 702f1bb2 40 inc eax 702f1bb3 84c9 test cl,cl //不是"/"则直接写入到stack buffer中 702f1bb5 8808 mov [eax],cl 702f1bb7 75e2 jnz 702f1b9b //把扩展过后的buffer,按照一定格式写入到pOutBuf 702f1bb9 8d85fcfeffff lea eax,[ebp-0x104] 702f1bbf 50 push eax 702f1bc0 8b450c mov eax,[ebp+0xc] 702f1bc3 68e01b2f70 push 0x702f1be0 702f1bc8 48 dec eax 702f1bc9 50 push eax 702f1bca ff7508 push dword ptr [ebp+0x8] 702f1bcd ff15fc132b70 call dword ptr [urlmon!_imp__wnsprintfA] 702f1bd3 83c410 add esp,0x10 702f1bd6 33c0 xor eax,eax 702f1bd8 5e pop esi 702f1bd9 c9 leave 702f1bda c20c00 ret 0xc 从以上汇编代码中我们可以看到,stack buffer长度是0x104。从前面的分析我们知 道,w_buff_2长度要控制在0x89内,不然在urlmon!GetClassMime就溢出了,就到不了 这里了。在测试的时候,某些平台下,w_buff_2的长度好像必须控制在0x80内,不然到 不了这里。我们姑且把w_buff_2最大长度限制为0x80吧。 有两种办法可以利用此处溢出漏洞。 第一,jmp esp。这样构造w_buff_1: [/*64][BBBB][AAAA][CCCC][write_addr][write_len][A*4][shellcode40bytes] 上述字符串加一起一共128字节,即0x80。溢出后,函数会做如下操作: wnsprintf(write_addr, write_len-1,0x702f1be0,[ebp-0x104]) 0x702f1be0处的字符串是"clssid=%s"。 所以write_addr必须为一个可以写入不包含00的地址,而且要符合宽字符编码。不然 在wnsprintfA调用的时候就会发生异常,函数无法返回,加上seh我们无法覆盖,所以程 序流程我们就会得不到控制。 函数返回后,ebp=0x41414141,eip=0x43434343,esp指向shellcode。 这样看来,第二个溢出比第一个溢出在利用起来更麻烦: 1)要提供一个可写的不带0的地址 2)sc只能有40字节 其实wnsprintfA函数很容易绕过,给它提供一个负数的size就ok了^_^,而且这个 size是我们可以控制的。但是,只有在IE6+sp1及以后的版本中,调用的才是wnsprintfA, 以往的版本调用的都是wsprintfA。 用另外一种方法来构造w_buff_1可以得到多一点的空间来放置shellcode: [sc(x bytes)][/(y bytes)][eip][write_addr][write_len][A*4][jmp_back_to_sc] x和y要满足以下关系: x+y+20=0x80 <-保证buff不会被截短 x+y*4=0x104+4 <-用于保证buff扩展后能触发溢出 ==>x=56 y=52 山穷水尽?NO!我们为什么不充分利用wsprintfA函数呢。 第二,利用wsprintfA。构造以下buffer, [sc(x bytes)]["/"(y bytes)][eip][write_addr] 注意:我们所构造的buffer都必须要保证经过两次转换后不被改变。 x和y要满足以下关系: (1)x+y*4=0x104+4 <-用于保证buff扩展后能触发溢出 (2)x+y+8=0x80 <-保证buff不会被截短 很简单可以算出,x=72,y=48。 寻找一处不为0的,可以写入的地址作为write_addr。wsprintfA调用完成之后, write_addr+7指向的就是sc了。所以可以直接把write_addr+7作为eip。 sc最长可以有72字节,用来写搜索内存中真正功能的shellcode足够了。可以先 接管异常,然后从0x1000开始往内存高处搜索,100%能搜索到^_^。 所以利用第2个溢出,写个针对某种语言平台的通用的exp是完全可以实现的。 补丁是如何修补上述漏洞的?以urlmon.dll 6.0.2800.1226为例: urlmon!GetClassMime: 1a41ca51 55 push ebp 1a41ca52 8d6c2494 lea ebp,[esp-0x6c] 1a41ca56 81eca4030000 sub esp,0x3a4 ...... 1a41ca7f 689b000000 push 0x9b//<--长度限制 1a41ca84 688ca7451a push 0x1a45a78c//"MIME\Database\Content Type\" 1a41ca89 8d45c8 lea eax,[ebp-0x38] 1a41ca8c 50 push eax 1a41ca8d ff15e411401a call dword ptr [urlmon!_imp__lstrcpynA (1a4011e4)] 1a41ca93 8d45c8 lea eax,[ebp-0x38] 1a41ca96 50 push eax 1a41ca97 ff15d411401a call dword ptr [urlmon!_imp__lstrlenA (1a4011d4)] 1a41ca9d b99a000000 mov ecx,0x9a 1a41caa2 2bc8 sub ecx,eax//<--长度限制 1a41caa4 51 push ecx 1a41caa5 57 push edi 1a41caa6 8d45c8 lea eax,[ebp-0x38] 1a41caa9 50 push eax 1a41caaa ff157813401a call dword ptr [urlmon!_imp__StrNCatA (1a401378)] urlmon!ComposeHackClsidFromMime: 1a441d62 55 push ebp 1a441d63 8bec mov ebp,esp 1a441d65 81ec04010000 sub esp,0x104 1a441d6b 8b5510 mov edx,[ebp+0x10] 1a441d6e 56 push esi 1a441d6f 8d85fcfeffff lea eax,[ebp-0x104] 1a441d75 33f6 xor esi,esi 1a441d77 8a0a mov cl,[edx] 1a441d79 84c9 test cl,cl 1a441d7b 8808 mov [eax],cl 1a441d7d 742a jz urlmon!ComposeHackClsidFromMime+0x47 (1a441da9) 1a441d7f 80f92f cmp cl,0x2f 1a441d82 751a jnz urlmon!ComposeHackClsidFromMime+0x3c (1a441d9e) 1a441d84 81fe00010000 cmp esi,0x100//<--长度检查 1a441d8a 7d1d jge urlmon!ComposeHackClsidFromMime+0x47 (1a441da9) 1a441d8c c6005f mov byte ptr [eax],0x5f 1a441d8f 40 inc eax 1a441d90 c60032 mov byte ptr [eax],0x32 1a441d93 40 inc eax 1a441d94 c60046 mov byte ptr [eax],0x46 1a441d97 40 inc eax 1a441d98 c6005f mov byte ptr [eax],0x5f 1a441d9b 83c603 add esi,0x3 1a441d9e 46 inc esi//长度 1a441d9f 42 inc edx//src buffer addr 1a441da0 40 inc eax//dest buffer addr 1a441da1 81fe03010000 cmp esi,0x103//<--长度检查 1a441da7 7cce jl urlmon!ComposeHackClsidFromMime+0x15 (1a441d77) 1a441da9 802000 and byte ptr [eax],0x0 1a441dac 8d85fcfeffff lea eax,[ebp-0x104] 1a441db2 50 push eax 1a441db3 8b450c mov eax,[ebp+0xc] 1a441db6 68d01d441a push 0x1a441dd0 1a441dbb 48 dec eax 1a441dbc 50 push eax 1a441dbd ff7508 push dword ptr [ebp+0x8] 1a441dc0 ff15fc13401a call dword ptr [urlmon!_imp__wnsprintfA (1a4013fc)] 1a441dc6 83c410 add esp,0x10 1a441dc9 33c0 xor eax,eax 1a441dcb 5e pop esi 1a441dcc c9 leave 1a441dcd c20c00 ret 0xc 参考资料: <>Internet Explorer Object Type Property Overflow 的研究心得 http://www.xfocus.net/articles/200306/551.html |