探索黑客技术攻防,实战研究与安全创新

导航菜单

再议漏洞 MS08-067

最近闲来无事,就对之前一个非常有名的漏洞进行了分析,该漏洞就是大名鼎鼎的MS08-067。说实话,初次分析这个漏洞,确实让我费了点功夫。

我使用的netapi32.dll的版本号是5.1.2600.2976,存在问题的函数是NetpwPathCanonicalize,最终存在问题的函数是ConPathMacros,漏洞触发函数调用关系如图1所示。

图片1.png

图1

MS08-067的问题出在一个格式化路径的函数上,函数名为ConvertPathMacros,函数的任务就是处理掉路径字符串中的“.”和“..”,例如传入路径为“\A\B\.\C\..\D\..\..\E”,经过它的处理后会变成“\A\E”,就是把路径做简化操作。

漏洞函数分析

ConvertPathMacros函数对路径字符串的识别过程主要依靠ESI指针。当识别出存在“\..\”字符串时,ESI将指向该字符串,并且EDI指向“\..\”字符串前的“\”处,EAX指向“\..\”中的后一个“\”,之后启动字符串复制过程。该复制过程说来简单,例如在\bbb\..\aaa中,就是将aaa开始(包括aaa)的数据覆盖掉bbb开始(包括bbb)的数据,达到bbb和\..\相抵消的效果。复制操作的反汇编代码如下:


push
push
call
eax
;wchar_t*
;wchar_t*
edi
ds:__imp_wcscpy


程序执行到上面的复制代码后,将开始执行复制操作,EAX指向复制操作的源串,EDI指向复制操作的目的串。

第一次复制过程

复制前,EAX指向“\..\AAAA„„”,而EDI指向“\BBBBB\..\..\AAAA„„”,__imp_wcscpy函数执行时将用EAX指向的字符串\..\AAA„„覆盖掉EDI指向的字符串\BBBBB\..\..\AAA„„所在空间。如图2和图3所示。

图片2.png

第二次复制过程中,eax指向“\AAAA„„”,而edi指向原有字符串之前的一个“\”,如图3所示位置,__imp_wcscpy函数执行时将用EAX指向的字符串\AAA„„覆盖掉EDI指向的字符串所在空间。由于edi始终指向“\..\”字符串前的第一个“\”,那么可能这时的“\”已不再是路径中的一部分,可能是其他堆栈数据,这将导致覆盖其他堆栈重要数据的结果,如图4所示。

图片3.png

内存中的关键“\”为什么在字符串“\..\”的前方会存在“\”呢?就是因为“\”的存在,才导致了覆盖关键数据,最终导致跳转到恶意代码中。关键在于,在执行第二次__imp_wcscpy函数时,在堆栈中字符串“\..\”之前的适当位置存在一个“\”符号,ASCII码为5C00。之所以在该位置存在“\”字符,是因为在调用ConPathMacros函数之前,调用了函数RtlInitUnicodeString函数。RtlInitUnicodeString函数的功能为初始化一个Unicode字符串。

VOIDRtlInitUnicodeString(PUNICODE_STRINGDestinationString//指向一个Unicode字符串的缓冲,如果SourceString未被指定,初始化的长度为0。PCWSTRSourceString//可选的指向一个NULL结尾的Unicode字符串的指针。);

RtlInitUnicodeString函数的传入参数如下:


0012F7E8
0012F7EC
0012F7FC
0012F866
(DestinationString)
(
SourceString
,
其值为
UNICODE
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA“)
RtlInitUnicodeString函数的反汇编代码如下:
7C9212D6
7C9212D7
7C9212DB
7C9212DF
置0
push
mov
mov
mov
edi
edi,dwordptr[esp+C]//SourceString
edx,dwordptr[esp+8]//DestinationString
dwordptr[edx],0//DestinationString的开头4个字节被7C9212E5movdwordptr[edx+4],edi//DestinationString开头后的4个字节被赋值为SourceString的地址
7C9212E8
7C9212EA
7C9212EC
7C9212EF
7C9212F1
or
edi,edi//判断edi是否为0,为0说明SourceString为空
short7C92130E
je
or
ecx,FFFFFFFF//将ecx置为FFFFFFFF
xor
eax,eax
//将eax置为0
repnescaswordptres:[edi]//扫描SourceString中的字符串,
ecx为长度的反。计算的是45个A..A的长度
7C9212F4
7C9212F6
7C9212F8
7C9212FE
not
shl
cmp
jbe
ecx
//将ecx取反,该值是A..A的宽字节长度
//该值乘2,将是字节长度
ecx,1
ecx,0FFFE
//0FFFE可能是字符串长度的上限


short7C921305//jbe是不高于跳转,如果ecx小于等于上限,那么直接去7C921305执行,如果7C9213007C921305movmovecx,0FFFE//ecx大于上限,那么ecx将被置为0FFFEwordptr[edx+2],cx//将把长度和长度减2,分别放入edx+2和edx指针指向的内存中。


7C921309dececx
7C92130A
7C92130B
7C92130E
7C92130F
dec
mov
pop
retn
ecx
wordptr[edx],cx
edi
8


第一次复制完成后,图5下面的蓝色区域(除\..以外)覆盖了绿色区域,图6为复制

后的效果图。

图片4.png

对复制完后的堆栈进行描述,图7、图8、图9和图10是第2次复制完后的效果图。

图片5.png

图片6.png

结语

之前在研究MS08-067漏洞时碰到了许多问题,其中一个最大的问题就是忽略了RtlInitUnicodeString函数而导致的溢出利用失败。后来经过逆向跟踪发现,该函数在溢出成功利用中起了很大的作用。就是因为该函数的存在,才导致堆栈中会多出来一个“\”字符,才能让畸形数据覆盖返回地址。

相关推荐