查看原文
其他

微软“照片”应用Raw 格式图像编码器漏洞 (CVE-2021-24091)的技术分析

代码安全实验室 代码卫士 2022-04-06

 聚焦源代码安全,网罗国内外最新资讯!


2020年12月和2021年2月,微软两次针对“照片”应用的Raw格式图像编码器发布安全更新,其中2月9日修复的是CVE-2021-24091。笔者从事文件格式方面的安全研究工作,找到研究人员提供的poc 后,对该漏洞进行了漏洞验证和分析。

根据MSRC 和漏洞发现者公开的信息,该漏洞存在于Windows Imaging Component解码Olympus E300 相机拍摄的原始图像的相关函数中。由于互联网并没有过多公开资源介绍 E300 RAW 格式(笔者仅找到一份公开资料,见https://myolympus.org/E300/#RAW),所以本文将从漏洞产生机制的角度,分析漏洞产生的原因。


0x00 漏洞验证


1、在Win10 1903 x64系统上,使用gflags工具为图片app开启页堆,双击图片文件打开(图片默认应用为照片App)。一段时间后,App退出进程。

2、使用Windbg附加照片App(Windbg 调试UWP方法详见微软文档),敲击g运行程序。一段时间后,进程崩溃。如下图所示:

0x01 漏洞分析


使用ida pro 加载崩溃的dll,可以确认崩溃发生在一个将数据写入缓冲区的循环之中。


通过这段代码,可初步大致判断循环体条件语句导致循环次数过多,造成越界写入。函数部分变量的初始化如下:


结合函数开始时局部变量的初始化和变量交叉引用的情况来看,可以得出:

1、代码通过读取某个类型对象的成员值,并加以运算,计算结果即为需要申请的缓冲区的大小;

2、缓冲区分为两部分,一部分为size*(this+12320*4) * 2的数据块(chunk2)另一部分数据块(chunk1)的大小为*(this+0x12320*4) * 16 / 10个字节。


3、执行初始化后,代码首先执行一个for循环,在这个循环体的内部执行另一个for循环,向chunk2内写入数据。

所以,这段代码的伪代码如下:

chunk2_size = this->mem_12320;chunk1_size = chunk2_size * 16 / 10;char * data = (char *)malloc(chunk1_size + 2 * chunk2_size);for (char *i = data+chunk1_size; v12 < this->mem_12325; a5 = v12){ expresions; for (char *pdata = data, char *j = i; j < chunk+chunk1_size+2*chunk2_size; pdata += 3, j += 4) { if ((pdata – data) %15) pdata++; *(word *)j = pdata[1] << 8 | pdata[0]; //写入两个字节 *(word *)(j+1) = pdata[2] << 4 | pdata[1] >> 4; //写入两个字节 } expressions;}

按照上面的伪代码,每次循环都写入四个字节,循环次数应该是(chunk2_size * 2 / 4)向上取整的值。在第一个for循环中,当 i = &data[chunk1_size],即从第二个chunk头部开始循环写入字节时,如果chunk2_size为奇数,循环次数 * 4 将大于chunk2_size。也就是说,最后一次循环中,写入后两个字节时,将造成越界,产生访问违例。


0x02 漏洞调试


使用windbg 附加App进程,并在崩溃函数设置断点:

bu WindowsCodecsRaw!COlympusE300LoadRaw::olympus_e300_load_raw


图片App 加载poc 文件时,获取的chunk2_size0xd79,是一个奇数。


通过上文的伪代码可得:

chunk1_size = 0x158e

data 指向的内存区域是一个大小为0x3080的缓冲区。


代码执行到第二个for循环时,需要写入数据的指针存放在r15中,即为chunk2 缓冲区的起始地址(r15  ==  data + chunk1_size):


所以,在这种情况下,循环次数应为⌈ (0xd79 * 2 / 4) ⌉,即为1725 次。而缓冲区只有2 * chunk2_size, 共6898个字节,不能支持1725*4 = 6900个字节的写入。由此可知,最后一次循环将产生两个字节的越界。至此,漏洞分析完毕。

循环次数记录如下:共命中725次,与分析无误。


0x03 关于这段代码的来源


该漏洞的发现者提到:通过函数名查找,这段代码与LibRaw Lite库的同名函数有较大的相似性,但是这个库目前已经停止维护和更新了,源代码下载地址失效,所以笔者在github上找到了类似的代码片段。(https://github.com/coolshou/DIR-850L_A1/blob/92b64054ac75795429b9a6678baef5b3e69dfc10/progs.gpl/image_tools/netpbm-10.35.81/converter/other/cameratopam/camera.c)


对比可知,这段代码与漏洞函数在实现上基本一致,所以微软的代码应该是在此基础上重新实现了一遍。

因此,基于代码供应链安全的考量,建议使用LibRaw Lite 库函数的代码,由相关人员自行更新补丁。


0x04 微软代码补丁


微软官方在2月9日推出的补丁内容如下:


这个补丁比较简单粗暴,即:复制第二个像素(第二次写入双字节)时,判断指针是否指向缓冲区末端。


0x05 小结


笔者认为这个函数也存在其它问题。首先,从分析来看,缓冲区分配基于16个字节对齐的原则,而如果计算错误,不确定是否会导致像素解析不完全或产生其它影响;其次,由于在执行free之前,并没有对解析的像素数量进行判断,因此如果文件大小不符合规范,可能会将未初始化的数据解码,并产生意外的像素值。由于笔者对olympus e300 raw格式了解粗浅,所以并没有从poc 文件构造方式的角度进行解读,仅给出漏洞产生的原因。希望大家多多交流。



推荐阅读
Windows DNS Server 远程代码执行漏洞 (CVE-2021-24078) 的详细原理分析
FireEye 红队失窃工具大揭秘之:分析复现SolarWinds RCE 0day (CVE-2020-10148)
APACHE OFBIZ XML-RPC 反序列化漏洞 (CVE-2020-9496) 的复现与分析




题图:Pixabay License


转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。



奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的

产品线。

    觉得不错,就点个 “在看” 或 "” 吧~


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存