How It Works
This section references the methodology used by ThreatLabz to discover and analyze CVE-2025-50165. We touch on how ThreatLabz identified the vulnerable code path, the process of triaging the crash, and the development of a Proof-of-Concept (PoC) exploit.
Identify the vulnerable path for fuzzing
In the figure below, line 51 served as the entry point for fuzzing. At this line, the original FileSize value can be replaced with MutatedBufferSize. This modification controls the buffer contents returned by MapViewOfFile and defines the snapshotâs entry point for subsequent fuzzing operations.
 exploit.
Identify the vulnerable path for fuzzing
In the figure below, line 51 served as the entry point for fuzzing. At this line, the original FileSize value can be replaced with MutatedBufferSize. This modification controls the buffer contents returned by MapViewOfFile and defines the snapshotâs entry point for subsequent fuzzing operations.

Figure 1: IDA decompilation of the function GpReadOnlyMemoryStream::InitFile.
Crash analysis
The fuzzing process successfully identified a crash, as shown in the figure below.

Figure 2: Crash analysis of Microsoft Visio captured in WinDbg.
The WinDbg output pinpointed the crashing instruction as call qword ptr [r8+10h] ds:0000080667c170=c0c0c0c0c0c0c0c0. This showed r8+10h was being dereferenced but pointed to uninitialized memory, a state identifiable by the c0c0c0c0c0c0c0c0 pattern used by Gflags. Further examination of the r8 registerâs memory dump, shown in the figure below, revealed this uninitialized memory could be user-controlled through heap spraying.

Figure 3: Memory dump of r8 register in WinDbg.
The address 0000015fc1cab170 contained the untrusted function pointer. The pointer was dereferenced at the windowscodecs!jpeg_finish_compress+0xcc instruction, directly leading to the crash. To understand the execution flow that led to this point, ThreatLabz conducted a stack trace analysis, as illustrated below.
STACK_TEXT:
RetAddr Call Site
00007ffe`a8da58bb WindowsCodecs!jpeg_finish_compress+0xcc
00007ffe`a8d4b6cd WindowsCodecs!CJpegTurboFrameEncode::HrWriteSource+0x46b
00007ff7`16d6218b WindowsCodecs!CFrameEncodeBase::WriteSource+0x18d
The WinDbg stack trace highlighted three prominent functions within windowsCodecs.dll:
jpeg_finish_compressCJpegTurboFrameEncode::HrWriteSourceCFrameEncodeBase::WriteSource
Further investigation into the stack trace and additional research revealed the vulnerabilityâs precise origin and the corresponding code snippet. By modifying the path to the crafted JPEG file, the exact location of the vulnerabilityâs trigger was pinpointed to the execution of piFrameEncode->WriteSource, as shown in the example below.
[ REDACTED ]
// Create the decoder.
if (SUCCEEDED(hr))
{
hr = piFactory->CreateDecoderFromFilename(L"/path/to/poc.jpg", NULL, GENERIC_READ,
WICDecodeMetadataCacheOnDemand, // For JPEG lossless decoding/encoding.
&piDecoder);
}
[ REDACTED ]
if (SUCCEEDED(hr))
{
hr = piFrameEncode->WriteSource(
static_cast (piFrameDecode),
NULL); // Using NULL enables JPEG lossless encoding.
}
Exploit
ANALYST NOTE: Control Flow Guard (CFG) is disabled for the 32-bit version of *windowscodecs.dll* by default. However, the 64-bit version requires a CFG bypass to successfully exploit the vulnerability.
By leveraging heap spraying and exploiting the untrusted pointer dereference vulnerability, control of the instruction pointer (IP) can be obtained which enables further exploitation through Return-Oriented Programming (ROP). This is achieved with the following steps:
- Allocate a series of heap chunks, each sized 0x3ef7, with the ROP chain data stored within them.
- Free some of these heap chunks and return them to the free list so that one is reallocated as the victim chunk.
- Trigger the untrusted pointer dereference vulnerability.
- Use RIP control to employ a stack pivot gadget and redirect execution to the ROP chain stored within the heap.
The figure below shows the exploitation process and the resulting crash output.

Figure 4: Illustration of the exploitation steps and the resulting crash output captured in WinDbg.
With control of the IP established through the exploitation process, the next phase involves leveraging ROP gadgets to achieve arbitrary code execution. These gadgets are used to create a Read-Write-Execute (RWX) memory region using a function like VirtualAlloc. Once the RWX memory is set up, additional ROP gadgets such as mov dword [rax], rcx, where RAX points to the shellcode address and RCX contains the shellcode data, are used to write the malicious shellcode into this newly created memory region. Finally, execution is redirected to the shellcode by jumping to the address of the RWX memory.
Proof-of-Concept (PoC)
To demonstrate the exploitation of the vulnerability, ThreatLabz created an example application which allows the user to control the heap and process the JPEG image using the three functions.
- Enables the creation of heap allocations with user-specified inputs:
- Index: Specifies the location in a global array where the allocated heap address will be stored (e.g., index 0, 1, ...).
- Size: Determines the size of the heap memory block to allocate.
- Data: Represents the value stored within the allocated memory block.
- Frees a heap allocation based on the provided index value.
- Accepts Base64-encoded JPEG image data and uses the JPEG re-encode example code to process the image.
- Terminates the application.
In the figure below, the memory address 0X00007FF7F0BB448, displayed as Reward, represents the virtual memory address of the applicationâs main function. This address allows users to calculate the base address where the applicationâs process is mapped in memory, providing critical information for exploitation. Using options 1 and 2, users can perform a heap spray attack by densely allocating and freeing heap memory chunks to control the layout of data in memory. Option 3 then triggers the vulnerability by processing a specially crafted JPEG image, leveraging the heap spray setup to manipulate the applicationâs control flow and gain access to memory required for exploitation.

Figure 5: Core functionality of the executable used to demonstrate the PoC.
Check out this video for a detailed demonstration of RIP control in the example application, showing how RCE is achieved during the exploitation process.
Explore more Zscaler blogs

ThreatLabz Discovers 117 Vulnerabilities in Microsoft 365 Apps Via the SketchUp 3D Library - Part 1

ThreatLabz Discovers 117 Vulnerabilities in Microsoft 365 Apps Via the SketchUp 3D Library - Part 2

CVE-2025-53770: Zero-Day Exploit Impacts Microsoft SharePoint Services