重新审视 CVE-2025-50165:Windows 图像组件中的一个严重缺陷

2026-01-07 18:38:35 0 167

ESET 研究人员对 CVE-2025-50165 进行了分析。这是一个严重的 Windows 漏洞,攻击者只需打开一个特制的 JPG 文件(一种最常用的图像格式)即可远程执行代码。该漏洞由 Zscaler ThreatLabz 发现并记录,引起了我们的兴趣。微软虽然将其严重性评为“严重”,但认为其可利用性较低。我们的根本原因分析使我们能够精确定位故障代码的位置并重现崩溃。我们认为,实际利用该漏洞的难度比表面看起来要大。





ESET 研究人员对 CVE-2025-50165 进行了分析。这是一个严重的 Windows 漏洞,攻击者只需打开一个特制的 JPG 文件(一种最常用的图像格式)即可远程执行代码。该漏洞由 Zscaler ThreatLabz 发现并记录,引起了我们的兴趣。微软虽然将其严重性评为“严重”,但认为其可利用性较低。我们的根本原因分析使我们能够精确定位故障代码的位置并重现崩溃。我们认为,实际利用该漏洞的难度比表面看起来要大。

本文要点:
ESET 研究人员对 CVE-2025-50165 漏洞进行了深入分析,并用伪代码片段进行说明。
我们提供了使用简单的 12 位或 16 位 JPG 图像重现崩溃的方法,并检查了最初发布的补丁。
CVE-2025-50165 是 JPG 图像编码和压缩过程中的一个缺陷,而不是解码过程中的一个缺陷。
我们的结论探讨并重新评估了该漏洞的可利用性和攻击场景。

概述
2025年11月20日,Zscaler ThreatLabz 发布了一篇文章,记录了 CVE-2025-50165 的发现。这是一个影响巨大的远程代码执行漏洞,存在于WindowsCodecs.dll中。该库是 Windows 的主要接口库,负责处理大多数常见的图像文件格式,例如 JPG、PNG、GIF、BMP 等。Zscaler 的发现以及微软提供的描述表明,该漏洞源于WindowsCodecs.dll中一个未初始化的函数指针的解引用,而 WindowsCodecs.dll 是 Windows 图像处理组件的一部分。Zscaler 成功地将解引用问题定位在jpeg_finish_compress函数内部,该函数恰好在 JPG 图像流被压缩和(重新)编码时被调用。提到图像处理漏洞,人们首先想到的可能是图像渲染时发生的解析和解码错误。因此,这条较为特殊的易受攻击代码路径给我们留下了一些想要解答的问题:

导致未初始化函数指针被解引用的易受攻击代码路径的具体条件是什么?
jpeg_finish_compress何时被调用?
为什么函数指针没有初始化?
鉴于 JPG 图片在网络上的普遍性,我们想要找到答案,因此我们首先调查了导致崩溃的代码。

坠机地点
根据CVE-2025-50165条目的描述,WindowsCodecs.dll版本 10.0.26100.0 及之前的版本(10.0.26100.4946 之前)均受影响。我们分析了易受攻击的版本 10.0.26100.4768(SHA-1:5887D96565749067564BABCD3DC5D107AB6666BD ),并将其与第一个已修复的版本 10.0.26100.4946(SHA-1: 4EC1DC0431432BC318E78C520387911EC44F84FC )进行了二进制比较。下载相应的符号后,我们查看了导致崩溃的函数jpeg_finish_compress。根据WindowsCodecs.dll 中的版本字符串——libjpeg-turbo 版本 3.0.2(构建版本 20250529) ——该 DLL 依赖于libjpeg-turbo 库的一个较旧的实现(发布于 2024 年 1 月 24日)来处理 JPG 图像。公开的代码库使我们能够将大部分相关的二进制代码和结构映射到其对应的源代码。例如,编译后的jpeg_finish_compress函数与其在此处提供的源代码非常相似,如图 1 所示。

根据 Zscaler 的分析结果,崩溃发生在jpeg_finish_compress+0xCC处,对应于图 1 中的第 48 行,此时程序正在解引用一个未知结构体 ( pub ) 偏移量为0x10的函数指针。根据libjpeg-turbo 的源代码,这对应于一个名为compress_data_12的函数指针。为了到达这个特定的路径,jpeg_compress_struct的data_precision成员需要设置为12。这对应于位深度,或者换句话说,就是用于描述颜色的位数。本质上,WindowsCodecs.dll在尝试编码 12 位精度的 JPG 图像时崩溃了。

补丁差异分析和根本原因分析
我们使用二进制差异工具Diaphora,对存在漏洞的版本 10.0.26100.4768 和已修补的版本 10.0.26100.4946 进行了差异比较,如图 2 所示。

ESET 研究人员对 CVE-2025-50165 进行了分析。这是一个严重的 Windows 漏洞,攻击者只需打开一个特制的 JPG 文件(一种最常用的图像格式)即可远程执行代码。该漏洞由 Zscaler ThreatLabz 发现并记录,引起了我们的兴趣。微软虽然将其严重性评为“严重”,但认为其可利用性较低。我们的根本原因分析使我们能够精确定位故障代码的位置并重现崩溃。我们认为,实际利用该漏洞的难度比表面看起来要大。

本文要点:
ESET 研究人员对 CVE-2025-50165 漏洞进行了深入分析,并用伪代码片段进行说明。
我们提供了使用简单的 12 位或 16 位 JPG 图像重现崩溃的方法,并检查了最初发布的补丁。
CVE-2025-50165 是 JPG 图像编码和压缩过程中的一个缺陷,而不是解码过程中的一个缺陷。
我们的结论探讨并重新评估了该漏洞的可利用性和攻击场景。
概述
2025年11月20日,Zscaler ThreatLabz 发布了一篇文章,记录了 CVE-2025-50165 的发现。这是一个影响巨大的远程代码执行漏洞,存在于WindowsCodecs.dll中。该库是 Windows 的主要接口库,负责处理大多数常见的图像文件格式,例如 JPG、PNG、GIF、BMP 等。Zscaler 的发现以及微软提供的描述表明,该漏洞源于WindowsCodecs.dll中一个未初始化的函数指针的解引用,而 WindowsCodecs.dll 是 Windows 图像处理组件的一部分。Zscaler 成功地将解引用问题定位在jpeg_finish_compress函数内部,该函数恰好在 JPG 图像流被压缩和(重新)编码时被调用。提到图像处理漏洞,人们首先想到的可能是图像渲染时发生的解析和解码错误。因此,这条较为特殊的易受攻击代码路径给我们留下了一些想要解答的问题:

导致未初始化函数指针被解引用的易受攻击代码路径的具体条件是什么?
jpeg_finish_compress何时被调用?
为什么函数指针没有初始化?
鉴于 JPG 图片在网络上的普遍性,我们想要找到答案,因此我们首先调查了导致崩溃的代码。

坠机地点
根据CVE-2025-50165条目的描述,WindowsCodecs.dll版本 10.0.26100.0 及之前的版本(10.0.26100.4946 之前)均受影响。我们分析了易受攻击的版本 10.0.26100.4768(SHA-1:5887D96565749067564BABCD3DC5D107AB6666BD ),并将其与第一个已修复的版本 10.0.26100.4946(SHA-1: 4EC1DC0431432BC318E78C520387911EC44F84FC )进行了二进制比较。下载相应的符号后,我们查看了导致崩溃的函数jpeg_finish_compress。根据WindowsCodecs.dll 中的版本字符串——libjpeg-turbo 版本 3.0.2(构建版本 20250529) ——该 DLL 依赖于libjpeg-turbo 库的一个较旧的实现(发布于 2024 年 1 月 24日)来处理 JPG 图像。公开的代码库使我们能够将大部分相关的二进制代码和结构映射到其对应的源代码。例如,编译后的jpeg_finish_compress函数与其在此处提供的源代码非常相似,如图 1 所示。

图 1. Hex-Rays IDA 反编译器对 jpeg_finish_compress 函数的输出
图 1. Hex-Rays IDA 反编译器对jpeg_finish_compress函数的输出与引用的源代码类似。
根据 Zscaler 的分析结果,崩溃发生在jpeg_finish_compress+0xCC处,对应于图 1 中的第 48 行,此时程序正在解引用一个未知结构体 ( pub ) 偏移量为0x10的函数指针。根据libjpeg-turbo 的源代码,这对应于一个名为compress_data_12的函数指针。为了到达这个特定的路径,jpeg_compress_struct的data_precision成员需要设置为12。这对应于位深度,或者换句话说,就是用于描述颜色的位数。本质上,WindowsCodecs.dll在尝试编码 12 位精度的 JPG 图像时崩溃了。

补丁差异分析和根本原因分析
我们使用二进制差异工具Diaphora,对存在漏洞的版本 10.0.26100.4768 和已修补的版本 10.0.26100.4946 进行了差异比较,如图 2 所示。

图 2. 两个库之间部分匹配和不匹配的功能
图 2. Diaphora 成功地突出显示了两个库之间部分匹配和不匹配的功能。
令人惊讶的是,文章中提到的导致崩溃的函数jpeg_finish_compress并不存在。然而,有两个与编码相关的函数被修改了: rawtransencode_master_selection和jinit_c_rawtranscode_coef_controller_turbo 。图 3 显示了rawtransencode_master_selection函数在存在漏洞版本和已修复版本之间的差异。

图 3. rawtransencode_master_selection的易受攻击版本(左)和已修复版本(右)之间的差异
唯一相关的区别似乎在于,之前内联在`rawtransencode_master_selection`函数体内的`jinit_c_rawtranscode_coef_controller_turbo`函数现在被分离出来了。查看修改后的 ` jinit_c_rawtranscode_coef_controller_turbo`函数可以发现,之前未初始化的结构体成员`compress_data_12`现在指向一个名为`rawtranscode_compress_output_16`的函数,如图 4 所示。

请注意,在存在漏洞的版本中未初始化的字段compress_data_16 ,在已修复的版本中也被设置为指向rawtranscode_compress_output_16。该函数只是一个调用rawtranscode_compress_output的存根函数,这可能表明没有专门的代码来处理 12 位或 16 位精度的 JPG 图像。

重现崩溃
正如 Zscaler 的文章中提到的,可以编译 Microsoft 提供的代码片段(https://learn.microsoft.com/en-us/windows/win32/wic/-wic-codec-jpegmetadataencoding#jpeg-re-encode-example-code)来解码和重新编码 JPG 图像。

程序编译完成后,提供 12 位或 16 位 JPG 文件即可重现崩溃问题。在libjpeg-turbo代码库中,有一个 12 位精度的示例图像可供下载,地址为。将此图像输入到重新编码示例应用程序中,程序在与 Zscaler 文章中提到的完全相同的位置崩溃。图 5 显示了调试过程中崩溃的上下文。

内存地址指向的重复十六进制值0xBAADF00D是 C 运行时 (CRT) 堆在程序调用HeapAlloc分配内存时使用的一个“魔法值”。它将内存标记为未初始化(参见https://www.nobugs.org/developer/win32/debug_crt_heap.html)。

如前所述,所分析的两个版本的WindowsCodecs.dll似乎都能处理 16 位精度的 JPG 图像。但是,在测试此类图像时,重新编码应用程序在解引用compress_data_16 函数指针时崩溃,如图 6 所示。

重现崩溃后,我们想知道libjpeg-turbo库的源代码中是否也存在这种特定的漏洞。

探索源代码
查阅libjpeg-turbo的提交记录发现,类似的问题已于2024 年 12 月 18 日通过提交e0e18de得到解决,该提交引入了 3.1.1 版本。本质上,该提交确保结构体被零初始化,并在指针为NULL时引发错误。然而,事实证明,该提交引入的所有零初始化和检查在存在漏洞的WindowsCodecs.dll版本和已修复版本中均不存在。

补丁消息还暗示了其他潜在的易受攻击的代码路径,更重要的是,在处理 JPG 图像时,解压缩过程中也可能发生崩溃,如图7 中jdapistd.c 文件的差异所示。

正如提交描述中明确指出的那样,调用应用程序只有在调用jpeg_start_compress或jpeg_start_decompress例程后错误地更改了data_precision字段时才会崩溃(由于解引用未初始化的函数指针)。这造成了一种相当特殊且可能不切实际的场景:使用WindowsCodecs.dll 的应用程序会改变内部结构的状态。虽然可能存在这样的应用程序,但 Windows Imaging Component API 似乎并不允许这种行为。

可利用性
根据我们的根本原因分析,CVE-2025-50165 的核心问题在于WindowsCodecs.dll对数据精度值非标准的 8 位 JPG 图像的处理方式。在压缩过程中,两个与精度相关的函数指针( compress_data_12和compress_data_16 )未被初始化,从而产生了两条易受攻击的代码路径,这两条路径似乎只有在(重新)编码JPG 图像时才会被访问。仅仅打开并解码渲染一张特制的图像并不会触发此漏洞。但是,如果保存图像,或者宿主应用程序(例如 Microsoft Photos 应用程序)创建图像缩略图,则可能会调用易受攻击的函数jpeg_finish_compress,如图 8 所示。


一个程序要被认定为存在漏洞,需要具备以下特征:

利用了存在漏洞的WindowsCodecs.dll版本,
在解码 12 位或 16 位 JPG 文件时不会崩溃或中止,并且
允许对图像进行重新编码。
此外,正如 Zscaler 研究人员所提到的,要利用此漏洞,必须存在地址泄露和对堆的足够控制。

结论
尽管 JPG 格式历史悠久、应用广泛,并且可能是模糊测试中最流行的数字图像格式,但某些编解码器中仍然存在漏洞。这项针​​对 CVE-2025-50165 的研究也强调了在使用第三方库时及时更新安全补丁的重要性。

根本原因分析结合补丁差异比较,证明是一种非常有效的组合,使我们能够解答最初的问题。我们发现,当WindowsCodecs.dll对 12 位或 16 位精度的 JPG 流进行编码时,会触发此错误,因为这两个精度相关的函数指针在解引用之前既没有初始化也没有检查。此外,我们还发现,当保存此类图像或从中创建缩略图时,就会发生这种情况。

本次调查得出的结论与微软对该漏洞可利用性的结论相似。事实上,由于 WindowsCodecs.dll 是一个库,如果宿主应用程序允许对 JPG 图像进行(重新)编码,则该应用程序将被视为易受攻击,并且只有当攻击者对应用程序拥有足够的控制权(例如地址泄漏、堆操作)时,该漏洞才能被利用。综上所述,该漏洞被利用的可能性似乎确实不大。

最后值得一提的是,截至撰写本文时,根据我们的测试,较新版本的WindowsCodecs.dll(例如 10.0.22621.6133,SHA-1:3F3767D05E5A91184005D98427074711F68D9950 )实现了libjpeg-turbo提交中提到的不同更改,有效地解决了缺少初始化和函数指针验证的问题。

关于作者

Celevice5篇文章24篇回复

评论0次

要评论?请先  登录  或  注册