Windows图形漏洞深度剖析:从远程代码执行到内存泄露

本文深入分析了Windows图形设备接口(GDI)中的三个安全漏洞,包括CVE-2025-30388、CVE-2025-53766和CVE-2025-47984,详细介绍了漏洞的发现过程、技术原理和修复方案,涉及EMF+文件格式解析和内存越界操作等关键技术细节。

背景

Check Point Research (CPR) 在Windows图形设备接口(GDI)中发现了三个安全漏洞。我们及时向微软报告了这些问题,并在2025年5月、7月和8月的补丁星期二更新中得到了修复。

这些漏洞包括:

  • CVE-2025-30388:评级为重要,被认为更有可能被利用
  • CVE-2025-53766:分类为严重级别,可能允许远程攻击者在受影响系统上执行任意代码
  • CVE-2025-47984:同样评级为重要,可能导致通过网络未经授权披露敏感信息

几何图形失控 - CVE-2025-30388

我们发现了与处理EmfPlusDrawStringEmfPlusFillRectsEmfPlusFillClosedCurve记录相关的三个独立崩溃。所有三种情况都有一个共同的根源:另一个记录为利用设置了舞台。然而,结果会根据执行期间处理哪些附加记录而有所不同。

GdiPlus.dll模块的10.0.26100.3037版本中,ScanOperation::AlphaMultiply_sRGB()ScanOperation::Blend_sRGB_sRGB_MMX()EpAntialiasedFiller::OutputSpan()函数中发生了多次访问违规异常。这些异常是在系统尝试读取或写入4000/0xFA0字节堆块末尾的内存时,或者尝试访问保留但未分配的内存时触发的。

此漏洞可能允许远程攻击者使用特制的EMF+元文件执行越界读取或写入内存操作。

在我们的崩溃样本中,EmfPlusClear记录位于元文件中的EmfPlusDrawString记录之前。该记录清除输出坐标空间,并使用其Color字段定义的背景颜色和透明度进行初始化。该字段包含指定红、绿、蓝和alpha分量的EmfPlusARGB对象。此细节很重要,因为它允许攻击者在利用过程中控制写入内存的值。

进一步调查显示,EmfPlusClear记录处理程序使用EpScanBitmap::Start()函数分配一个堆块来存储4000字节(0xFA0)。然后使用指定的EmfPlusARGB对象填充此缓冲区,该对象在EmfPlusDrawString记录处理期间通过AlphaMultiply_sRGB()函数进行alpha乘法。

存储在ebx寄存器中的循环计数器从0x950开始。由于每次迭代将4字节对象写入ecx + edx处的目标缓冲区,当计数器达到0x567时,函数在1000字节后越界写入。

负空间 - CVE-2025-53766

我们在处理EmfPlusDrawRects记录时发现了第四次崩溃。在GdiPlus.dll10.0.26100.4202版本中,ScanOperation::AlphaDivide_sRGB()函数尝试写入保留但未分配的内存时发生了访问违规异常。

此漏洞可能允许远程攻击者使用特制的EMF+元文件执行越界内存写入。

此问题与我们之前讨论的CVE-2025-30388漏洞类似。该漏洞是由EmfPlusSetTSClip记录中的无效RECT对象引起的,该记录位于发生崩溃的其他记录之前。在这个新案例中,漏洞源于受影响EmfPlusDrawRects记录中的一系列EmfPlusRect对象。

EmfPlusDrawRects记录前面有一个EmfPlusObject记录,该记录指定在图形操作中使用的EmfPlusPen对象。EmfPlusPen对象定义了与笔关联的图形画刷。画刷是纯色画刷,由EmfPlusARGB值表征。攻击者可以通过此值控制eax寄存器中写入操作的源。

在位图图像中,扫描线是一行水平像素。逐行处理图像意味着一次处理一条扫描线,从左到右,从上到下。进一步分析显示,EpScanBitmap::NextBuffer()函数从未验证要处理的扫描线数量是否适合目标位图,这意味着如果调用请求的扫描线多于存在的扫描线,该函数可能会被诱骗读取或写入图像底部边缘之外。

假设为缩略图生成分配的位置为100×100(0x64 × 0x64)像素,PoC元文件中的矩形数据故意将扫描位置推到位图底部边缘之外,并触发越界写入。这些矩形中的任何一个都会强制光栅化器(将矢量图形转换为像素网格)处理Y坐标远远超出位图0-99范围的扫描线。

微软在GdiPlus.dll10.0.26100.4946版本中修复了EpScanBitmap::NextBuffer()函数中的此漏洞,通过添加检查来检测请求的扫描线数量是否超过位图高度。该函数现在自动修剪请求的扫描线以适应剩余行,防止任何越界访问。

未完成的事务 - CVE-2025-48984

我们在处理EMR_STARTDOC记录时发现了第五次崩溃,这立即显示出与CVE-2022-35837漏洞相关。在gdi32full.dll10.0.26100.3624版本中,StringLengthWorkerW()函数尝试读取288/0x120字节堆块末尾的内存时发生了访问违规异常。

堆栈跟踪表明问题可能在于StringLengthWorkerW()函数,该函数对用户控制的数据执行长度检查,并假定输入是以空字符结尾的字符串。但是,如果提供的字符串不是以空字符结尾,该函数可能会读取超出分配的缓冲区,导致潜在的信息泄露。

反编译的MRSTARTDOC::bPlay()函数源代码表明,CVE-2022-35837是通过将值分配给lpszDocNamelpszOutput字段(如果它们各自的字段非空)来处理的,这些值通过存储在v5变量中的计算偏移量进行分配。

越界读取的发生是因为MRSTARTDOC::bPlay()函数验证记录内的字符串偏移量。特制的元文件可能会填充lpszDocName字段中的第一个字符串,使其几乎到达记录的末尾。在复制该字符串后,代码将其内部游标推进到该点之外,但对lpszOutput字段的下一个验证仍然将提供的偏移量视为相对于记录的原始基址。

这种差异允许攻击者提供一个通过MR::bValidOff()函数检查的值,而实际上指向堆块外部。由于找不到空终止符,StringLengthWorkerW()函数继续读取相邻内存,暴露其内容。崩溃样本通过将EMF_HEADER.nBytes字段设置为仅0x120(288)字节来加剧问题,导致分配的缓冲区小于嵌入数据,并保证过度读取。

微软在gdi32full.dll10.0.26100.4652版本中修复了MRSTARTDOC::bPlay()处理函数中的此漏洞,通过更正偏移算法。修补后的函数现在在重新验证之前将指针转换回相对于记录开始的偏移量,并对lpszOutput字段应用相同的逻辑。因此,检查将与将被解引用的数据匹配。

结论

我们在Windows GDI中发现了可能对系统安全产生严重影响的漏洞。我们对EMF+文件的广泛调查表明,要保持领先于潜在威胁,需要持续的关注和适应。通过分享这些发现,我们希望提高认识,并为所有Windows用户提供有价值的见解和建议以增强安全性。

安全漏洞可能会持续多年未被发现,通常由于不完整的修复而重新出现。特定的信息泄露漏洞,尽管通过安全补丁正式解决,但由于原始问题仅得到部分修复,仍然活跃多年。这个例子强调了研究人员面临的一个基本难题:引入漏洞通常很容易,修复它可能很困难,而验证修复是否彻底有效则更具挑战性。

这些问题突显了为什么全面和持续的安全测试至关重要,必须不断更新和改进验证技术。通过供应商和安全研究人员之间的密切合作,包括与最初报告问题的研究人员共享计划修复,可以大大增强这项工作。这种协作方法增加了额外的审查层,有助于及早发现潜在差距,并加强软件生态系统的整体安全性。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计