Project Zero 关于 Fuzzing ImageIO 的研究

这篇博客文章讨论了在新的上下文中的图像格式解析器中的漏洞:流行的 Messenger 应用程序中的无交互代码路径。这项研究的重点是 Apple 生态系统及其提供的图像解析 API:ImageIO 框架。我们发现了图像解析代码中的多个漏洞,并将其报告给 Apple 或相应的开源图像库维护者,然后进行了修复。在这项研究期间,针对闭源二进制文件的轻量级且开销低的引导 Fuzzing 方法得以实施,并与本博文一起发布。

整个博客中描述的漏洞可以通过 Messenger 来访问,但不属于其代码库。因此,供应商需要修复它们。

Project Zero 关于 Fuzzing ImageIO 的研究0x01 基础介绍

在对 Messenger 应用进行逆向时,我在无需用户交互就可以到达的代码路径上遇到了以下代码(手动反编译为 ObjC 并稍作简化):

NSData* payload = [handler decryptData:encryptedDataFromSender, …];

if (isImagePayload) {

UIImage* img = [UIImage imageWithData:payload];

…;

}

此代码解密从发送方收到的作为传入消息一部分的二进制数据,并从中实例化 UIImage 实例。

https://developer.apple.com/documentation/uikit/uiimage?language=objc

然后,UIImage 构造函数将尝试自动确定图像格式。之后,将接收到的图像传递给以下代码:

CGImageRef cgImage = [image CGImage];

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef cgContext = CGBitmapContextCreate(0, thumbnailWidth, thumbnailHeight, …);

CGContextDrawImage(cgContext, cgImage, …);

CGImageRef outImage = CGBitmapContextCreateImage(cgContext); UIImage* thumbnail = [UIImage imageWithCGImage:outImage];

此代码的目的是渲染输入图像的较小尺寸的版本,以用作用户通知中的缩略图。毫不奇怪,在其他 Messenger 应用中也可以找到类似的代码。本质上,如上所示的代码将 Apple 的 UIImage 图像解析和 CoreGraphics 图像渲染代码转换为 0click 攻击面。

https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html

如果满足以下先决条件,则可以使用所描述的技术来利用内存破坏漏洞:

1. 一种从处理邮件的过程发送的传输形式;

2. 每次启动 ASLR 至少需要一些内存映射;

3. 自动重启服务。

在那种情况下,该漏洞可能例如用于破坏指向 ObjC 对象(或类似对象)的指针,然后构造一个崩盘 Oracle 以绕过 ASLR,然后再执行代码。

在当前的攻击场景中,所有前提条件都得到满足,因此促使人们对暴露图像解析代码的鲁棒性进行一些研究。查看 UImage 的文档,可以发现以下句子:“使用图像对象表示各种图像数据,并且 UIImage 类能够管理基础平台支持的所有图像格式的数据”。这样,下一步就是确切确定底层平台支持哪些图像格式。

0x02 ImageIO.framework 简介

在 ImageIO 框架中实现了对传递到 UIImage 的图像数据的解析。因此,可以通过逆向 ImageIO 库(在 macOS 上为 /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO 或在 iOS 上 dyld_shared_cache 的一部分)来枚举受支持的图像格式。

在 ImageIO 框架中,每种受支持的图像格式都有一个专用的 IIO_Reader 子类。每个 IIO_Reader 子类都应实现一个 testHeader 函数,当给定一个字节块时,该函数应决定这些字节是否以阅读器支持的格式表示图像。下面显示了 LibJPEG 阅读器的 testHeader 实现的示例实现。它只是测试输入的几个字节以检测 JPEG Header Magic。

bool IIO_Reader_LibJPEG::testHeader(IIO_Reader_LibJPEG *this, const unsigned__int8 *a2, unsigned__int64 a3, const__CFString *a4)

{

return *a2 == 0xFF && a2[1] == 0xD8 && a2[2] == 0xFF;

}

通过列出不同的 testHeader 实现,可以编译 ImageIO 库支持的文件格式列表。列表如下:

IIORawCamera_Reader :: testHeader

IIO_Reader_AI :: testHeader

IIO_Reader_ASTC :: testHeader

IIO_Reader_ATX :: testHeader

IIO_Reader_AppleJPEG :: testHeader

IIO_Reader_BC :: testHeader

IIO_Reader_BMP :: testHeader

IIO_Reader_CUR :: testHeader

IIO_Reader_GIF :: testHeader

IIO_Reader_HEIF :: testHeader

IIO_Reader_ICNS :: testHeader

IIO_Reader_ICO :: testHeader

IIO_Reader_JP2 :: testHeader

IIO_Reader_KTX :: testHeader

IIO_Reader_LibJPEG :: testHeader

IIO_Reader_MPO :: testHeader

IIO_Reader_OpenEXR :: testHeader

IIO_Reader_PBM :: testHeader

IIO_Reader_PDF :: testHeader

IIO_Reader_PICT :: testHeader (仅适用于 macOS)

IIO_Reader_PNG :: testHeader

IIO_Reader_PSD :: testHeader

IIO_Reader_PVR :: testHeader

IIO_Reader_RAD :: testHeader

IIO_Reader_SGI :: testHeader (仅限 macOS)

IIO_Reader_TGA :: testHeader

IIO_Reader_TIFF :: testHeader

尽管此列表包含许多熟悉的格式(JPEG,PNG,GIF 等),但也有许多相当奇特的格式(KTX 和 ASTC),并且其中某些似乎特定于 Apple 生态系统(图标使用 ICNS,Animojis 使用 ATX)

对不同格式的支持也有所不同。某些格式似乎完全受支持,并且通常使用似乎是开源解析库的格式来实现,该库可以在 macOS 上的 /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources 中找到:libGIF.dylib,libJP2.dylib ,libJPEG.dylib,libOpenEXR.dylib,libPng.dylib,libRadiance.dylib 和 libTIFF.dylib。其他格式似乎仅能处理最常见的情况。

最后,某些格式(例如 PSD)似乎也支持进程外解码(在 macOS 上,由 /System/Library/Frameworks/ImageIO.framework/Versions/A/XPCServices/ImageIOXPCService.xpc 处理)解析器中的沙箱漏洞。但是,似乎无法指定是在公共 API 中在进程内还是进程外执行解析,并且未尝试更改默认行为。

0x03 Fuzzing 闭源图像解析器

考虑到可用图像格式的广泛范围以及大多数代码都没有源代码的事实,Fuzzing 似乎是显而易见的选择。

使用哪种 Fuzzer 和 Fuzzing 方法的选择不是很明确。由于大多数目标代码不是开源的,因此许多标准工具不能直接应用。此外,为简化起见,我决定将 Fuzzing 限制为单个 Mac Mini。因此,模糊器应:

1. 以尽可能少的开销运行,以充分利用可用的计算资源;

2. 利用某种代码覆盖率指导。

最后,我决定在 Honggfuzz 之上自己实现一些功能。Fuzzing 方法的想法大致基于以下文章:全速 Fuzzing:通过覆盖率引导的跟踪减少 Fuzzing 的开销

https://github.com/google/honggfuzz https://arxiv.org/abs/1812.11875

并通过以下方式实现了闭源代码的轻量级,低开销的引导 Fuzzing:

1. 枚举程序 / 库中每个基本块的起始偏移量,这是通过简单的 IDAPython 脚本完成的;

2. 在运行时,在 Fuzzing 的过程中,用断点指令(Intel 上的 int3)替换每个未发现的基本块的第一个字节。覆盖位图中的原始字节和对应的偏移量存储在专用的影子内存映射中,该映射的地址可以从修改后的库的地址计算得出。

3. 安装一个 SIGTRAP 处理程序,它将:

a. 检索故障地址并计算库中的偏移量以及影子存储器中相应条目的地址

b. 将基本块标记为在全局覆盖位图中找到

c. 用原始字节替换断点

d. 恢复执行

由于仅检测未发现的基本块,并且由于每个断点仅触发一次,因此运行时开销迅速接近零。但是,应该指出的是,这种方法只能实现基本的块覆盖,而不能实现例如 AFL 所使用的边缘覆盖,对于封闭源目标,可以通过动态二进制工具来实现 尽管有一些性能开销。因此,它将更加“粗糙”,例如,将相同的基本块的不同过渡视为相等,而 AFL 则不然。这样,在相同的迭代次数下,此方法可能会发现较少的漏洞。我认为这是可以接受的,因为此研究的目的不是要彻底发现所有漏洞,而是要快速测试图像解析代码的鲁棒性并突出显示攻击向量。在任何情况下,始终最好由维护人员通过源代码访问来进行彻底的 Fuzzing。

https://project.inria.fr/FranceJapanICST/files/2019/04/19-Kyoto-Fuzzing_Binaries_using_Dynamic_Instrumentation.pdf https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz

通过修补 honggfuzz 的客户端工具代码并编写 IDAPython 脚本来枚举基本块偏移量,可以很容易地实现所描述的方法。补丁程序和 IDAPython 脚本都可以在此处找到。

然后,Fuzzer 从大约 700 个种子图像的小型语料库开始,覆盖支持的图像格式,并运行了数周。最后,确定了以下漏洞:

P0 问题 1952*

ImageIO 使用 libTiff 时发生的一个错误,导致受控数据被写入内存缓冲区末尾。没有为这个问题分配 CVE,可能是因为在我们报告之前 Apple 已在内部发现它。

CVE-2020-3826 / P0 问题 1953

处理大小参数无效的 DDS 图像时,堆上的读取越界。

CVE-2020-3827 / P0 版本 1956

使用优化的解析器处理 JPEG 图像时,越界写入堆中。

CVE-2020-3878 / P0 1974 年

PVR 解码逻辑中可能出现一对一的错误,导致越过输出缓冲区的末端越界写入了另一行像素数据。

CVE-2020-3878 / P0 发行 1984

PVR 解码器中的一个相关错误导致越界读取,该错误可能与 1974 年 P0 版本具有相同的根本原因,因此被分配了相同的 CVE 编号。

CVE-2020-3880 / P0 发行 1988

在处理 OpenEXR 映像期间超出范围的读取。

最后一个问题有些特殊,因为它发生在与 ImageIO 捆绑在一起的第三方代码中,即 OpenEXR 库的问题。由于该库是开源的,因此我决定也分别对其进行模糊处理。

0x04 OpenEXR

OpenEXR 是“用于计算机成像应用程序的高动态范围(HDR)图像文件格式”。该解析器是用 C 和 C ++实现的,可以在 github 上找到。

https://github.com/AcademySoftwareFoundation/openexr

如上所述,OpenEXR 库通过 Apple 的 ImageIO 框架公开,因此通过 Apple 设备上各种流行的 Messenger 应用程序公开为 0click 攻击面。尽管我还没有进行其他研究来支持这种说法,但攻击面可能不仅限于消息传递应用程序。

由于该库是开源的,因此“常规”引导的 Fuzzing 更容易执行。我使用了一个运行在大约 500 个内核上的 Google 服务器集群,覆盖率指导的 Fuzzing,持续了大约两个星期。该 Fuzzer 以使用 llvm 的 SanitizerCoverage 进行边缘覆盖为指导,并通过使用常见的二进制突变策略对现有信号进行突变,并从大约 80 个现有的 OpenEXR 图像作为种子开始,生成了新的输入。

确定了八个可能的独特漏洞,并将其作为 P0 问题 1987 报告给 OpenEXR 维护人员,然后在 2.4.1 版本中修复。接下来简要概述它们:

CVE-2020-11764

在 copyIntoFrameBuffer 函数中的堆上(可能是图像像素)越界写入。

CVE-2020-11763

导致 std :: vector 超出边界读取的漏洞。之后,调用代码将写入此向量的元素插槽中,从而可能损坏内存。

CVE-2020-11762

一个越界内存,正在读取越界数据,然后又可能越界写入数据。

CVE-2020-11760 ,CVE-2020-11761,CVE-2020-11758

像素数据和其他数据结构的各种越界读取。

CVE-2020-11765

在堆栈上的越界读取,可能是由于先前发生了一个不正确的错误,该错误先前覆盖了堆栈上的字符串空终止符。

CVE-2020-11759

可能发生整数溢出问题,导致写入野指针。

有趣的是,最初由 ImageIO Fuzzer 发现的崩盘(发行于 1988 年)在上游 OpenEXR 库中似乎无法再现,因此直接报告给了 Apple。可能的解释是,Apple 发行了过时的 OpenEXR 库版本,并且该错误已在上游修复。

0x05 研究总结

媒体格式解析仍然是一个重要的问题。其他研究人员也证明了这一点,以下两项也可以说明:

·https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/

·https://www.facebook.com/security/advisories/cve-2019-11931

当然,这表明应该在供应商 / 代码维护者方面对输入解析器进行连续的 Fuzzing。此外,允许像 ImageIO 这样的库的客户端限制所允许的输入格式,并可能选择加入进程外解码,可以帮助防止利用。

在信使端,一种建议是通过将接收方限制为少量受支持的图像格式(至少对于不需要交互的消息预览)来减少攻击面。在这种情况下,发送方将在将任何不支持的图像格式发送给接收方之前对其重新编码。就 ImageIO 而言,这将使攻击面从大约 25 种图像格式减少到很少或更少。

这篇博客文章描述了作为操作系统或第三方库一部分的图像解析代码最终如何通过流行的 Messenger 暴露于 0click 攻击表面。对公开代码的模糊处理导致许多新漏洞被修复。经过足够的努力(以及由于自动重启服务而授予的利用尝试),很可能在 0click 攻击情形下可以将某些发现的漏洞用于 RCE。不幸的是,很可能还会存在其他错误,或者将来会引入其他错误。因此,对这种和类似媒体格式的解析代码进行持续的 Fuzzing,并积极减少攻击面是很有价值的。

参考及来源:https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html

Project Zero 关于 Fuzzing ImageIO 的研究

Project Zero 关于 Fuzzing ImageIO 的研究

来源链接:mp.weixin.qq.com

声明:以上内容采集自互联网,作品版权归原创作者所有内容均以传递信息为目的,不代表本站同意其观点,不作为任何投资指导。币圈有风险,投资需谨慎
提示:投资有风险,入市需谨慎,本资讯不作为投资理财建议。请理性投资,切实提高风险防范意识;如有发现的违法犯罪线索,可积极向有关部门举报反映。
你可能还喜欢