原文标题:《用 Opatch 修补一个 0day:Windows gdi32.dll 内存信息泄露 (CVE-2017-0038)》
我第一个修复的 0day, (我甚至不是一个微软的开发人员)。
by Luka Treiber, 0patch Team
我正在在网上钓鱼以查找我下一个去修补的目标时,有东西被抓到了我的网里. 然后 Mitja 走进来并且问:
- 是的,但不是那个意思,这是一个 0day
正如你可能已经知道的那样,上一个补丁日微软并没有修复这个漏洞。前段时间(Project Zero)连续公布了几个微软的 0day 漏洞, CVE-2017-0038 是里面的第一个。但是不用担心,即使在黑暗中也有一线光明。我上周有些空闲时间来查看该漏洞的问题所在,所以现在我可以给你一个非常第一手的对这个漏洞的 0patch 修补。
CVE-2017-0038 是因为 EMF 图片格式解析逻辑里,在解析一个图像文件提供的像素数量时,没有充分检查图像文件中指定的图像尺寸。如果图像大小足够大,解析器会被触发去读取正在被解析的 EMF 文件内存映射区域前面的内存内容。一个攻击者可以利用这个楼顶偷取应用程序存储在内存里面的敏感数据,或者作为对其他利用技术的辅助(只要突破 ASLR)
我想要感谢谷歌 Project Zero 团队的 Mateusz Jurczyk 对这个漏洞做的简介而准确的报告,这使我快速找出了 bug 的所在,并且跳转到补丁代码去修补它。
Mateusz's 的 POC 的作用原理就像描述的那样——通过启动一个 IE 11,并且拖入一个 poc.emf 进去,这是我得到的结果:
这张图片需要被放大到最大,以看清楚通过彩色像素泄露的惊人的堆数据。而且它确实泄露了:每一次重新加载这个 POC 都会导致一幅不一样的图片。
换句话说:
这与报告里面描述的一致(我们只要仔细看就行了)。所以这个实际来源于所提供的 EMF 文件里面数据的唯一像素值就是它(FF 33 FF 33)(用4个字节表示)——此外所有的数据都从图像像素数据外面的内存区域所读取。
现在让我们来修补它。在行动之前,我们需要理解漏洞代码的位置在哪里。幸运的是报告里面紧接着就揭露了漏洞函数的名称 MRSETDIBITSTODEVICE::bPlay。这是重要的,因为 PoC 触发的过程中既没有crash也没有任何可以被调试器捕获以进行深入分析的异常。手握这部分信息后,我们可以用windbg 附加到 iexplore.exe ,并且设置一个断点:
根据报告,这就是所有问题的所在。报告里面说 cbBitsSrc 并没有检查“预期的 bitmap 字节数”-- 从 cxSrc 和cySrc 计算出来额图像大小。
我们需要一点来自 MSDN 的帮助:
在为 windbg 加载 EMRSETDIBITSTODEVICE 的符号后(这个在后面有解释),报告里面的解释一下子变得清晰起来。
当处理 PoC 内的 EMF文件时,一个 EMRSETDIBITSTODEVICE 结构体作为第一参数(该结构体的详细信息在上面截图的右下角)被传入 MRSETDIBITSTODEVICE::Play 函数(rcx 的值 复制给 rbx 的值,标记为蓝色背景)。包含的 cbBitsSrc 值被设为 4,这没有匹配被 cxSrc 和 cySrc 匹定义的图像尺寸(256个像素或者是 1024 个字节)
脑海里有了这些后,我选择了一个实际的 patch 点。为了写入尽可能少的 patch 代码,我将 patch 点设为了跳转到函数退出处的第一个分支指令(这样绕开了图像数据的处理过程),所以我可以越过这里(这句建议看原文)。在上面的代码截图中,patch 点被标记为红色,同时相应的 jump 指令被标记为亮粉色。在 patch 点,对 pvClientObjGet 的调用结果会被检验是否为0,后面紧跟一个用于跳转到函数退出处的 jne 指令(如果 rax=0).在这处逻辑里同一个 jne 可以帮助我们的 patch 代码退出函数:如果 cbBitsSrc 的值小于 cxSrc * cySrc * 4 我们只需要将 rax 设为 0 即可。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!