Drawn to Danger: Windows Graphics Vulnerabilities Lead to Remote Code Execution and Memory Exposure

Wait 5 sec.

BackgroundCheck 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:CVE-2025-30388, rated important and considered more likely to be exploited;CVE-2025-53766, classified as critical severity and may allow remote attackers to execute arbitrary code on affected systems;CVE-2025-47984, also rated important and can result in the unauthorized disclosure of sensitive information over the network.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.Geometry Gone Rogue – CVE-2025-30388We 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.EmfPlusClear clear = { .Type = 0x4009, .Flags = 0x0102, .Size = 0x0000003c, .DataSize = 0x00000030, .Color = 0xaabbccff // Value written to memory};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.0:000> g(16b8.5ec): Access violation - code c0000005 (first/second chance not available)First chance exceptions are reported before any exception handling.This exception may be expected and handled.eax=ffccbbaa ebx=00000567 ecx=022cec8c edx=08732374 esi=000015e3 edi=db000000eip=74e16d9d esp=0075ddb0 ebp=0075ddc0 iopl=0 nv up ei ng nz na po nccs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000282gdiplus!ScanOperation::AlphaMultiply_sRGB+0x2d:74e16d9d 890411 mov dword ptr [ecx+edx],eax ds:002b:0aa01000=????????0:000> kb # ChildEBP RetAddr Args to Child 00 0075ddc0 74e16653 00000950 0872bd0c 0075e99c gdiplus!ScanOperation::AlphaMultiply_sRGB+0x2d01 0075ddf4 74e16520 00000000 00000000 00000015 gdiplus!EpScanBitmap::NextBuffer+0xc302 0075de2c 74e15c91 00000000 00000000 00000015 gdiplus!DpOutputSolidColorSpan::OutputSpan+0x4003 0075de58 74e188d4 00000000 ffffff19 00000955 gdiplus!DpClipRegion::OutputSpan+0x14104 0075de7c 74e15988 00000000 0f47eed0 74e01b40 gdiplus!EpAliasedFiller::FillEdgesWinding+0x5405 0075e938 74e0420e 00000001 00000003 00000000 gdiplus!RasterizePath+0x5d806 0075ea74 74e01cee 08747da0 0873cf98 0075eab8 gdiplus!DpDriver::StrokePath+0x3ee07 0075ea9c 74e01db7 0075eab8 0075eb20 0075ede0 gdiplus!GpGraphics::DrvStrokePath+0x3808 0075eadc 74df3b06 0075eb10 0075eb20 0075edcc gdiplus!GpGraphics::RenderDrawPath+0xbc09 0075ed68 74e50a58 0075edcc 0075ed98 00000002 gdiplus!GpGraphics::DrawLines+0x1480a 0075ee54 74f034c2 0aa08ff0 0f2a2fb8 00000000 gdiplus!FullTextImager::GdipLscbkDrawUnderline+0x2280b 0075eee4 74ef7d57 0075f048 00000004 00000000 gdiplus!DrawUnderlineMerge+0x16f0c 0075ef90 74eef9fd 00000001 00000000 00001015 gdiplus!DisplaySublineCore+0x25b0d 0075f018 74ed907c 0aa40fa0 0aa40fa0 0aa40fa0 gdiplus!LsDisplayLine+0x1930e 0075f02c 74edc08e 0075f048 00000000 0aa04e70 gdiplus!BuiltLine::Draw+0x120f 0075f058 74edbfd4 0aa40fa0 00000000 0aa04e70 gdiplus!FullTextImager::RenderLine+0x5010 0075f0d8 74ed915f 0aa04e70 74ed9090 00000000 gdiplus!FullTextImager::Render+0x21111 0075f108 74e6192a 0873ed28 0075f150 08744cb0 gdiplus!FullTextImager::Draw+0xcf12 0075f3c8 74e8c55f 05b70184 00000014 0874bfe0 gdiplus!GpGraphics::DrawString+0x2f41213 0075f404 74e02081 08744cb0 0000401c 0000df00 gdiplus!DrawStringEPR::Play+0xdf // Affects EmfPlusDrawString record14 0075f428 74e01f97 0000401c 0000df00 00000044 gdiplus!GdipPlayMetafileRecordCallback+0x7115 0075f45c 74e01e7c 00000104 05b700a8 0865ad58 gdiplus!MetafilePlayer::EnumerateEmfPlusRecords+0x9716 0075f47c 75ca5d2f a901095a 0866cff8 05b70098 gdiplus!EnumEmfWithDownLevel+0x8c17 0075f510 75ca4309 74e01df0 08744cb0 0075f58c gdi32full!bInternalPlayEMF+0x83018 0075f524 771cad7f a901095a 824606f7 74e01df0 gdi32full!EnumEnhMetaFile+0x3919 0075f544 74df5ed6 a901095a 824606f7 74e01df0 GDI32!EnumEnhMetaFileStub+0x2f1a 0075f5a0 74df5b3b a901095a 824606f7 0075f5e4 gdiplus!MetafilePlayer::EnumerateEmfRecords+0xdf1b 0075f648 74df81bc 08744cb0 824606f7 0075f774 gdiplus!GpGraphics::EnumEmfPlusDual+0x351 // Affects EMF+ Dual1c 0075f7b8 74e1384c 0075f810 0075f810 00000002 gdiplus!GpMetafile::EnumerateForPlayback+0x8191d 0075f8d8 74e01903 08723f28 0075f908 0075f918 gdiplus!GpGraphics::DrawImage+0x5ec1e 0075f944 74e8a69e 08723f28 0075f96c 0075f97c gdiplus!GpGraphics::DrawImage+0x611f 0075f9a4 74e8b8a6 00000064 00000064 08723f28 gdiplus!GpMetafile::GetBitmap+0x1d220 0075f9b8 74e6e16b 00000064 00000064 00000000 gdiplus!GpMetafile::GetThumbnail+0x2621 0075f9e0 006511a0 08723f28 00000064 00000064 gdiplus!GdipGetImageThumbnail+0x6b // Reachable via thumbnails0:000> dd [ecx+edx]-0xfa0-10 L80aa00050 00000000 00000000 046e0b3c dcbabbbb0aa00060 ffccbbaa ffccbbaa ffccbbaa ffccbbaa 0:000> dd [ecx+edx]-10 L80aa00ff0 ffccbbaa ffccbbaa ffccbbaa ffccbbaa 0aa01000 ???????? ???????? ???????? ????????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.EmfPlusSetTSClip clip = { .Type = 0x403a, .Flags = 0x0003, // Number of RECT objects .Size = 0x00000048, .DataSize = 0x0000003c, .NumRects[3] = { { 0x00000005, 0x00000000, 0x04000000, 0x0000001f }, // Valid { 0x00000000, 0x00e90000, 0xfff70000, 0x00000000 }, // Not valid { 0x00000000, 0x00000000, 0x00000015, 0x003f8000 } // Valid }};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.Figure 4. New functions in GdiPlus.dll to validate RECT objects.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.Negative Space – CVE-2025-53766We 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.EmfPlusDrawRects rects = { .Type = 0x400B, .Flags = 0xEF00, .Size = 0x00000048, .DataSize = 0x0000003C, .Count = 0x00000003, .RectData[Count] = { { 0x0000, 0x3400, 0x3434, 0x3434 }, { 0x3434, 0x3434, 0x3434, 0x3434 }, { 0x3434, 0x3434, 0x3434, 0x3434 }, { 0x3434, 0x3434, 0x0000, 0x3000 }, { 0x3434, 0x3434, 0x3434, 0x3434 }, { 0x3434, 0x3434, 0x0034, 0x0010 }, { 0x4000, 0x3434, 0x3434, 0x3434 }, }};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.0:000> g(abc.7148): Access violation - code c0000005 (first/second chance not available)First chance exceptions are reported before any exception handling.This exception may be expected and handled.eax=ffff004c ebx=000000ff ecx=fffcf63c edx=086cb9c4 esi=6af57480 edi=0868bd58eip=6af574b3 esp=004feb34 ebp=004feb48 iopl=0 nv up ei pl zr na pe nccs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246gdiplus!ScanOperation::AlphaDivide_sRGB+0x33:6af574b3 890411 mov dword ptr [ecx+edx],eax ds:002b:0869b000=100000110:000> kb# ChildEBP RetAddr Args to Child 00 004feb48 6af573bd 00000035 0868bd0c 004fef98 gdiplus!ScanOperation::AlphaDivide_sRGB+0x3301 004feb7c 6af5b364 00000064 00000063 00000064 gdiplus!EpScanBitmap::NextBuffer+0xdd02 004febbc 6af5ab8e 004fef98 004fee40 00000001 gdiplus!OnePixelLineDDAAliased::DrawXMajor+0x5403 004fecd4 6af5aabf 004fef98 00000000 00000000 gdiplus!DrawSolidLineOnePixelAliased+0x9e04 004fed18 6af58bb8 004fefac 004fee20 00000005 gdiplus!DrawSolidStrokeOnePixel+0x9f05 004fef48 6af3fe26 00000000 00000000 6af5aa20 gdiplus!FixedPointPathEnumerate+0x35806 004feff8 6af405ac 086a7da0 0869cf98 004ff178 gdiplus!DpDriver::SolidStrokePathOnePixel+0x12407 004ff134 6af43107 086a7da0 0869cf98 004ff178 gdiplus!DpDriver::StrokePath+0x33c08 004ff15c 6af43096 004ff178 004ff1e8 086b1f8c gdiplus!GpGraphics::DrvStrokePath+0x3b09 004ff19c 6af429c7 004ff1c4 004ff1e8 086b1f78 gdiplus!GpGraphics::RenderDrawPath+0xbc0a 004ff39c 6afd3746 086b1f78 07bb0160 00000003 gdiplus!GpGraphics::DrawRects+0x3560b 004ff3c4 6af40b59 086a4cb0 0000400b 00000400 gdiplus!DrawRectsEPR::Play+0x860c 004ff3e8 6af40a67 0000400b 00000400 0000003c gdiplus!GdipPlayMetafileRecordCallback+0x790d 004ff41c 6af4094c 00000104 07bb00a8 08006d58 gdiplus!MetafilePlayer::EnumerateEmfPlusRecords+0x970e 004ff43c 75eab76f 1c01154a 08d06ff8 07bb0098 gdiplus!EnumEmfWithDownLevel+0x8c0f 004ff4d0 75ea9d49 6af408c0 086a4cb0 004ff54c gdi32full!bInternalPlayEMF+0x83010 004ff4e4 75f2b9bf 1c01154a a94624e7 6af408c0 gdi32full!EnumEnhMetaFile+0x3911 004ff504 6af368f6 1c01154a a94624e7 6af408c0 GDI32!EnumEnhMetaFileStub+0x2f12 004ff560 6af36551 1c01154a a94624e7 004ff5a4 gdiplus!MetafilePlayer::EnumerateEmfRecords+0xdf13 004ff608 6af38bdc 086a4cb0 a94624e7 004ff734 gdiplus!GpGraphics::EnumEmfPlusDual+0x35114 004ff778 6af5454c 004ff7d0 004ff7d0 00000002 gdiplus!GpMetafile::EnumerateForPlayback+0x81915 004ff898 6af467ac 08683f28 004ff8c8 004ff8d8 gdiplus!GpGraphics::DrawImage+0x5ec16 004ff904 6afd198e 08683f28 004ff92c 004ff93c gdiplus!GpGraphics::DrawImage+0x6117 004ff964 6afd2b96 00000064 00000064 08683f28 gdiplus!GpMetafile::GetBitmap+0x1d218 004ff978 6afb540b 00000064 00000064 00000000 gdiplus!GpMetafile::GetThumbnail+0x2619 004ff9a0 009a11a0 08683f28 00000064 00000064 gdiplus!GdipGetImageThumbnail+0x6bListing 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 about 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.Unfinished Business – CVE-2025-48984We 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.0:000> g(48b0.7b68): Access violation - code c0000005 (first chance)First chance exceptions are reported before any exception handling.This exception may be expected and handled.eax=00dbf028 ebx=08195f7c ecx=08196000 edx=7fffffbc esi=7ffffffe edi=00000000eip=7792d586 esp=00dbf014 ebp=00dbf01c iopl=0 nv up ei pl nz na po nccs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202gdi32full!StringLengthWorkerW+0xf:7792d586 663939 cmp word ptr [ecx],di ds:002b:08196000=????0:000> kb # ChildEBP RetAddr Args to Child 00 00d3f37c 7796f55c 00d3f388 00000000 00d3f3cc gdi32full!StringLengthWorkerW+0xf01 00d3f38c 77974155 00d3f3b0 0885bf4c 779740b0 gdi32full!StringCbLengthW+0x1a02 00d3f3cc 77966ffb 9e011903 0885dff8 00000001 gdi32full!MRSTARTDOC::bPlay+0xa503 00d3f420 6d971f4d 9e011903 0885dff8 0885bf4c gdi32full!PlayEnhMetaFileRecord+0x5b04 00d3f438 6d971e21 08ab5720 6d971c40 0000006b gdiplus!EmfEnumState::PlayRecord+0x2d05 00d3f450 6d980b8c 0000006b 00000024 0885bf54 gdiplus!EmfEnumState::ProcessRecord+0x1e106 00d3f470 6d9b939d 0000006b 00000000 00000024 gdiplus!GdipPlayMetafileRecordCallback+0xec07 00d3f49c 7796824f 9e011903 0885dff8 0885bf4c gdiplus!EnumEmfDownLevel+0x7d08 00d3f530 77966829 6d9b9320 08ab2cb0 00d3f5ac gdi32full!bInternalPlayEMF+0x83009 00d3f544 7632ad7f 9e011903 65461856 6d9b9320 gdi32full!EnumEnhMetaFile+0x390a 00d3f564 6d9768c6 9e011903 65461856 6d9b9320 GDI32!EnumEnhMetaFileStub+0x2f0b 00d3f5c0 6d9741ac 9e011903 65461856 00d3f664 gdiplus!MetafilePlayer::EnumerateEmfRecords+0xdf0c 00d3f678 6d9789eb 08ab2cb0 65461856 00d3f7b4 gdiplus!GpGraphics::EnumEmf+0x4130d 00d3f7f8 6d99452c 00d3f850 00d3f850 00000002 gdiplus!GpMetafile::EnumerateForPlayback+0x6580e 00d3f918 6d98676c 08a93f28 00d3f948 00d3f958 gdiplus!GpGraphics::DrawImage+0x5ec0f 00d3f984 6da108fe 08a93f28 00d3f9ac 00d3f9bc gdiplus!GpGraphics::DrawImage+0x6110 00d3f9e4 6da11b06 00000064 00000064 08a93f28 gdiplus!GpMetafile::GetBitmap+0x1d211 00d3f9f8 6d9f439b 00000064 00000064 00000000 gdiplus!GpMetafile::GetThumbnail+0x2612 00d3fa20 00db11aa 08a93f28 00000064 00000064 gdiplus!GdipGetImageThumbnail+0x6bListing 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.EMR_STARTDOC startDoc = { .Type = 0x0000006b, .Size = 0x0000002c, .docInfo = { .cbSize = 0x00000020, .lpszDocName = 0x2b464d45, // -> 0x1002 .lpszOutput = 0x00004001, // -> 0x6b14 .lpszDatatype = 0x0000001c, .fwType = 0x00000010 }};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:0000h 6B 00 00 00 2C 00 00 00 20 00 00 00 45 4D 46 2B k...,... ...EMF+ 0010h 01 40 00 00 1C 00 00 00 10 00 00 00 02 10 C0 DB .@............ÀÛ 0020h 01 00 00 05 05 7F FF 00 60 00 00 00 46 00 00 00 .....ÿ.`...F... 0030h 14 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 6B 5C 6B .kkkkkkkkkkkkk\k 0040h 6B 6B 6B 6B 6B 6B 6B 6B 6B 10 6B 6B 6B 6B 6B 6B kkkkkkkkk.kkkkkk 0050h 6B 6B 6B 6B kkkkListing 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 next 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.ConclusionWe 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 post Drawn to Danger: Windows Graphics Vulnerabilities Lead to Remote Code Execution and Memory Exposure appeared first on Check Point Research.