您现在的位置: 游戏网 >> 游戏学院 >> 黑客教程 >> 安全漏洞 >> 文章正文

最新更新

热门速递

分析 MS06-040 浅入浅出

『 更新时间:2007-9-1 14:18:06 』『 字体:变小 变大 』『 作者:佚名 | 来源:网络 』

 转载要求: 除本站特殊申明之作品不得转载外,其余作品均可转载,转载作品请保留作者及来源信息。

    

想必很多人很关心ms06040吧~~明理人一眼就清楚06040是干什么用的了~
下面是某坛子的分析结果~

首发于0x557论坛.

起初以为这个只是2个简单的栈溢出, 一个是在 NetpwNameCompare 里面.

.text:7CDD649D mov esi, ds:__imp_wcslen
.text:7CDD64A3 lea eax, [ebp+var_408] ; 目标缓冲区
.text:7CDD64A9 push eax ; wchar_t *
.text:7CDD64AA call esi ; __imp_wcslen ; 计算长度
.text:7CDD64AC mov edi, eax
.text:7CDD64AE pop ecx
.text:7CDD64AF lea eax, [ebp+var_204] ; 目标缓冲区
.text:7CDD64B5 push eax ; wchar_t *
.text:7CDD64B6 call esi ; __imp_wcslen ; 计算长度
.text:7CDD64B8 pop ecx
.text:7CDD64B9 mov ecx, 100h ; 比较是否 > 0x100
.text:7CDD64BE cmp edi, ecx
.text:7CDD64C0 jg loc_7CDCD761
.text:7CDD64C6 cmp eax, ecx
.text:7CDD64C8 jg loc_7CDCD761
.text:7CDD64CE push [ebp+arg_0] ; wchar_t *
.text:7CDD64D1 mov esi, ds:__imp_wcscpy
.text:7CDD64D7 lea eax, [ebp+var_408]
.text:7CDD64DD push eax ; wchar_t *
.text:7CDD64DE call esi ; __imp_wcscpy ; 拷贝, 溢出!
.text:7CDD64E0 pop ecx
.text:7CDD64E1 lea eax, [ebp+var_204]
.text:7CDD64E7 pop ecx
.text:7CDD64E8 push [ebp+arg_4] ; wchar_t *
.text:7CDD64EB push eax ; wchar_t *
.text:7CDD64EC call esi ; __imp_wcscpy ; 拷贝, 溢出!

可以看到这里微软犯了一个很傻的错误, 前面2个wcslen居然判断目标缓冲区的长度! 紧接着的长度判断根本就无效了, wcscpy拷贝后就是一个栈式的缓冲区溢出.
但是在XP下面微软已经修正了这个问题, 所以这个漏洞只影响2000.
然后另外一个漏洞存在于CanonicalizePathName函数中.
2000 下的反汇编:

.text:7CDCAB37 push edi
.text:7CDCAB38 cmp [ebp+arg_0], esi ; 判断传入参数1是否为NULL
.text:7CDCAB3B mov edi, ds:__imp_wcslen
.text:7CDCAB41 mov ebx, 411h ; 长度
.text:7CDCAB46 jnz loc_7CDD6323 ; 如果不为NULL就跳转

.text:7CDD6323 loc_7CDD6323: ; CODE XREF: sub_7CDCAB28+1Ej
.text:7CDD6323 push [ebp+arg_0]
.text:7CDD6326 call edi ; call wcslen
.text:7CDD6328 mov esi, eax
.text:7CDD632A pop ecx
.text:7CDD632B test esi, esi ; 判断长度是否为0
.text:7CDD632D jz loc_7CDCAB53
.text:7CDD6333 cmp esi, ebx ; 判断长度是否 > 0x411
.text:7CDD6335 ja loc_7CDCABCF
.text:7CDD633B push [ebp+arg_0] ; wchar_t *
.text:7CDD633E lea eax, [ebp+var_414]
.text:7CDD6344 push eax ; wchar_t *
.text:7CDD6345 call ds:__imp_wcscpy ; 拷贝, 溢出!

 

虽然这里已经检测了传入的长度是否 > 0x411, 而本身缓冲区分配的长度为0x414, 但是wcslen函数是计算Unicode字符串长度的, 应该把计算后的结果乘以2判断.
显然这里没有, 所以如果你传入的数据只要不超过0x411 * 2, 是可以绕过这个长度检测的, 所以后面拷贝导致了栈式溢出.
反观Windows XP的代码:
.text:5B878808 loc_5B878808: ; CODE XREF: CanonicalizePathName(x,x,x,x,x)+32j
.text:5B878808 push esi ; wchar_t *
.text:5B878809 call ds:__imp__wcslen
.text:5B87880F mov ebx, eax
.text:5B878811 test ebx, ebx
.text:5B878813 pop ecx
.text:5B878814 jz loc_5B86A31F
.text:5B87881A cmp ebx, 208h ; 判断长度是否 > 0x208
.text:5B878820 ja loc_5B86A3C4
.text:5B878826 lea eax, [ebp+var_418]
.text:5B87882C push esi ; wchar_t *
.text:5B87882D push eax ; wchar_t *
.text:5B87882E call ds:__imp__wcscpy

注意这里是很重要的一个判断, 如果arg_04长度不为0的话, 就会进入上面那个判断是否>0x208的流程, 这样就无法到达可以利用的那部分代码了.
所以我们这里要使的arg_04不为NULL, 而且长度为0, 进入这个跳转流程.

.text:5B86A31F ; CanonicalizePathName(x,x,x,x,x)+E58Cj ...
.text:5B86A31F mov esi, ds:__imp__wcslen
.text:5B86A325 push edi ; wchar_t *
.text:5B86A326 call esi ; __imp__wcslen ; 计算arg_01的长度
.text:5B86A328 add eax, ebx
.text:5B86A32A cmp eax, 207h ; 是否大于 0x207
.text:5B86A32F pop ecx
.text:5B86A330 ja loc_5B86A3C4 ; 大于则跳转
.text:5B86A336 lea eax, [ebp+var_418]
.text:5B86A33C push edi ; wchar_t *
.text:5B86A33D push eax ; wchar_t *
.text:5B86A33E call ds:__imp__wcscat ; wcscat拷贝数据.

上面似乎也有长度检测, 栈里面分配的空间是0x420, 貌似这部分代码是没有问题, 但是值得注意的是这里用的是wcscat函数进行拷贝.
man手册中对于wcscat的描述:


#include

wchar_t *wcscat(wchar_t *ws1, const wchar_t *ws2);

DESCRIPTION

The wcscat() function appends a copy of the wide-character string pointed to by ws2 (including the terminating null wide-character code)
to the end of the wide-character string pointed to by ws1. The initial wide-character code of ws2 overwrites the null wide-character code at the end of ws1.
If copying takes place between objects that overlap, the behaviour is undefined.

可见其功能是和C中的strcat功能是差不多的, 只是一个针对unicode. 一个是ansi的. 综合对上面的汇编理解, 我们可以写一个简单的符合上述流程的函数.

void func(char *string)
{
char buff[256];

if (strlen(string) > 0 && strlen(string) < 256) // 长度检测
{
strcat(buff, string); // strcat 长度拷贝
}
}

乍看是很安全的操作, 检测了长度, 应该不会导致缓冲区溢出. 但是设想一下如果该函数被调用2次会出现什么情况.
int main(int argc, char *argv[])
{
func(argv[1]);

func(argv[1]);

[1] [2] 下一页

【点击数:】【发表评论】【加入收藏】【告诉好友】【关闭窗口

发起评论

您的姓名:
评分等级:
1分 2分 3分 4分 5分
评论内容:
1、严禁发表危害国家安全、政治、黄色淫秽等内容的评论。
2、用户需对自己在使用雪儿网络服务过程中的行为承担法律责任。
3、本站管理员有权保留或删除评论内容。
4、评论内容只代表网友个人观点,与本网站立场无关。