CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2025-71176
6.80.01%

CVE-2025-71176: Local Privilege Escalation and Information Disclosure via TOCTOU Race Condition in pytest

Amit Schendel
Amit Schendel
Senior Security Researcher

Mar 25, 2026·7 min read·7 visits

PoC Available

Executive Summary (TL;DR)

pytest <= 9.0.2 utilizes a predictable temporary directory structure (/tmp/pytest-of-{user}) that is vulnerable to local symlink attacks. Local attackers can pre-create this directory to steal test data, alter file permissions, or cause a Denial of Service.

The pytest testing framework through version 9.0.2 on UNIX-like systems creates base temporary directories using a predictable naming pattern. This predictable pattern allows a local attacker to execute a symlink race or Time-of-Check Time-of-Use (TOCTOU) attack, potentially resulting in Denial of Service (DoS), information disclosure, or local privilege escalation.

Vulnerability Overview

The pytest testing framework, widely used in Python environments, utilizes a base temporary directory to store artifacts generated during test execution. In versions up to and including 9.0.2 on UNIX-like operating systems, this directory is created using a predictable naming convention. Specifically, the framework constructs the directory path as /tmp/pytest-of-{user}.

This predictable path generation introduces a significant security vulnerability when the temporary directory is shared among multiple users, such as the standard /tmp directory. A local attacker can anticipate the exact path that pytest will attempt to create or access for a given target user. This predictability forms the foundational requirement for local symlink attacks and race conditions.

The vulnerability is formally classified under CWE-379 (Creation of Temporary File in Directory with Insecure Permissions) and CWE-367 (Time-of-Check Time-of-Use Race Condition). Successful exploitation permits a local, unauthenticated attacker to redirect file write operations performed by pytest. The primary impacts include Denial of Service (DoS), information disclosure of sensitive test data, and potential local privilege escalation.

Root Cause Analysis

The root cause resides within the src/_pytest/tmpdir.py module, which is responsible for managing the test execution environment's temporary storage. When pytest initializes, it determines the base temporary directory by resolving the system's default temporary path and appending the pytest-of-{user} string. The framework subsequently attempts to create this directory using the mkdir function with the exist_ok=True parameter.

The inclusion of exist_ok=True instructs the file system abstraction to silently proceed if the target path already exists. This design choice prevents pytest from crashing upon subsequent runs, but it fails to validate the nature of the pre-existing filesystem object. Consequently, the framework makes no initial distinction between a legitimate directory created during a previous test run and a malicious symlink placed by an attacker.

Furthermore, the mechanism relies on a deferred ownership validation check implemented as rootdir.stat().st_uid != uid. This check evaluates the ownership of the directory after it has already been targeted for initialization. This introduces a Time-of-Check Time-of-Use (TOCTOU) race condition window, during which an attacker can manipulate the filesystem structure before the validation mechanism enforces security constraints.

Code Analysis

Analysis of the vulnerable implementation in src/_pytest/tmpdir.py demonstrates the flawed initialization sequence. The code constructs the target path and invokes directory creation without ensuring atomic operations. The conceptual vulnerable pattern is outlined below.

temproot = Path(from_env or tempfile.gettempdir()).resolve()
user = get_user() or "unknown"
rootdir = temproot.joinpath(f"pytest-of-{user}")
try:
    rootdir.mkdir(mode=0o700, exist_ok=True)
except OSError:
    rootdir = temproot.joinpath("pytest-of-unknown")
    rootdir.mkdir(mode=0o700, exist_ok=True)

The exist_ok=True parameter within the rootdir.mkdir call is the critical failure point. If an attacker pre-creates /tmp/pytest-of-target as a symlink to /etc/sensitive_file, the mkdir operation does not fail. Instead, it resolves the symlink and attempts to apply the mode=0o700 permissions to the target destination.

The patched implementation, introduced in pull request #14279, abandons the predictable naming scheme entirely. The remediation leverages the Python standard library's tempfile.mkdtemp() function. This function guarantees atomic directory creation with randomized naming and secure default permissions, completely eliminating the possibility of predictable path prediction and symlink pre-creation.

Exploitation

Exploitation of CVE-2025-71176 requires local access to the system where the target user executes the pytest framework. The attacker must possess write permissions to the shared temporary directory, which is the default configuration for /tmp on standard UNIX-like operating systems. The attack leverages basic filesystem utilities to construct the trap.

The attacker initiates the exploit by creating a symlink pointing to a target destination they wish to affect. The symlink is named using the predictable pytest format for the victim's username. The following bash commands demonstrate the conceptual proof-of-concept for this attack phase.

# As attacker 'mallory'
ln -s /home/mallory/fake_pytest /tmp/pytest-of-alice
 
# As victim 'alice'
pytest
# Pytest will now write test data into /home/mallory/fake_pytest/

Once the symlink is established, the attacker waits for the victim to invoke the pytest command. Upon execution, pytest resolves the attacker's symlink and begins writing test artifacts, such as the pytest-0 and pytest-1 subdirectories, into the specified destination.

If the attacker directs the symlink to a directory they own, they gain read access to any sensitive data written by the testing suite. This may include environment variables, database credentials, or proprietary source code artifacts. Alternatively, directing the symlink to a system-critical location can corrupt application configurations or alter file permissions via pytest's initialization routines.

Impact Assessment

The security impact of CVE-2025-71176 is primarily constrained to the local execution environment, reflected by its CVSS base score of 6.8. The vulnerability does not expose services to remote, unauthenticated network attackers. However, in shared computing environments, the consequences of successful exploitation are substantial.

Information disclosure is the most probable impact scenario. Continuous Integration and Continuous Deployment (CI/CD) pipelines frequently utilize shared runners to execute test suites. An attacker with access to the runner environment can extract sensitive secrets, API keys, and deployment credentials injected into the pytest environment during the testing phase.

Denial of Service (DoS) attacks can be executed with high reliability. An attacker can create a rigid symlink structure that causes pytest to crash or hang indefinitely upon initialization. This can be weaponized to disrupt automated build pipelines, delaying critical software releases and consuming CI/CD computing resources.

Local privilege escalation is technically feasible but subject to strict environmental prerequisites. The attacker must target a file or directory where pytest's subsequent permission modifications (e.g., applying 0o700 modes) grant the attacker elevated access. Modern UNIX-like operating systems mitigate this risk through kernel-level protections such as fs.protected_symlinks, which restrict the effectiveness of symlink traversal by unprivileged users.

Remediation

The primary remediation strategy for CVE-2025-71176 requires updating the pytest framework to a version beyond the 9.0.2 release. The maintainers have integrated a robust fix in pull request #14279, which transitions the directory creation logic to utilize tempfile.mkdtemp(). This update securely randomizes the base temporary directory nomenclature and ensures atomic file operations.

Security engineers should audit local development environments and CI/CD pipeline configurations to identify vulnerable pytest versions. Dependency management tools, including Pip, Poetry, and Pipenv, must be updated to explicitly require the patched versions. Automated vulnerability scanners should be deployed to detect residual instances of pytest <= 9.0.2 across the infrastructure.

For environments where immediate upgrading is not viable, several effective workarounds exist. Development teams can explicitly define a secure, user-controlled temporary directory using the --basetemp command-line argument. By passing a dynamically generated path, such as --basetemp=$(mktemp -d), the predictable /tmp pathing is bypassed entirely.

Administrators must ensure that OS-level symlink protections are active on all shared environments. Verifying that the sysctl parameters fs.protected_symlinks and fs.protected_hardlinks are set to 1 provides a critical defense-in-depth layer. These kernel configurations prevent unprivileged users from following symlinks to files they do not own, significantly reducing the exploitation viability of this specific bug class.

Official Patches

pytest-devPull request introducing randomized directory names to fix the vulnerability

Technical Appendix

CVSS Score
6.8/ 10
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:L
EPSS Probability
0.01%
Top 100% most exploited

Affected Systems

UNIX-like operating systems utilizing the default /tmp directoryShared Continuous Integration / Continuous Deployment (CI/CD) runner environmentsMulti-user Linux development servers running Python test suites

Affected Versions Detail

Product
Affected Versions
Fixed Version
pytest
pytest-dev
<= 9.0.2> 9.0.2
AttributeDetail
CWE IDCWE-379
Attack VectorLocal
CVSS Score6.8
EPSS Score0.00006
ImpactInformation Disclosure, Denial of Service, Privilege Escalation
Exploit StatusProof-of-Concept Available

MITRE ATT&CK Mapping

T1222File and Directory Permissions Modification
Defense Evasion
T1539Steal Web Session Cookie
Credential Access
T1037Boot or Logon Initialization Scripts
Persistence
CWE-379
Creation of Temporary File in Directory with Insecure Permissions

The product creates a temporary file or directory in a location where the permissions are insecure, allowing a local attacker to manipulate it before it is fully initialized.

Known Exploits & Detection

GitHub Issue #13669Conceptual Proof of Concept demonstrating local symlink hijacking in /tmp

Vulnerability Timeline

Vulnerability reported to pytest maintainers via GitHub Issue #13669
2025-08-24
Public disclosure on the Openwall oss-security mailing list
2026-01-21
GitHub Advisory GHSA-6w46-j5rx-g56g published
2026-01-21
CVE-2025-71176 assigned and published in NVD
2026-01-22
Fix PR #14279 synchronized in the main repository
2026-03-12

References & Sources

  • [1]GitHub Issue: Vulnerability report
  • [2]Fix Pull Request
  • [3]Openwall Disclosure
  • [4]Red Hat Advisory
  • [5]GitHub Advisory
  • [6]NVD Detail

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.