Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2025-59042

CVE-2025-59042: PyInstaller RCE Vulnerability

CVE-2025-59042 is a remote code execution vulnerability in PyInstaller versions before 6.0.0 that allows attackers to execute arbitrary Python code. This article covers technical details, affected versions, and mitigation.

Published:

CVE-2025-59042 Overview

CVE-2025-59042 is a local code injection vulnerability in PyInstaller versions before 6.0.0. The flaw resides in the bootstrap process of PyInstaller-frozen applications, where a special entry is appended to sys.path and the bootstrap script attempts to load an optional bytecode-decryption module while that entry is still active. An unprivileged local attacker who can create files or directories next to a frozen executable can inject a malicious Python module that the bootloader loads at startup. When the target binary carries the setuid bit, this results in local privilege escalation.

Critical Impact

Arbitrary Python code execution in the context of a PyInstaller-frozen binary, escalating to root when the executable is setuid, without any modification of the executable itself.

Affected Products

  • PyInstaller versions prior to 6.0.0 (both onedir and onefile modes)
  • Frozen applications built without the optional bytecode encryption feature enabled
  • Non-Windows platforms whose filesystems allow ? characters in file or directory names

Discovery Timeline

  • 2025-09-09 - CVE-2025-59042 published to the National Vulnerability Database (NVD)
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2025-59042

Vulnerability Analysis

PyInstaller bundles a Python application together with its dependencies into a single executable. During startup, the bootloader communicates the offset of the embedded PYZ archive to the bootstrap script by appending a specially formatted entry to sys.path. The bootstrap script then attempts to import an optional module used for bytecode decryption. Because the crafted sys.path entry is still active during that import, Python's import machinery searches next to the executable for a module matching the expected name. This mismatch between transport metadata and the module search path introduces a code injection primitive tracked as [CWE-94].

Root Cause

The root cause is the reuse of sys.path to transmit archive-location information while simultaneously performing module imports. When the optional bytecode-encryption feature is disabled, no built-in shadow module exists to satisfy the import, so any attacker-controlled module with the matching name is loaded instead.

Attack Vector

Exploitation requires the attacker to create a directory or ZIP archive next to the executable whose name matches the format the bootloader uses to encode the PYZ offset. The name contains a ? character, restricting the attack to filesystems that permit that character. The attacker must also determine the PYZ archive offset within the executable. Placing a Python module with the bytecode-encryption module's expected name inside that directory causes the bootstrap script to execute the attacker's code. If the target binary has the setuid bit set, the injected module executes with elevated privileges.

python
# Security patch in PyInstaller/archive/pyz_crypto.py
# Removes the --key/cipher bytecode encryption feature that
# caused the bootstrap script to import an optional module
# while a controllable sys.path entry was still active.

-import os
-
-from PyInstaller import log as logging
-
-BLOCK_SIZE = 16
-logger = logging.getLogger(__name__)
-
 class PyiBlockCipher:
-    """
-    This class is used only to encrypt Python modules.
-    """
     def __init__(self, key=None):
-        logger.log(
-            logging.DEPRECATION,
-            "Bytecode encryption will be removed in PyInstaller v6. "
-            "Please remove cipher and block_cipher parameters ..."
-        )
-        assert type(key) is str
-        if len(key) > BLOCK_SIZE:
-            self.key = key[0:BLOCK_SIZE]
-        else:
-            self.key = key.zfill(BLOCK_SIZE)
-        assert len(self.key) == BLOCK_SIZE
-
-        import tinyaes

Source: PyInstaller commit f5adf29

Detection Methods for CVE-2025-59042

Indicators of Compromise

  • Unexpected directories or ZIP files containing a ? character in their names located next to PyInstaller-built executables.
  • Presence of Python modules inside those directories whose names match PyInstaller's optional bytecode-decryption module.
  • setuid binaries built with PyInstaller residing in directories writable by non-privileged users.

Detection Strategies

  • Enumerate all PyInstaller-frozen executables on Linux and macOS hosts and inspect their parent directories for anomalous entries.
  • Audit filesystem write permissions on directories containing security-sensitive frozen binaries, especially those with the setuid bit set.
  • Monitor process execution telemetry for setuid PyInstaller binaries loading Python modules from unexpected paths.

Monitoring Recommendations

  • Alert on file or directory creation events where the name contains ? and the parent directory holds an executable ELF or Mach-O binary.
  • Track child processes spawned by setuid PyInstaller binaries and flag unusual command execution.
  • Integrate build-time inventory of PyInstaller versions into asset management to identify hosts still running versions before 6.0.0.

How to Mitigate CVE-2025-59042

Immediate Actions Required

  • Rebuild affected applications with PyInstaller 6.10.0 or later, which reworks the bootstrap process to stop using sys.path for PYZ location transport.
  • Remove the setuid bit from any PyInstaller-built binaries where elevated execution is not strictly necessary.
  • Restrict write permissions on directories containing security-sensitive frozen executables so only trusted accounts can create files there.

Patch Information

PyInstaller 6.0.0 removed support for bytecode encryption in commit f5adf291c8b832d5aff7632844f7e3ddf7ad4923, eliminating the vulnerable import path. PyInstaller 6.10.0 (commit cfd60b510f95f92cb81fc42735c399bb781a4739) further reworked the bootstrap so sys.path is no longer used to transmit the PYZ archive location. See the PyInstaller Security Advisory GHSA-p2xp-xx3r-mffc for full details.

Workarounds

  • Enforce strict directory permissions (for example, 0755 owned by root) on directories containing setuid PyInstaller executables so unprivileged users cannot create adjacent files.
  • Relocate frozen setuid binaries into directories such as /usr/local/sbin that are writable only by root.
  • Where feasible, replace setuid deployment with sudoers rules or capabilities to reduce the attack surface.
bash
# Configuration example: lock down the directory containing a
# setuid PyInstaller binary so only root can create files in it.
sudo chown root:root /opt/myapp
sudo chmod 0755 /opt/myapp
sudo chown root:root /opt/myapp/myapp
sudo chmod 4755 /opt/myapp/myapp

# Verify no world- or group-writable entries exist next to the binary
find /opt/myapp -maxdepth 1 -perm /022 -ls

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.