Windows Workstation Service远程溢出的分析

创建时间:2003-11-17 更新时间:2003-11-18
文章属性:转载
文章提交: l0pht (anonymous_at_21cn.com)

Windows Workstation Service远程溢出的分析



By snake. snake@cnns.net

Company page: http://www.cnns.net



题注:世界著名安全组织eEye Digital Security,于11月11日在 http://www.eeye.com 站点主页公布了他们关于对Windows Workstation服务存在缓冲溢出缺陷的发现。这个缺陷牵涉到的是多数Windows操作系统赖以正常运行的基本服务,可以被远程利用,相关的TCP端口是139和445。这个缺陷的公布,对正处于多事之秋的Windows操作系统安全体系又是一次冲击。不过,正如CNNS提出的“攻击是检验网络安全的最佳手段”,每一次操作系统的严重缺陷和他们的攻击利用代码,以及后来出现的蠕虫,都迫使厂商加速推出安全解决方案,有关这些服务的安全性也将得到一定提升。这种攻与防的循环,将伴随任何一个主流操作系统的生命周期。

本文是CNNS研发部门对此缺陷的攻击利用技术进行逐步分析的记述,以裨专业人员参考。时间仓促,有任何错误与不足之处,欢迎来信交流:snake@cnns.net



///



Eeye在11月11日公布了Windows Workstation存在远程溢出的漏洞,这是微软的又一个基本服务,存在严重的被攻击缺陷。

本文将就如何利用这个漏洞做逐步分析。

根据eeye的一些公开的信息来看,漏洞是出在 wkssvc.dll的 vsprintf调用。推断应该是没有检查输入缓冲的长度。利用函数NetValidateName 可以直接攻击。



下面的环境是:

客户端:win2k,和服务端建立 ipc$连接,然后,用 NetValidateName 进行交互,触发溢出。具体的sample代码不贴出来了,packetstorm和其他站点已经公布了不少。。。



服务端:被攻击端(简体中文win2k + sp3)



打开windbg,跟踪相关的函数,开始是 RPCRT4.dll的  NdrServerCall2调用,这个短时间内没有办法细看和消化,不理会,继续跟踪进去。。。

然后又是一些调用。包括 NdrServerInitializeNew的调用, NdrPointerUnmarshell和 NdrConformantStringUnmarshall的调用,这些也可以不理会,继续跟踪,呵呵,机器其实已经重新启动了N次,没有关系,虚拟机。:)

  下面是出错函数的分析,上面的一些初始化动作不理会,总之,错误是出在这里面,利用也是在这个函数返回的时候利用。。



.text:76724CD7 ; int __stdcall sub_76724CD7(HANDLE hFile,int,int)

.text:76724CD7 sub_76724CD7    proc near               ; CODE XREF: sub_76724DB5+20p

.text:76724CD7

.text:76724CD7 var_81A         = byte ptr -81Ah

.text:76724CD7 var_819         = byte ptr -819h

.text:76724CD7 Buffer          = byte ptr -818h

.text:76724CD7 var_817         = byte ptr -817h

.text:76724CD7 NumberOfBytesWritten= dword ptr -14h

.text:76724CD7 SystemTime      = _SYSTEMTIME ptr -10h

.text:76724CD7 hFile           = dword ptr  8

.text:76724CD7 arg_4           = dword ptr  0Ch

.text:76724CD7 arg_8           = dword ptr  10h

.text:76724CD7

.text:76724CD7                 push    ebp

.text:76724CD8                 mov     ebp, esp

.text:76724CDA                 sub     esp, 818h       ; //!!这里只分配了 0x818=2072个字节的空间给全部变量

.text:76724CE0                 cmp     [ebp+hFile], 0  ; //判断是否无效的文件句柄

.text:76724CE4                 jz      locret_76724DB1 ; //如果是,则返回

.text:76724CEA                 push    edi

.text:76724CEB                 mov     edi, offset unk_76727C60

.text:76724CF0                 push    esi

.text:76724CF1                 push    edi             ; lpCriticalSection

.text:76724CF2                 call    ds:EnterCriticalSection ; //进入临界空间

.text:76724CF8                 xor     esi, esi

.text:76724CFA                 cmp     dword_76727A3C, esi ; 判断是否需要打印时间信息

.text:76724D00                 jz      short loc_76724D3C

.text:76724D02                 lea     eax, [ebp+SystemTime] ; 下面进行时间信息字符串的输出。

.text:76724D05                 push    eax             ; lpSystemTime

.text:76724D06                 call    ds:GetLocalTime

.text:76724D0C                 movzx   eax, [ebp+SystemTime.wSecond]

.text:76724D10                 push    eax

.text:76724D11                 movzx   eax, [ebp+SystemTime.wMinute]

.text:76724D15                 push    eax

.text:76724D16                 movzx   eax, [ebp+SystemTime.wHour]

.text:76724D1A                 push    eax

.text:76724D1B                 movzx   eax, [ebp+SystemTime.wDay]

.text:76724D1F                 push    eax

.text:76724D20                 movzx   eax, [ebp+SystemTime.wMonth]

.text:76724D24                 push    eax

.text:76724D25                 lea     eax, [ebp+Buffer]

.text:76724D2B                 push    offset a02u02u02u02u02 ; "%02u/%02u %02u:%02u:%02u "

.text:76724D30                 push    eax

.text:76724D31                 call    ds:sprintf      ; //at first, format the time string...

.text:76724D37                 add     esp, 1Ch

.text:76724D3A                 mov     esi, eax

.text:76724D3C

.text:76724D3C loc_76724D3C:                           ; CODE XREF: sub_76724CD7+29j

.text:76724D3C                 push    [ebp+arg_8]

.text:76724D3F                 lea     eax, [ebp+esi+Buffer] ; 得到输出缓冲的地址,这里是 esi-0x818

.text:76724D3F                                         ; 其中,esi是调整的输出指针。如果打印了时间信息,

.text:76724D3F                                         ; 则=时间字符串的长度。否则,=0。

.text:76724D46                 push    [ebp+arg_4]     ; 这里的格式是:

.text:76724D46                                         ; NetpValidateName: checking to see if '%ws' is valid as type %d name.

.text:76724D46                                         ;

.text:76724D46                                         ; *** 注意,是 %ws 和  %d 的参数。

.text:76724D46                                         ; %ws。。。。比较麻烦的转换。嘿嘿,还是有办法的。

.text:76724D46                                         ;

.text:76724D49                 push    eax

.text:76724D4A                 call    ds:vsprintf     ; 这里发生了溢出

.text:76724D50                 add     esp, 0Ch

.text:76724D53                 add     esi, eax        ; 这里判断是否 esi+eax = 0。如果没有输出,则做个标记=0

.text:76724D55                 jz      short loc_76724D6D

.text:76724D57                 cmp     [ebp+esi+var_819], 0Ah ; ...搞不懂为什么这里要判断。如果没有回车,也做个标记。。:(

.text:76724D57                                         ;

.text:76724D5F                 jnz     short loc_76724D6D

.text:76724D61                 mov     dword_76727A3C, 1

.text:76724D6B                 jmp     short loc_76724D78 ; 增加一个回车到输出缓冲的开头,很好玩,

.text:76724D6B                                         ;

.text:76724D6D ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

.text:76724D6D

.text:76724D6D loc_76724D6D:                           ; CODE XREF: sub_76724CD7+7Ej

.text:76724D6D                                         ; sub_76724CD7+88j

.text:76724D6D                 xor     eax, eax

.text:76724D6F                 test    eax, eax

.text:76724D71                 mov     dword_76727A3C, eax

.text:76724D76                 jz      short loc_76724D91

.text:76724D78

.text:76724D78 loc_76724D78:                           ; CODE XREF: sub_76724CD7+94j

.text:76724D78                 mov     [ebp+esi+var_819], 0Dh ; 增加一个回车到输出缓冲的开头,很好玩,

.text:76724D78                                         ;

.text:76724D80                 mov     [ebp+esi+Buffer], 0Ah

.text:76724D88                 and     [ebp+esi+var_817], 0

.text:76724D90                 inc     esi

.text:76724D91

.text:76724D91 loc_76724D91:                           ; CODE XREF: sub_76724CD7+9Fj

.text:76724D91                 lea     eax, [ebp+NumberOfBytesWritten]

.text:76724D94                 push    0               ; lpOverlapped

.text:76724D94                                         ; 这里进行写文件的动作。

.text:76724D94                                         ; 注意,WriteFile的第4个参数

.text:76724D94                                         ; lpNumberOfBytesWritten 是在

.text:76724D94                                         ; ebp-14的位置,会改写buffer,所以,

.text:76724D94                                         ; 如果有shellcode放到那里,就要小心

.text:76724D94                                         ; 这个位置的数据了。。

.text:76724D96                 push    eax             ; lpNumberOfBytesWritten

.text:76724D97                 lea     eax, [ebp+Buffer]

.text:76724D9D                 push    esi             ; nNumberOfBytesToWrite

.text:76724D9E                 push    eax             ; lpBuffer

.text:76724D9F                 push    [ebp+hFile]     ; hFile

.text:76724DA2                 call    ds:WriteFile

.text:76724DA8                 push    edi             ; lpCriticalSection

.text:76724DA9                 call    ds:LeaveCriticalSection ; 这里 LeaveCriticalSection。还好,参数edi没有被改掉。

.text:76724DA9                                         ; 否则,进行攻击的时候又多了很多麻烦了。

.text:76724DAF                 pop     esi

.text:76724DB0                 pop     edi

.text:76724DB1

.text:76724DB1 locret_76724DB1:                        ; CODE XREF: sub_76724CD7+Dj

.text:76724DB1                 leave

.text:76724DB2                 retn    0Ch             ; ok,函数返回,嘿嘿,处理好了,就会执行我们的shellcode。

.text:76724DB2 sub_76724CD7    endp

.text:76724DB2

.text:76724DB5



如上分析,程序在vsprintf中,的参数 %ws进行格式化,将NetValidateName的第2个参数作为输入,格式化后,输出数据到堆栈中去,当内容太长的时候,就会发生堆栈溢出。



现在分析被攻击的可能性。

1.         这个函数开始就检查文件句柄的合法性,如果没有办法打开 %windir%/debug/netsetup.log的话,则这个函数没有办法被执行。所以,当触发该服务器执行文件记录时,连接的账号如果没有权限打开该文件,则不能进行以后的攻击。除非,服务器权限设置不正确,或者是fat32的文件格式,没有办法进行权限限制。嘿嘿。。。

2.         输入的长度不长的时候,会发生堆栈溢出,只要在溢出点(大概是 0x818-12)的位置,填入 jmp esp的内容,然后,在下一个地址开始的地方,写入shellcode,就可以运行代码了。

3.         输入的长度很长的时候,会触发windows的结构化异常保护,如果数据足够大,把异常结构都覆盖了,也可以实现跳转,不过,这个时候就比较危险了,系统再也无法被第2次溢出,因为函数开始的时候,进入了临界区,而退出的时候,这种情况下,该临界变量没有被释放。

4.         NetValidateName第2个参数的缓冲,输入的时候,是Unicode的,被vsprintf的时候,是 %ws,会被转换回ANSI字符串。这里调用的vsprintf是msvcrt.dll的同名函数,而非libc的标准库中的函数。这个vsprintf在转换的时候,并不是100%都能够转换,跟踪了一下,发现是调用 wctomb的函数来执行。到最后,即使能够被转换,最后输出可能也是0。标准的可见字符串是可以被转换的,但是,多字节语言的数据就没有那么好弄了。也就是说,执行的代码暂时只能限制在可见字符内,其它的,要看如何小心构造的。更多的细节,需要详细研究。( 结论就是:多语种通用的攻击代码,要实现,还有比较长的一段路要走。。。)

5.         输入的缓冲中,0x818-12-0x14-strlen(“NetpValidateName: checking to see if '”)的位置的数据,会被 WriteFile的参数改写,所以,这里要注意shellcode被破坏。

6.         这个攻击如果在shellcode执行完成以后,采用exitthread,则应该可以无限次被溢出。。。



总之,上面已经分析了不少东西了,写出具体的通用的攻击程序只是时间的问题。里面好像也没有更多的技术难点和技巧,没有什么好说了。希望已经实现了的大侠慎重公布攻击工具和代码,本文章只是从技术角度进行分析,这种”垃圾代码”其实还是比较容易防护的,只要把vsprintf转换成 vsnprintf就可以避免出现类似的问题!。



Eeye还有一个攻击方法,是 NetAddAlternateComputerName的攻击,针对NTFS的格式有效,我没有看,XP下可以,2K下没有这个函数,不知道XP能否攻击2K。。。有空再弄了。



*** 后记 ***

真不知道如何评价这个漏洞,微软的软件,这个SERVER和WORKSTATION的服务提供了很强大的服务功能,但是,偏偏存在那么严重的漏洞。。。。恐怖,自己写程序的时候还是要小心点,尤其是广为使用的程序。



SNAKE. 2003/11/17 morning
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值