前言以及准备工作
关于 CVE-2017-7269,网上的大神们的分析的都很有高度,我做为一个小白,拿着这种给了 exp 的漏洞,我没有想太多,直接 windbg 和 IDA 动静结合,调试进行分析,不过,这样可能造成分析有些片面,毕竟是纯逆向进行分析。
好了,首先说说。环境 Windows Server 2003R2 上开启 WebDAV 服务的 IIS6.0。工具 Windbg,IDA。接着修改网上的 EXP 代码我们现在不需要完美执行这个 EXP,我们需要它崩溃,所以去掉作者添加的 shellcode,只保留 EXP 部分,如下图:
现在进行调试,Windbg 直接附加进程,上网查了查 IIS 的进程名称为 W3WP.EXE,好的附加,然后 g 跑起来。接着把修改后的 EXP 运行。好了,如下图所示,它崩溃了。
退出 Windbg,再次附加 W3WP.EXE 的时候,发现已经找不到这个进程了,这是 IIS应用池机制的问题,解决办法有两个:
备份虚拟机镜像
在IIS信息服务管理器里面把网站下的任意网站右键浏览一下,W3WP.EXE就会重新启动
接着进行调试,这个漏洞作者是给了漏洞函数的 ScStoragePathFromUrl。使用 IDA 静态分析吧,那么 dll 是那个呢,作者并没写明 dll 名称,但是在 IIS 信息服务管理器开启 WebDAV 服务的时候可以知道,如下图。Dll 名称为 httpext.dll。
我们直接把这个 dll,放入 IDA,加载其符号表。然后 CTRL+P 查找函数,输入函数名 ScStoragePathFromUrl。双击进行进行分析。
其中很有趣地方里面有明显的内存拷贝,而且有if判断,那么是简单的栈溢出吗?
If 判断地址
漏洞原理攻击分析
我们继续进行动态调试,知道了dll 的名称,我们先用 sxe ld:httpext 下加载 dll 时的断点。
通过 IDA 知道了 ScStoragePathFromUrl 这个函数的 retn 地址为 0x673F702C,那我们直接在这个地址下断点,看看每次执行完这个函数时候栈返回地址是否被覆盖,但是不幸的是我尝试几次,返回地址没有被明显的直接覆盖,最后都会以崩溃掉。
好像直接观察堆栈并不能看出什么来,换个思路,完全从逆向的角度分析,先不断下断点定位崩溃函数,看函数崩溃的原因是什么。进过几次的断点后,定位到了ScStoragePathFromUrl 内部的一个函数 ScStripAndCheckHttpPrefix,这个函数代码如下。
没有错,这个函数调用了虚函数,那么这个洞是简单的堆溢出吗?
再次进行调试,这次进行大量的逆向数据记录,希望从数据的变化中分析出这个洞的成因到底是什么。
首先我们要确定一下我们的要关注的数据什么?(地址通过 IDA 得到)
内存拷贝 代码地址 0x673F6F99
判断是否进行数据拷贝的代码地址 代码地址 0x673F6F55
于是 windbg下断点,执行情况如下
可以发现这个数据拷贝并不会每次进入 ScStoragePathFromUrl 函数都会进行拷贝,而且拷贝次数只有 3 次,分别是第二调用 ScStoragePathFromUrl 函数,第三调用 ScStoragePathFromUrl 函数,第五调用 ScStoragePathFromUrl 函数(其实没进行拷贝的调用也很重要),好吧,既然拷贝了三次数据,那么就下断点进行查看到底拷贝了什么数据,对堆栈进行了什么的影响吧。
再进入 windbg,只对内存拷贝进行下断点,并且每次跟完全部的拷贝,第一次拷贝的结果如下(代码太多,不全部展示)
第一次拷贝的数据范围
edi 0130f804~0130f958
(第二次和第三次执行的时候 edi 的地址会大改,但是篇幅的原因,不能全部展示)
现在讲讲通过上面的代码再结合IDA,Windbg,我自己找到数据关系。
一共三次大内存拷贝,每次进行一次大内存拷贝时,有 4 次小拷贝,其中有两次不会拷贝,而另外进行拷贝的两次的地址edi是连续的,而 esi 是不连续的。
Edi 的地址如果在某次大拷贝的时候变成一个对象的地址,然后进行拷贝,是不是就可以利用了呢?原作者也确实这么做了。
三次大内存拷贝的edi的地址变化范围如下:
第一次拷贝的数据edi:0x0130f804~0x0130f958
第二次拷贝的数据edi:0x680312c0~0x68031464
第三次拷贝的数据edi:0x0130fab4~0x0130fc08
通过数据好像看起来第一次和第三次好像是栈溢出。第二次是堆溢出
但是 edi、esi 的值是怎么被修改的,edi 的值来自哪里?Esi 的值来自哪里,这个问题需要弄明白。
通过 IDA,和 windbg,我找到了这些代码
通过看上面的第一次拷贝的范围可以知道,第二次开始的 edi(堆地址)的来源地址 0130f7a0,它并没有在第一次拷贝的范围内,但是这个地址就不能通过别的方法修改吗?
通过继续跟踪函数我找到了这条指令
当前的esp=0130f7a4,push也就是在给0130f7a0赋值
而ebp+0Ch =0x0130f7b8来自于
Edi来自于
跟到了这里原来数据来源是 0130f90c,结合第一次拷贝的数据范围 edi:0x0130f804~0x0130f958 可以知道,这个地址就是拷贝进去的数据。(这里由于篇幅有限只分析,怎么通过第一次拷贝的数据,修改第二次将进行拷贝的 edi 的值,第二次的拷贝怎么修改第三次的 edi 值类推)
那么 ESI 是什么呢
通过几次再内存中的查看
[1] 第一次拷贝前的 esi 指向的内存内容
[2] 第二次拷贝前的 esi 指向的内存内容
[3] 第三次拷贝前的esi指向的内存内容
可以知道 esi 的内容就只想我们发送数据的地址。
好了,我们现在能知道原作者通过第一次的溢出,使第二次的 edi,变成自构造的对象堆地址,进行了第二次堆溢出,那么第三次溢出的作用是什么?
第三次拷贝的数据范围 edi:0x0130fab4~0x0130fc08
下面这条指令是给 ecx(this指针) 赋值的,此时的 ebp-14h=0130fbbc 在第三次的溢出范围。而且结合第二次拷贝的数范围据 edi:0x680312c0~0x68031464 可以知道现在赋值的ecx就是第二次拷贝的起始地址。
//ecx值来源栈地址 0130f960内容 ->变成680312c0
接下来在执行虚函数时 ecx 已经是构造的 exp 的地址
进入之后发现虚函数的内容如下
把 ecx 赋值给 esp,retn 后,就从对象开始执行,并进行 ROP,绕过 DEP
漏洞成因
最后拷贝数据的大小从哪里来的?
进行每次大拷贝的第二次拷贝的 ecx 作为循环的次数
而 ecx 的值的来自于 ebx(edx总为0)
ebx来自eax
eax来自ds:impwcslen返回值
所以可以看出它是以数据的大小作为我们拷贝数据的大小,并没有拷贝大小限制,导致溢出。
PS:分析了半天,如果说,它为什么了去了 shellcode 就会崩,其实崩的地址就是第二次拷贝的数据末尾 +0x4,崩的原因是当执行到的第二次拷贝的数据末尾 +0x4 地址时,它的内容为 0x0000,这个硬编码对应汇编代码add byte ptr [eax],al,因为 eax 的值指向地址无效,所以崩溃。
其实这个位置本来该是 shellcode 的起始地址(执行到这里已经关闭 DEP 可以顺利执行数据代码了)。因为我们去掉了 shellcode,并没有任何数据,以地址访问异常导致崩溃。
本文由 看雪漏洞分析小组成员 kanss 原创
戳👇 图片加入看雪漏洞分析小组哦!
❤ 往期热门内容推荐
更多优秀文章,长按下方二维码,“关注看雪学院公众号”查看!
看雪论坛:http://bbs.pediy.com/
微信公众号 ID:ikanxue
微博:看雪安全
投稿、合作:www.kanxue.com