Windows Workstation Service远程溢出的再次分析By snake. snake@cnns.net
Company page: http://www.cnns.net
第一次分析:http://vitter.8866.org/vitter/file/list.asp?id=126
本人前两个星期发表的《Windows Workstation Service远程溢出的分析》,其实,里面有不少细节的地方没有写清楚,特别是关于如何利用vsprintf输出到大小不正确定义的缓冲区,进行攻击的部分。借周末的机会,我再详细的研究了一下,下面是本人的一些研究心得。同样的,里面没有具体的攻击源代码,只是在技术的原理上进行分析和探讨,涉及具体化的东西,例如源代码,sorry,我就不能贴出来了,大家如果真的对这些技术感兴趣,那么,自己构造一下相应的数据包,应该是有比较大的成功率的。
要完全理解本文,需要3个方面的预备知识:
1. 理解堆栈溢出的原理
2. Windows Unicode和 多字节之间的转换知识
3. 阅读理解了上一篇文章 <<Windows Workstation Service远程溢出的分析>>
好了,转入正题。这里研究的系统对象还是Windows 2000简体中文版,其他多字节语言的版本道理上相通的,只是实现的具体不一样罢了。
参考上次的文章,可以看到问题主要是出在msvcrt.dll的vsprintf(下面所有提到的vsprintf,都是msvcrt.dll的vsprintf函数,非LIBC的vsprintf)字符格式化输出的结果上。在攻击端输入 NetValidateName(L”\\IP”, “attack string”,L “”,L””, NetSetupUnknown); ,那么,服务端就会进行相应的日志纪录(当然,这些都是在权限等条件都符合的情况下。)。 函数定义了输出缓冲字符串的变量,只有 0x804 的大小,vsprintf格式化输出内容到该变量中,如果大小超过了0x804,那么,就会覆盖其顶部的堆栈数据,包括变量和函数返回地址,造成缓冲区溢出。
Vsprintf格式化的字符串为:[NetpValidateName: checking to see if '%ws' is valid as type %d name.]。 可以看到,输入字符串到输出缓冲的转换是 %ws的转换。跟踪进去 msvcrt.dll的vsprintf时,发现流程是这样的:(分析的汇编代码略去,大家可以看msvcrt.dll的vsprintf源代码)
1. 逐个格式化字符读取,进行处理。
2. 如果字符的ASCII不是 0x20~0x78的范围,那么,跳转,这里我不关心它是如何处理的,跳过。。。
3. 根据当前的字符的ASCII值到转换表格中查找,然后,得到转换值。
4. 根据转换值,再次查表,得到实际的处理函数地址,处理如下:
a) 不同的字母,跳到不同的处理函数地址,例如。%,w,s,d,f,p等的跳转地址是不一样的。
b) 如果是%,则设置开始转换的标志位。
c) 如果是w,则设置宽字节处理标志为1。
d) 如果是s,则开始取后面的参数,进行字符串复制。
e) 其他的参数,不关心,跳过。
5. 当前字符处理完,继续读取下一个字节,直到长度超过 0x7fffffff,或者碰到0字符作为结束。否则,跳到1循环处理。
这里详细分析vsprintf中对 %ws的处理,也是上面流程中 4->d的分析。
通过跟踪,发现vsprintf的是调用 wctomb进行宽字节转换,wctomb的msdn解析是将宽字节的字符转成多字节的字符。返回值是转换的字符数。如果转换的内容是字节0,那么,返回值是1,如果转换失败,那么,返回-1。
针对wctomb再分析,跟踪进去,最后发现是调用 WideCharToMultiByte进行转换。(倒!又是和系统语言相关的功能。这样可能意味着不同系统之间,不能做到完全通用,而且,E文的字母没有多字节,怎么转换呢。。。?)。如果WideCharToMultiByte转换失败,那么,wctomb就返回-1。
Vsprintf中,当碰到转换失败的宽字节字符时,转换立刻停止,不管后面还有没有更多等待转换的字符,均不进行处理。所以,就要求在作为参数输入的所有攻击数据中,字符都必须是可以被转换的宽字节字符。
根据上面的分析结果,和上一篇文章,构造如下的攻击包。
缓冲1 缓冲2(Jmp esp地址) 缓冲3(shellcode)
MultiByte (2023字节) 4个字节 不限制
无论发送的WideChar 缓冲多大,被转换的长度必须符合上面的表格要求。
1. 缓冲1被转换回多字节的字符串以后,长度必须是 0x7e7个字节。
2. 缓冲2是jmp esp地址,被转换回多字节的字符串以后,长度必须是4个字节,并且,在service.exe进程中,内存地址指着的内容是应该是:0x54 0xc3(push esp, ret);或者 0xff 0xe4 (jmp esp);或者是 0xff 0xd4 (call esp)。
3. 缓冲3是溢出发生时,esp指针的地址,必须是正确shellcode的内容,并且,这些shellcode都是经过 WideCharToMultiByte考验的,也就是说,全部都能够被WideCharToMultiByte转换,并且,不会出现 0x00 的数据。这里对于其他字符,则不会有特别的限制,例如,不会碰到 <, >, *, ?, ‘, “, +, -, \, / 等符号的限制。
4. 整个转换后的数据包的长度不能太长(具体多少,我没有计算,但是,经验测试,超过5000个字节就会触发,这个时候用来观察现象,定位溢出的原因还是可以的。),否则,就会触发异常处理函数,这是另外的一个利用办法了,这里不详细叙述,但是,转换的道理是一样的。
上面就是条件,如何在上面的框架中,进行稳定的攻击呢?也就是说,写出针对同种语言的win2k的通用的攻击代码(不分service pack),我们必须找到如下的数据:
1. 最重要的,是有一个完全能够转换的shellcode,这个yuange前几年的文章已经说出一个技术办法,大家翻一翻,或者找得到。:)
2. 能够找到一个所有版本都相同的跳转esp地址,并且,能够成功被转换成宽字节的。以便在vsprintf中转换,还原。
3. 前面的缓冲1,有2023字节的长度,可以用来存放攻击代码,原始的内容不管,原始长度是多长也不管,但是,在WideCharToMultiByte转换后,长度必须是2023。
4. 上一篇文章说到,缓冲1末尾的一个地方,会被 WriteFile的参数改写,这里不详细计算,就不要用了。
Ok. 构造完成以后,发送,获得shell!,嘿嘿,就这么简单,可能吗?当然!
经过这些比较杂乱的分析,大家可以看到,Windows Workstation Service的通用的攻击,其实并不是不可能的。只是中间费的心事比较多而已。 开始 Webdav也好像很难,但是,毕竟也是出来了。
至于 E文的,由于 WideCharToMultiByte,无法对大于 0x78的字节成功转换,暂时想不到有更好的处理方法。也不清楚市面上流行的溢出程序是如何实现的,好像对方不用考虑这种转换的问题。真的不明白,可能是技术的角度不一样吧。如果哪位朋友理解了,那么,请告诉本人一下,让我也可以学习学习。~
Win XP也有这个溢出,但是,我研究的中文xp版本,其WideCharToMultiByte的转换的codepage竟然是437,也就是说,是E文的方式转换,很难利用。。。现在还想不到有更好的实现办法。
本溢出,虽然可以做到通用,但是无法做到多次溢出,因为,rpc函数进入本函数之前,已经至少2个地方出现了 EnterCriticalSection, 进入了临界区,虽然本溢出函数也进入了,并且函数返回的时候也的确正确退出了当前的临界区,由于溢出的特性,后面堆栈的数据已经被利用来做shellcode存放区,无法正确的保存返回地址,所以,溢出利用后,已经无法回到原路,继续原始代码的执行了,前面的2个临界区的进入,也无法退出。下次再进入,也会造成锁定,长久等待。。。
ps:本溢出攻击成功后,不能ExitProcess,ExitThread是一个好办法。否则,Services.exe进程死掉后,系统会保护性的重新启动。但是,在xp的环境下,由于workstation服务是由svchost.exe启动的,所以,可以重复溢出攻击而不会重新启动。只是,xp到现在还无法成功利用。。。
Ok,讨论到此,本文只从技术的角度上来探讨实现的可能性,希望悟出来的高手不要放出代码,毕竟这个漏洞还是很广泛的存在着的,一个不小心,又会造成蠕虫泛滥,慎重,慎重!
Snake. 2003/11/30 night.