Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-55798

CVE-2026-55798: Python Pillow RCE Vulnerability

CVE-2026-55798 is a remote code execution vulnerability in Python Pillow caused by improper shell command construction in WindowsViewer. This post covers technical details, affected versions, and mitigation steps.

Published:

CVE-2026-55798 Overview

CVE-2026-55798 is a command injection vulnerability [CWE-78] in Python Pillow, a widely used Python imaging library. The flaw exists in the WindowsViewer.get_command() function within src/PIL/ImageShow.py. Prior to version 12.3.0, the function constructed a cmd.exe shell command by embedding a file path directly into an f-string without escaping. The resulting command was passed to subprocess.Popen(..., shell=True), allowing shell metacharacters in file paths to inject arbitrary cmd.exe commands. An attacker who controls the file path passed to Image.show() on Windows can execute arbitrary commands in the context of the calling process.

Critical Impact

Attacker-controlled file paths can trigger arbitrary Windows shell command execution when displayed through Pillow's WindowsViewer.

Affected Products

  • Python Pillow versions prior to 12.3.0
  • Applications on Windows using PIL.ImageShow.WindowsViewer or Image.show()
  • Python workflows that pass untrusted file paths to Pillow's display functions

Discovery Timeline

  • 2026-07-06 - CVE-2026-55798 published to NVD
  • 2026-07-07 - Last updated in NVD database

Technical Details for CVE-2026-55798

Vulnerability Analysis

The vulnerability resides in PIL/ImageShow.py, specifically in the WindowsViewer.get_command() method. The method builds a Windows shell command using an f-string of the form f'start "Pillow" /WAIT "{file}" ...' and returns it for execution via subprocess.Popen(..., shell=True) (or os.system). Because the file argument is interpolated without sanitization, characters such as &, |, %, and quote characters are interpreted by cmd.exe rather than treated as part of a filename. An attacker who can influence the path passed to Image.show() can append additional shell commands that execute with the privileges of the Python process. Local user interaction is required to trigger the show operation, and Pillow's Windows-only display path limits the affected surface to Windows hosts.

Root Cause

The root cause is unsafe string interpolation of untrusted input into a shell command string, classified as [CWE-78] OS Command Injection. Pillow relied on shell=True execution while performing no escaping of metacharacters and no validation of quoting characters in the input path.

Attack Vector

Exploitation requires a local attacker to influence a filename that a victim opens with Pillow's Image.show() on Windows. Delivery vectors include archives extracted with attacker-controlled names, files dropped in monitored folders, or crafted paths supplied by higher-level applications that call Pillow. When WindowsViewer handles the path, embedded shell metacharacters break out of the intended start command and execute additional operations under cmd.exe.

python
# Patch: src/PIL/ImageShow.py - Raise ValueError on double quote in WindowsViewer file
    options = {"compress_level": 1, "save_all": True}

    def get_command(self, file: str, **options: Any) -> str:
+       if '"' in file:
+           msg = "Windows filenames cannot contain double quotes"
+           raise ValueError(msg)
        return (
            f'start "Pillow" /WAIT "{file}" '
            "&& ping -n 4 127.0.0.1 >NUL "

# Patch: src/PIL/ImageShow.py - Prevent variable expansion in WindowsViewer get_command
        if '"' in file:
            msg = "Windows filenames cannot contain double quotes"
            raise ValueError(msg)
+       file = file.replace("%", '"%"')
        return (
            f'start "Pillow" /WAIT "{file}" '
            "&& ping -n 4 127.0.0.1 >NUL "

Source: Pillow Commit b0e06ca and Pillow Commit 8819416. These patches reject double quotes in filenames and neutralize % variable expansion before the string reaches cmd.exe.

Detection Methods for CVE-2026-55798

Indicators of Compromise

  • Windows image files or paths containing shell metacharacters such as &, |, %, or embedded quote characters
  • python.exe or a packaged Python application spawning cmd.exe child processes with unexpected command lines
  • Files opened via Image.show() where the on-disk filename includes &&, ||, or %VAR% sequences

Detection Strategies

  • Inventory installed Python packages and flag hosts running Pillow versions earlier than 12.3.0
  • Alert on cmd.exe processes spawned by Python interpreters with command lines referencing start "Pillow" followed by additional shell operators
  • Perform static analysis of internal code for calls to Image.show() or ImageShow.WindowsViewer where the path originates from untrusted input

Monitoring Recommendations

  • Monitor endpoint process trees for python.execmd.exe → arbitrary binary chains on Windows workstations
  • Track file creation events for image files with metacharacters in their names in shared folders and download directories
  • Log Pillow version data through software bill of materials tooling and correlate with Image.show() usage in internal telemetry

How to Mitigate CVE-2026-55798

Immediate Actions Required

  • Upgrade Pillow to version 12.3.0 or later on all Windows systems
  • Audit internal applications for calls to Image.show() or WindowsViewer that accept externally influenced file paths
  • Validate that filenames passed to Pillow do not contain shell metacharacters, quotes, or % sequences

Patch Information

The issue is fixed in Pillow 12.3.0. Reference the Pillow Release Notes 12.3.0 and GitHub Security Advisory GHSA-4x4j-2g7c-83w6 for details. The fix commits switch the display path away from shell=True execution and reject dangerous characters in filenames, as seen in commit 8404ea5.

Workarounds

  • Avoid calling Image.show() on Windows with file paths sourced from untrusted input until patched
  • Rename or sanitize filenames to strip &, |, %, and quote characters before passing them to Pillow
  • Replace Image.show() usage in production code with explicit viewer invocations using subprocess.Popen with an argument list and shell=False
bash
# Upgrade Pillow to the fixed release
python -m pip install --upgrade "Pillow>=12.3.0"

# Verify installed version
python -c "import PIL; print(PIL.__version__)"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.