Drawn to Danger: Windows Graphics Vulnerabilities Lead to Remote Code Execution and ...
Check Point Research (CPR) identified three security vulnerabilities in the Graphics Device Interface ( GDI ) in Windows. We promptly reported these issues to Microsoft, and they were addressed in the Patch Tuesday updates in May, July, and August 2025.
These are the vulnerabilities:
Vulnerability disclosures such as these highlight the need for proactive measures to mitigate potential risks. Our purpose in publishing this blog after security fixes were implemented is to further raise awareness of these vulnerabilities and provide Windows users with defensive insights and mitigation recommendations. In the following sections, we detail the findings of our fuzzing campaign , which targeted Windows GDI using the EMF format and led to the discovery of these security vulnerabilities.
We found three separate crashes related to the processing of EmfPlusDrawString , EmfPlusFillRects and EmfPlusFillClosedCurve records. All three cases have a common root cause: another record sets the stage for exploitation. However, the outcome varies depending on which additional records are processed during the execution. Our current analysis focuses on the crash involving the EmfPlusDrawString record.
Multiple access violation exceptions occurred in the ScanOperation::AlphaMultiply_sRGB() , ScanOperation::Blend_sRGB_sRGB_MMX() and EpAntialiasedFiller::OutputSpan() functions within version 10.0.26100.3037 of the GdiPlus.dll module. These exceptions were triggered when the system attempted to read or write memory at the end of a 4000/0xFA0 bytes heap block, or while attempting to access reserved but unallocated memory.
This vulnerability could potentially allow a remote attacker to perform out-of-bounds read or write memory operations using a specially crafted EMF+ metafile. Figure 1 shows the decompiled source code of the ScanOperation::AlphaMultiply_sRGB() function at the time of the crash.
Figure 1. Decompiled source code of the affected ScanOperation::AlphaMultiply_sRGB() function.
In our crash sample, which serves as a proof of concept (PoC) for reproducing a vulnerability, an EmfPlusClear record is located before the EmfPlusDrawString record within the metafile. This record clears the output coordinate space and initializes it with a background color and transparency, as defined by its Color field. The field contains an EmfPlusARGB object specifying red, green, blue, and alpha components. This detail is significant because it allows an attacker to control the value written to memory during exploitation. Listing 1 shows the affected EmfPlusClear record.
Listing 1. Sample EmfPlusClear record showing the value written to memory.
Further investigation revealed that the EmfPlusClear record handler uses the EpScanBitmap::Start() function to allocate a heap block to store 4000 bytes (0xFA0). This buffer is then populated with the specified EmfPlusARGB object, which undergoes alpha multiplication by the AlphaMultiply_sRGB() function during the processing of the EmfPlusDrawString record.
Note that the loop counter stored in the ebx register begins at 0x950. As each iteration writes a 4-byte object into the target buffer at ecx + edx , the function writes out-of-bounds after 1000 bytes, when the counter reaches 0x567. This behavior can be observed in Listing 2 which shows an excerpt from the crash analysis.
Listing 2. Stack trace showing an access violation in the ScanOperation::AlphaMultiply_sRGB() function.
Alpha values are multiplied by color values to ensure that transparent areas (where the alpha value is 0) do not contribute any color (R = G = B = 0). As a result, colors in semi-transparent areas appear darker because these objects emit less light compared to opaque ones.
The fourth byte is the alpha value, which can range from 0x00 to 0xFF. Setting the alpha value to 0xFF allows an attacker near-complete control over what is written to the buffer, as the RGB values remain unchanged. In the example above, B=0xAA , G=0xBB , R=0xCC and A=0xFF were written into the target buffer.
Figure 2. Binary differences showing the new ValidateAndSet() and IsRectValid() functions.
As illustrated by Figure 2 , patch analysis confirmed that the crashes which occurred during the processing of various metafile records all shared a common root cause: the presence of invalid RECT objects within an EmfPlusSetTSClip record that preceded the other records in which the crashes were observed. Listing 3 shows the affected record from our crash samples.
Listing 3. Sample EmfPlusSetTSClip record containing invalid RECT objects.
Processing invalid RECT objects results in a heap-based buffer overflow that may allow an attacker to perform out-of-bounds memory operations. An attacker could exploit this vulnerability by using various other metafile records to write or read memory, as a corrupted EmfPlusSetTSClip record sets the stage for exploitation. Looking at the rendering of the sample EmfPlusFillRects record in Figure 3 indicates that it is possible to disclose uninitialized or initialized heap bytes via the filled rectangle.
Figure 3. Leaking heap memory in Word via the sample EmfPlusFillRects record.
As we can see from the resulting image in Microsoft Word 365, the output varies in each rendering, which demonstrates that this crash sample leaks memory and eventually Word is unexpectedly terminated. This behavior already crosses a security boundary as it can lead to information disclosure if an attacker can read back the rendered image, for example, using JavaScript in a web browser.
Microsoft fixed this vulnerability within the SetTSClipEPR::Play() handler function in version 10.0.26100.4061 of GdiPlus.dll by introducing the ValidateAndSet() and IsRectValid() functions to validate RECT objects, as shown in Figure 4 .
This bug was addressed with KB5058411 in the May 2025 Patch Tuesday as a remote code execution vulnerability of important severity, tracked as CVE-2025-30388 . Notably, this issue also affects Microsoft Office for Mac and Android. In addition, MSRC’s exploitability assessment is “ Exploitation More Likely” , indicating that this vulnerability presents a high-value target for attackers.
We identified a fourth crash while processing an EmfPlusDrawRects record. An access violation exception occurred in the ScanOperation::AlphaDivide_sRGB() function within version 10.0.26100.4202 of GdiPlus.dll , as it attempted to write to reserved but unallocated memory.
This vulnerability could allow a remote attacker to perform an out-of-bounds memory write using a specially crafted EMF+ metafile. Figure 5 shows the decompiled source code of the affected ScanOperation::AlphaDivide_sRGB() function at the time of the crash.
Figure 5. Decompiled source code of the ScanOperation::AlphaDivide_sRGB() function.
This issue is similar to CVE-2025-30388 , the vulnerability we previously discussed. That bug was caused by invalid RECT objects within an EmfPlusSetTSClip record that preceded other records in which the crash occurred. In this new case, the vulnerability stems from a series of EmfPlusRect objects within the affected EmfPlusDrawRects record, detailed in Listing 4 .
Listing 4. Sample EmfPlusDrawRects structure containing malformed EmfPlusRect objects.
The EmfPlusDrawRects record is preceded by an EmfPlusObject record that specifies an EmfPlusPen object used in graphics operations. The EmfPlusPen object defines a graphics brush associated with the pen. The brush is a solid-color brush, characterized by an EmfPlusARGB value. The source of the write operation in the eax register can be controlled by the attacker through this value, as shown by the excerpt of the crash analysis in Listing 5 .
Listing 5. Stack trace showing an access violation in the ScanOperation::AlphaDivide_sRGB() function.
In a bitmap image, a scan-line is one horizontal row of pixels. Processing an image line by line means handling one scan-line at a time, from left to right and top to bottom. Further analysis showed that the EpScanBitmap::NextBuffer() function never verified that the number of scan-lines it was to process fit in the destination bitmap, meaning that the function could be tricked into reading or writing past the bottom edge of an image if a call requested more scan-lines than existed.
Assuming that the bitmap allocated for thumbnail generation is 100×100 (0x64 × 0x64) pixels, the rectangle data in the PoC metafile deliberately pushes the scan position past the bottom edge of the bitmap and triggers the out-of-bounds write. Any one of those rectangles forces the rasterizer (which converts vector graphics into a pixel grid) to process scan-lines whose Y coordinate is well beyond the 0-99 range of the bitmap.
Microsoft fixed this vulnerability within the EpScanBitmap::NextBuffer() function in version 10.0.26100.4946 of GdiPlus.dll , as shown in Figure 6 , by adding a check to detect when the requested number of scan-lines exceeds the height of the bitmap. The function now automatically trims the requested scan-lines to fit within the remaining rows, preventing any out-of-bounds access, as shown in Figure 7 .
Figure 7. Decompiled source code of the patched GEpScanBitmap::NextBuffer() function.
This vulnerability was addressed with KB5063878 in the August 2025 Patch Tuesday as a critical severity remote code execution vulnerability tracked as CVE-2025-53766 . Notably, this vulnerability requires no privileges or user interaction and can be exploited remotely over a network, making it a high-risk threat to web services that parse specially crafted metafiles.
We identified a fifth crash while processing an EMR_STARTDOC record, which immediately appeared to be related to the CVE-2022-35837 vulnerability. An access violation exception happened in the StringLengthWorkerW() function within version 10.0.26100.3624 of gdi32full.dll while attempting to read memory at the end of a 288/0x120 bytes heap block. Listing 6 contains the relevant excerpt of the crash analysis.
Listing 6. Stack trace showing an access violation in the StringLengthWorkerW() function.
The stack trace suggests that the issue may lie with the StringLengthWorkerW() function, which performs a length check on user-controlled data and assumes the input is a null-terminated string. However, if the provided string is not null-terminated, the function may read beyond the allocated buffer, leading to potential information disclosure.
The decompiled source code of the MRSTARTDOC::bPlay() function shown in Figure 8 demonstrates that CVE-2022-35837 was addressed by assigning values to the lpszDocName and lpszOutput fields through a calculated offset stored in the v5 variable, if their respective fields are non-null.
Figure 8. Decompiled source code of the MRSTARTDOC::bPlay() function.
Listing 7 shows the affected EMR_STARTDOC metafile record. The DOCINFO structure contains the input and output file names and other information used by the StartDoc() function, which starts a print job.
Listing 7. Sample EMR_STARTDOC record with a DOCINFO structure.
The raw EMR_STARTDOC record shown in Listing 8 demonstrates that after patching the originally reported arbitrary information disclosure , the lpszDocName field points to 0x1002 , located immediately after the record at offset 1Ch . Meanwhile, the lpszOutput field points to 0x6b14 , positioned at offset 1Ch + 14h = 30h :
Listing 8. Raw EMR_STARTDOC record showing the calculated offsets.
The out-of-bounds read occurs because the MRSTARTDOC::bPlay() function validates string offsets inside the record. A specially crafted metafile may pad the first string in the lpszDocName field so that it nearly reaches the end of the record. After copying that string, the code advances its internal cursor beyond that point, but the validation for the lpszOutput field still treats the supplied offset as if it were relative to the original base of the record.
This discrepancy allows an attacker to provide a value that passes the MR::bValidOff() function while actually pointing outside the heap block. Because no null terminator is found, the StringLengthWorkerW() function continues reading into adjacent memory, exposing its contents. The crash sample intensifies the issue by setting the EMF_HEADER.nBytes field to just 0x120 (288) bytes, causing the allocated buffer to be smaller than the embedded data and guaranteeing an over-read.
Microsoft fixed this vulnerability within the MRSTARTDOC::bPlay() handler function in version 10.0.26100.4652 of gdi32full.dll by correcting the offset arithmetic. The patched function now converts the pointer back to an offset relative to the start of the record before revalidating it and applies the same logic to the lpszOutput field. Therefore, the check matches the data that will be dereferenced, as shown in Figure 9 .
Figure 9. Decompiled source code of the patched MRSTARTDOC::bPlay() function.
This bug was addressed with KB5062553 in the July 2025 Patch Tuesday as an important severity information disclosure vulnerability tracked as CVE-2025-47984 . MSRC classified it as CWE-693: Protection Mechanism Failure , which in this case really means that the security fix to address CVE-2022-35837 was incomplete.
We discovered vulnerabilities in Windows GDI that could have serious implications for system security. Our extensive investigation into EMF+ files shows that staying ahead of potential threats requires continuous diligence and adaptation. By sharing these findings, we hope to raise awareness and provide valuable insights and recommendations to enhance security for all Windows users.
Security vulnerabilities can persist undetected for years, often resurfacing due to incomplete fixes. A particular information disclosure vulnerability, despite being formally addressed with a security patch, remained active for years due to the original issue receiving only a partial fix. This example underscores a basic conundrum for researchers: introducing a vulnerability is often easy, fixing it can be difficult, and verifying that a fix is both thorough and effective is even more challenging.
These issues highlight why comprehensive and continuous security testing, using verification techniques that must be constantly updated and improved, is crucial. This effort can be greatly enhanced by close collaboration between vendors and security researchers, including sharing planned fixes with the researchers who initially reported the issue. Such a collaborative approach adds an extra layer of review, helping to catch potential gaps early and strengthens the overall security of the software ecosystem.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
