# Root Cause Analysis — CVE-2025-50165

## Summary

| Field | Value |
|---|---|
| **CVE** | CVE-2025-50165 |
| **Binary** | windowscodecs.dll (Windows Imaging Component) |
| **Component** | libjpeg-turbo JPEG compression path |
| **Bug Class** | Use of Uninitialized Pointer / Untrusted Pointer Dereference (CWE-824) |
| **Impact** | Remote Code Execution |
| **CVSS 3.1** | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| **Exploited ITW** | No |
| **Patch** | August 2025 Patch Tuesday (version 10.0.26100.4946) |

## Vulnerability Overview

A critical remote code execution vulnerability exists in windowscodecs.dll due to
uninitialized function pointers in the JPEG compression path. The Windows Imaging
Component (WIC) library embeds libjpeg-turbo 3.0.2 for JPEG handling. When a JPEG
image with 12-bit or 16-bit data precision is re-encoded (compressed), the function
`jpeg_finish_compress` dereferences a function pointer (`compress_data_12` or
`compress_data_16`) that was never initialized by the coefficient controller setup
function `jinit_c_rawtranscode_coef_controller_turbo`.

The uninitialized pointer points to heap memory filled with allocator debug patterns
(e.g., 0xBAADF00D from HeapAlloc). An attacker who controls the heap layout via
heap spraying can redirect this pointer to attacker-controlled memory, achieving
arbitrary code execution.

## Root Cause — Uninitialized Function Pointers

### The jpeg_compress_struct

The `jpeg_compress_struct` contains a `pub` member (the coefficient controller) with
function pointers for different precision levels:

```c
struct jpeg_c_coef_controller {
    // ...
    int (*compress_data)(j_compress_ptr cinfo, JSAMPIMAGE input_buf);     // 8-bit
    int (*compress_data_12)(j_compress_ptr cinfo, J12SAMPIMAGE input_buf); // 12-bit
    int (*compress_data_16)(j_compress_ptr cinfo, J16SAMPIMAGE input_buf); // 16-bit
    // ...
};
```

### The Bug

In the vulnerable version, `jinit_c_rawtranscode_coef_controller_turbo` (called
from `rawtransencode_master_selection`) only initializes `compress_data` (the 8-bit
pointer). The 12-bit and 16-bit pointers are left uninitialized:

```c
// VULNERABLE (pre-patch, inlined in rawtransencode_master_selection)
void jinit_c_rawtranscode_coef_controller_turbo(j_compress_ptr cinfo) {
    coef = alloc_struct(...);
    coef->pub.compress_data = rawtranscode_compress_output;  // 8-bit: OK
    // compress_data_12 = ???  NOT INITIALIZED
    // compress_data_16 = ???  NOT INITIALIZED
}
```

### The Crash

When `jpeg_finish_compress` processes a 12-bit JPEG:

```c
// jpeg_finish_compress (from jcapimin.c)
if (cinfo->data_precision == 12) {
    // Calls compress_data_12 which is UNINITIALIZED
    while (!(*cinfo->coef->pub.compress_data_12)(cinfo, NULL)) {
        // ...
    }
}
```

The crash occurs at `jpeg_finish_compress+0xCC`:
```
call qword ptr [r8+10h]    ; r8+10h = compress_data_12
; r8+10h -> 0xBAADF00DBAADF00D (uninitialized heap)
```

### Call Flow

```
Application (e.g., Microsoft Photos thumbnail generation)
  -> IWICBitmapFrameEncode::WriteSource
    -> CFrameEncodeBase::WriteSource
      -> CJpegTurboFrameEncode::HrWriteSource
        -> jpeg_finish_compress
          -> (*cinfo->coef->pub.compress_data_12)(cinfo, NULL)
            -> DEREFERENCE OF UNINITIALIZED POINTER -> CRASH/RCE
```

## Triggering Conditions

The vulnerability is triggered when ALL of the following conditions are met:

1. **Input**: A JPEG image with `data_precision` set to 12 or 16 (not the standard 8)
2. **Operation**: The image must be *re-encoded* (compressed), not just decoded/rendered
3. **Library**: The application uses a vulnerable version of windowscodecs.dll

Re-encoding happens during:
- Save/Save-As operations in image editors
- Thumbnail generation (e.g., Windows Explorer, Microsoft Photos)
- Image format conversion
- Document embedding (e.g., Office re-encodes embedded images)

Simply *opening* and *viewing* a crafted JPEG does NOT trigger the vulnerability.

## Patch Analysis

The patch separates `jinit_c_rawtranscode_coef_controller_turbo` from being inlined
and adds initialization for both missing pointers:

```c
// PATCHED (version 10.0.26100.4946)
void jinit_c_rawtranscode_coef_controller_turbo(j_compress_ptr cinfo) {
    coef = alloc_struct(...);
    coef->pub.compress_data    = rawtranscode_compress_output;     // 8-bit
    coef->pub.compress_data_12 = rawtranscode_compress_output_16;  // 12-bit: FIXED
    coef->pub.compress_data_16 = rawtranscode_compress_output_16;  // 16-bit: FIXED
}
```

`rawtranscode_compress_output_16` is a stub that calls `rawtranscode_compress_output`,
indicating there is no precision-specific handling needed.

The upstream libjpeg-turbo fix (commit e0e18de, version 3.1.1) additionally:
- Zero-initializes all structures
- Adds NULL checks before pointer dereference
- Fixes similar issues in the decompression path

## Exploitation Path

### 32-bit (Lower barrier)

CFG is disabled for 32-bit windowscodecs.dll by default:

1. Craft a 12-bit or 16-bit precision JPEG
2. Heap spray: allocate many 0x3EF7-byte chunks containing ROP chain data
3. Free some chunks to create holes for the victim allocation
4. Trigger re-encoding of the crafted JPEG
5. Uninitialized pointer lands on attacker-controlled heap data
6. RIP control via the dereferenced function pointer
7. Stack pivot to ROP chain in heap
8. VirtualAlloc RWX region, copy shellcode, jump to it

### 64-bit (Higher barrier)

CFG is enabled, requiring:
- An additional CFG bypass gadget
- Address leak to defeat ASLR
- More complex heap layout control

### Prerequisites for Exploitation

- Address leak (ASLR bypass)
- Sufficient heap control in the target application
- Target application must re-encode the JPEG (not just render it)

## Reachability

- **Attack vector**: Network — malicious JPEG can be delivered via email, web, or
  embedded in Office documents
- **Privileges required**: None
- **User interaction**: None (thumbnail generation can trigger automatically)
- **Attack surface**: Any application using windowscodecs.dll that re-encodes JPEGs:
  Microsoft Photos, Paint, Office (document thumbnails), Windows Explorer (thumbnails),
  third-party applications using WIC

## Affected Versions

| Product | Vulnerable | Patched |
|---|---|---|
| Windows Server 2025 | 10.0.26100.4851 | 10.0.26100.4946 |
| Windows 11 24H2 (x64) | 10.0.26100.4851 | 10.0.26100.4946 |
| Windows 11 24H2 (ARM64) | 10.0.26100.4851 | 10.0.26100.4946 |

## Detection

### YARA — Crafted 12-bit/16-bit JPEG

```yara
rule CVE_2025_50165_Crafted_JPEG
{
    meta:
        description = "Detects JPEG files with non-standard 12-bit or 16-bit precision"
        cve         = "CVE-2025-50165"
        author      = "OnlyFm252"

    strings:
        // SOI marker (Start of Image)
        $soi = { FF D8 }

        // SOF marker (Start of Frame) with 12-bit precision
        // FF C0/C1/C2/C3 = SOF0-3, next 2 bytes = length, then precision byte = 0x0C
        $sof_12bit_0 = { FF C0 ?? ?? 0C }
        $sof_12bit_1 = { FF C1 ?? ?? 0C }
        $sof_12bit_2 = { FF C2 ?? ?? 0C }
        $sof_12bit_3 = { FF C3 ?? ?? 0C }

        // SOF marker with 16-bit precision (0x10)
        $sof_16bit_0 = { FF C0 ?? ?? 10 }
        $sof_16bit_1 = { FF C1 ?? ?? 10 }
        $sof_16bit_2 = { FF C2 ?? ?? 10 }
        $sof_16bit_3 = { FF C3 ?? ?? 10 }

    condition:
        $soi at 0 and
        (
            any of ($sof_12bit*) or
            any of ($sof_16bit*)
        )
}
```

### Sigma — Suspicious JPEG Processing Crash

```yaml
title: CVE-2025-50165 WindowsCodecs.dll Crash on JPEG Processing
id: d6e7f8a9-b0c1-2345-def0-678901abcdef
status: experimental
description: >
    Detects crashes in windowscodecs.dll during JPEG processing, which may
    indicate exploitation of CVE-2025-50165 via crafted 12/16-bit JPEG.
author: OnlyFm252
date: 2026/07/26
references:
    - https://www.zscaler.com/blogs/security-research/cve-2025-50165-critical-flaw-windows-graphics-component
    - https://www.welivesecurity.com/en/eset-research/revisiting-cve-2025-50165-critical-flaw-windows-imaging-component/
    - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-50165
logsource:
    product: windows
    service: application
detection:
    selection_crash:
        EventID: 1000
        Application|endswith:
            - '\Photos.exe'
            - '\mspaint.exe'
            - '\explorer.exe'
            - '\WINWORD.EXE'
            - '\EXCEL.EXE'
            - '\POWERPNT.EXE'
        FaultModule|contains: 'WindowsCodecs'
    condition: selection_crash
level: high
tags:
    - attack.execution
    - attack.t1203
    - cve.2025.50165
```

### Sysmon Configuration

```xml
<Sysmon schemaversion="4.90">
  <EventFiltering>
    <!-- Event 11: Detect unusual JPEG file creation in temp/download dirs -->
    <FileCreate onmatch="include">
      <TargetFilename condition="end with">.jpg</TargetFilename>
      <TargetFilename condition="end with">.jpeg</TargetFilename>
    </FileCreate>

    <!-- Event 7: Detect windowscodecs.dll loading in unusual processes -->
    <ImageLoad onmatch="include">
      <ImageLoaded condition="end with">\WindowsCodecs.dll</ImageLoaded>
    </ImageLoad>
  </EventFiltering>
</Sysmon>
```

## References

- [Zscaler ThreatLabz — CVE-2025-50165: Critical Flaw in Windows Graphics Component](https://www.zscaler.com/blogs/security-research/cve-2025-50165-critical-flaw-windows-graphics-component)
- [ESET Research — Revisiting CVE-2025-50165: A Critical Flaw in Windows Imaging Component](https://www.welivesecurity.com/en/eset-research/revisiting-cve-2025-50165-critical-flaw-windows-imaging-component/)
- [MSRC Advisory — CVE-2025-50165](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-50165)
- [libjpeg-turbo fix commit e0e18de](https://github.com/libjpeg-turbo/libjpeg-turbo/commit/e0e18dea5433e600ea92d60814f13efa40a0d7dd)
- [Microsoft WIC JPEG Re-encode Example](https://learn.microsoft.com/en-us/windows/win32/wic/-wic-codec-jpegmetadataencoding)
