再谈 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