CVE-2025-68146
Medium · 6.3CVSS
6.3
EPSS
0.2%
Published
Dec 16, 2025
CISA due date
—
Cloudanix analysis
A local user on a shared system may be able to trick vulnerable software into emptying files it should not touch.
- Exploitation
- Local Privilege
- Attacker needs
- Local User
- Complexity
- High
- Impact
- Data Tampering
- Component
- Library
- Class
- Race Condition
- Remediation
- Upgrade
- Effort
- Medium
- Patch risk
- Low
What it actually does
This is a local Time-of-Check-Time-of-Use race condition in Python filelock before 3.20.1. The vulnerable code checks whether a lock file exists and then opens the path with truncation semantics. During the gap between the existence check and the open operation, a local attacker who can write to the lock-file directory can replace the expected lock file path with a symbolic link to another file. When the trusted process opens the lock file, the operating system follows the symlink and truncates the target file. The main impacts are arbitrary file truncation or corruption in the context of the process using filelock, causing integrity loss and availability disruption. The vulnerability does not directly expose file contents or provide remote code execution, and exploitation requires local filesystem access plus the ability to create symlinks. It affects Unix, Linux, macOS, and Windows deployments using UnixFileLock or WindowsFileLock with predictable or attacker-writable lock paths.
Why it matters to the business
An attacker with local access to a shared system, build worker, developer workstation, CI runner, batch host, or multi-user application server could cause important files to be overwritten or emptied by abusing predictable lock-file paths. This can break applications, corrupt outputs, disrupt jobs, damage user data, or create cascading failures in dependent Python tools and libraries. The public exploitation likelihood is currently low based on EPSS, it is not listed in CISA KEV, and it is not known to be used in ransomware campaigns; however, the issue has been public for 236 days and is practical in environments where untrusted users or workloads share writable directories with trusted processes.
How an attack unfolds
An attacker first obtains local filesystem access on a system where a Python application or dependent library uses filelock before version 3.20.1. The attacker identifies or predicts the lock file path, often by observing application behavior, common temporary-directory naming patterns, command-line arguments, configuration files, or process activity. If the lock directory is writable by the attacker, they repeatedly race the trusted process: immediately after the application checks whether the lock file exists but before it opens the file with truncation enabled, the attacker creates a symlink at the lock-file path pointing to a victim file writable by the trusted process. The vulnerable os.open behavior follows the symlink and opens the target with O_TRUNC, causing the target file to be truncated. Because the attacker does not need to read the file or interact with the application UI, exploitation is primarily a timing and filesystem-control problem. The attack is most reliable when lock paths are predictable, when lock directories are shared or world-writable, and when the trusted application runs repeatedly or on a schedule.
How to fix it
Upgrade the Python filelock package to version 3.20.1 or later wherever it is used directly or pulled in transitively by dependent libraries. Rebuild and redeploy applications, containers, virtual environments, serverless packages, and CI images that vendor or pin vulnerable filelock versions. If immediate upgrade is not possible, reduce exposure by moving lock files into directories writable only by the trusted application user, using permissions such as chmod 0700 on Unix-like systems; preventing untrusted users or workloads from writing to lock directories; avoiding predictable lock paths in shared temporary locations; and monitoring lock directories for symlinks before launching trusted jobs. SoftFileLock can be used as a partial workaround instead of UnixFileLock or WindowsFileLock, but it has different locking semantics and may not be safe for all workloads. These mitigations reduce exploitability but do not fully remove the race condition. Where the supplied vendor package mapping is applicable, ensure Ubuntu packages meet or exceed the listed fixed versions, including linux 18.04 4.13.0-16.19, linux-aws 18.04 4.15.0-1001.1, linux-kvm 18.04 4.15.0-1002.2, linux-oracle 18.04 4.15.0-1007.9, linux-raspi2 18.04 4.13.0-1005.5, linux-snapdragon 18.04 4.4.0-1077.82, linux 20.04 5.4.0-9.12, linux-aws 20.04 5.4.0-1005.5, linux-aws-5.15 20.04 5.15.0-1014.18~20.04.1, linux-azure 20.04 5.4.0-1006.6, linux-azure-5.15 20.04 5.15.0-1007.8~20.04.1, linux-azure-fde-5.15 20.04 5.15.0-1019.24~20.04.1.1, linux-bluefield 20.04 5.4.0-1007.10, and linux-gcp 20.04 5.4.0-1005.5.
How to confirm the fix worked
Confirm that no deployed Python environment contains filelock earlier than 3.20.1 by checking application dependency manifests, lock files, virtual environments, container layers, and runtime package inventories. Useful commands include python -m pip show filelock, python -m pip freeze | grep -i '^filelock==', poetry show filelock, pipenv graph, and software composition analysis scans for transitive dependency use. Validate that applications no longer import vulnerable filelock implementations from older package paths. Inspect lock-file directories to confirm they are not writable by untrusted users and are not shared world-writable locations such as /tmp unless safe per-user subdirectories are used. On Unix-like systems, verify permissions with ls -ld and find lock directories containing symlinks with find <lock_dir> -type l -ls. On Windows, confirm Developer Mode and symlink creation privileges are restricted where appropriate and that application lock directories are ACL-restricted. If vendor OS packages are in scope, verify installed package versions meet the applicable fixed versions with package-manager inventory commands and confirm systems have been restarted if kernel or platform packages were updated.
Typical attack path
- Gain local user access to a host, container, CI runner, development workstation, or shared filesystem where vulnerable Python filelock usage exists.
- Identify applications or libraries using filelock versions earlier than 3.20.1, especially UnixFileLock or WindowsFileLock.
- Determine the lock-file directory and filename through process inspection, temporary-directory monitoring, application configuration, logs, or predictable naming conventions.
- Confirm the attacker can create files or symlinks in the lock-file directory.
- Select a target file that the trusted process has permission to truncate or corrupt.
- Race the trusted process by creating or swapping a symlink at the lock-file path between the existence check and the truncating open call.
- Cause the trusted process to follow the symlink and truncate the target file.
- Repeat attempts until the race is won, then leverage the resulting data loss, application failure, or operational disruption.
How to detect it
To determine whether this was already exploited, focus on filesystem and process activity around lock-file directories used by Python applications. Review auditd, Sysmon for Linux, endpoint detection telemetry, file integrity monitoring, shell history, CI job logs, application logs, and operating system event logs for suspicious symlink creation immediately before a trusted Python process opened a lock file with truncation behavior. On Unix-like systems, search for symlinks in application lock directories, especially symlinks whose names match expected .lock files and whose targets point outside the intended lock directory. Check for files that unexpectedly became zero bytes or were modified at the same timestamp as lock acquisition attempts. Correlate these events with local user sessions, container jobs, cron jobs, or CI tasks that had write access to the lock directory. Audit records of open/openat/openat2 with O_TRUNC against paths ending in .lock, combined with symlink creation or rename activity by another user in the same directory, are strong signals. On Windows, review Sysmon file creation and reparse point activity, Developer Mode or symlink privilege usage, and application process file-write events involving lock paths. Because successful exploitation may leave only normal-looking truncation and symlink artifacts, absence of a named malware indicator does not rule out abuse.
- filelock==3.20.0
- filelock==3.19
- filelock==3.18
- filelock==3.17
- filelock==3.16
- UnixFileLock
- WindowsFileLock
- O_TRUNC
- .lock ->
- type=SYMLINK
- syscall=openat
- syscall=open
- nametype=CREATE
- nametype=NORMAL
- os.open(
- /tmp/*.lock
- C:\\Windows\\Temp\\*.lock
- C:\\Users\\*\\AppData\\Local\\Temp\\*.lock
What your security team should do
- Prioritize upgrading filelock to 3.20.1 or later in all application builds, containers, CI images, and developer tooling, especially where workloads run on shared hosts or writable shared filesystems.
- Use SCA and dependency-lockfile review to identify transitive consumers of filelock, not only direct application dependencies.
- Harden lock-file directory permissions so untrusted users, containers, and jobs cannot create symlinks in directories used by trusted processes.
- Avoid placing lock files directly in shared temporary directories; prefer per-user or per-service private runtime directories.
- Add CI checks that fail builds when filelock versions earlier than 3.20.1 are introduced.
- For high-risk shared systems, enable filesystem auditing around lock directories to detect symlink creation and truncating opens.
- Review recent application failures, zero-byte files, corrupted outputs, and unexplained job interruptions for possible local tampering.
- Treat exploitation risk as lower than internet-facing vulnerabilities because local access is required, but do not defer remediation on multi-tenant systems, CI runners, or developer infrastructure.
The record
filelock is a platform-independent file lock for Python. In versions prior to 3.20.1, a Time-of-Check-Time-of-Use (TOCTOU) race condition allows local attackers to corrupt or truncate arbitrary user files through symlink attacks. The vulnerability exists in both Unix and Windows lock file creation where filelock checks if a file exists before opening it with O_TRUNC. An attacker can create a symlink pointing to a victim file in the time gap between the check and open, causing os.open() to follow the symlink and truncate the target file. All users of filelock on Unix, Linux, macOS, and Windows systems are impacted. The vulnerability cascades to dependent libraries. The attack requires local filesystem access and ability to create symlinks (standard user permissions on Unix; Developer Mode on Windows 10+). Exploitation succeeds within 1-3 attempts when lock file paths are predictable. The issue is fixed in version 3.20.1. If immediate upgrade is not possible, use SoftFileLock instead of UnixFileLock/WindowsFileLock (note: different locking semantics, may not be suitable for all use cases); ensure lock file directories have restrictive permissions (chmod 0700) to prevent untrusted users from creating symlinks; and/or monitor lock file directories for suspicious symlinks before running trusted applications. These workarounds provide only partial mitigation. The race condition remains exploitable. Upgrading to version 3.20.1 is strongly recommended.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|---|---|---|
| PyPI | filelock | 0 | 3.20.1 |
| Chainguard | airflow-2 | 0 | 2.11.0-r17 |
| Chainguard | airflow-2-bitnami-compat | 0 | 2.11.0-r17 |
| Chainguard | airflow-2-compat | 0 | 2.11.0-r17 |
| Chainguard | airflow-2-iamguarded-compat | 0 | 2.11.0-r17 |
| Chainguard | airflow-3 | 0 | 3.1.5-r0 |
| Chainguard | airflow-3-bitnami-compat | 0 | 3.1.5-r0 |
| Chainguard | airflow-3-compat | 0 | 3.1.5-r0 |
| Chainguard | airflow-3-iamguarded-compat | 0 | 3.1.5-r0 |
| Chainguard | awx | 0 | 24.6.1-r21 |
| Chainguard | datadog-agent-7.71 | 0 | 7.71.2-r6 |
| Chainguard | datadog-agent-7.71-fakeintake | 0 | 7.71.2-r6 |
| Chainguard | datadog-agent-7.71-jmx | 0 | 7.71.2-r6 |
| Chainguard | datadog-agent-7.71-oci-compat | 0 | 7.71.2-r6 |
| Chainguard | datadog-agent-7.71-s6-overlay | 0 | 7.71.2-r6 |
| Chainguard | datadog-agent-7.72 | 0 | 7.72.4-r5 |
| Chainguard | datadog-agent-7.72-fakeintake | 0 | 7.72.4-r5 |
| Chainguard | datadog-agent-7.72-jmx | 0 | 7.72.4-r5 |
| Chainguard | datadog-agent-7.72-oci-compat | 0 | 7.72.4-r5 |
| Chainguard | datadog-agent-7.72-s6-overlay | 0 | 7.72.4-r5 |
| Chainguard | datadog-agent-fips-7.71 | 0 | 7.71.2-r5 |
| Chainguard | datadog-agent-fips-7.71-fakeintake | 0 | 7.71.2-r5 |
| Chainguard | datadog-agent-fips-7.71-jmx | 0 | 7.71.2-r5 |
| Chainguard | datadog-agent-fips-7.71-oci-compat | 0 | 7.71.2-r5 |
| Chainguard | datadog-agent-fips-7.71-s6-overlay | 0 | 7.71.2-r5 |
| Chainguard | datadog-agent-fips-7.72 | 0 | 7.72.4-r3 |
| Chainguard | datadog-agent-fips-7.72-fakeintake | 0 | 7.72.4-r3 |
| Chainguard | datadog-agent-fips-7.72-jmx | 0 | 7.72.4-r3 |
| Chainguard | datadog-agent-fips-7.72-oci-compat | 0 | 7.72.4-r3 |
| Chainguard | datadog-agent-fips-7.72-s6-overlay | 0 | 7.72.4-r3 |
| Chainguard | datadog-cluster-agent-7.71 | 0 | 7.71.2-r6 |
| Chainguard | datadog-cluster-agent-7.71-oci-compat | 0 | 7.71.2-r6 |
| Chainguard | datadog-cluster-agent-7.72 | 0 | 7.72.4-r5 |
| Chainguard | datadog-cluster-agent-7.72-oci-compat | 0 | 7.72.4-r5 |
| Chainguard | datadog-cluster-agent-fips-7.71 | 0 | 7.71.2-r5 |
| Chainguard | datadog-cluster-agent-fips-7.71-oci-compat | 0 | 7.71.2-r5 |
| Chainguard | datadog-cluster-agent-fips-7.72 | 0 | 7.72.4-r3 |
| Chainguard | datadog-cluster-agent-fips-7.72-oci-compat | 0 | 7.72.4-r3 |
| Chainguard | dogstatsd-7.71 | 0 | 7.71.2-r6 |
| Chainguard | dogstatsd-7.72 | 0 | 7.72.4-r5 |
| Chainguard | kserve | 0 | 0.16.0-r6 |
| Chainguard | kserve-agent | 0 | 0.16.0-r6 |
| Chainguard | kserve-agent-compat | 0 | 0.16.0-r6 |
| Chainguard | kserve-manager | 0 | 0.16.0-r6 |
| Chainguard | kserve-manager-compat | 0 | 0.16.0-r6 |
| Chainguard | kserve-qpext | 0 | 0.16.0-r6 |
| Chainguard | kserve-qpext-compat | 0 | 0.16.0-r6 |
| Chainguard | kserve-router | 0 | 0.16.0-r6 |
| Chainguard | kserve-router-compat | 0 | 0.16.0-r6 |
| Chainguard | kserve-storage-controller | 0 | 0.16.0-r7 |
| Chainguard | mlflow | 0 | 3.8.0-r0 |
| Chainguard | mlflow-bitnami | 0 | 3.8.0-r0 |
| Chainguard | mlflow-iamguarded-compat | 0 | 3.8.0-r0 |
| Chainguard | open-webui | 0 | 0.6.41-r1 |
| Chainguard | open-webui-compat | 0 | 0.6.41-r1 |
| Chainguard | py3-cassandra-medusa | 0 | 0.26.0-r2 |
| Chainguard | py3-cassandra-medusa-compat | 0 | 0.26.0-r2 |
| Chainguard | py3.11-text-generation-inference | 0 | 3.3.7-r0 |
| Chainguard | py3.11-wheels-torchaudio-cuda-12.3 | 0 | 2.3.1-r4 |
| Chainguard | py3.9-wheels-torchvision-cuda-11.8 | 0 | 0.18.1-r7 |
| Chainguard | py3.9-wheels-torchvision-cuda-12.3 | 0 | 0.18.1-r5 |
| Chainguard | request-1276 | 0 | 0.26.0-r3 |
| Chainguard | request-1276-compat | 0 | 0.26.0-r3 |
| Chainguard | text-generation-inference | 0 | 3.3.7-r0 |
| Chainguard | text-generation-inference-compat | 0 | 3.3.7-r0 |
| Chainguard | tritonserver-backend-vllm-cuda-12.9 | 0 | 25.9.0_git20251112-r3 |
| Chainguard | tritonserver-backend-vllm-meta-cuda-12.9 | 0 | 25.9.0_git20251112-r3 |
| SUSE:Linux Enterprise Module for Python 3 15 SP7 | python-filelock | 0 | 3.12.2-150400.10.8.1 |
| Wolfi | airflow-3 | 0 | 3.1.5-r0 |
| Wolfi | airflow-3-bitnami-compat | 0 | 3.1.5-r0 |
| Wolfi | airflow-3-compat | 0 | 3.1.5-r0 |
| Wolfi | airflow-3-iamguarded-compat | 0 | 3.1.5-r0 |
| Wolfi | datadog-agent-7.72 | 0 | 7.72.4-r5 |
| Wolfi | datadog-agent-7.72-fakeintake | 0 | 7.72.4-r5 |
| Wolfi | datadog-agent-7.72-jmx | 0 | 7.72.4-r5 |
| Wolfi | datadog-agent-7.72-oci-compat | 0 | 7.72.4-r5 |
| Wolfi | datadog-agent-7.72-s6-overlay | 0 | 7.72.4-r5 |
| Wolfi | datadog-cluster-agent-7.72 | 0 | 7.72.4-r5 |
| Wolfi | datadog-cluster-agent-7.72-oci-compat | 0 | 7.72.4-r5 |
| Wolfi | dogstatsd-7.72 | 0 | 7.72.4-r5 |
| Wolfi | kserve | 0 | 0.16.0-r6 |
| Wolfi | kserve-agent | 0 | 0.16.0-r6 |
| Wolfi | kserve-agent-compat | 0 | 0.16.0-r6 |
| Wolfi | kserve-manager | 0 | 0.16.0-r6 |
| Wolfi | kserve-manager-compat | 0 | 0.16.0-r6 |
| Wolfi | kserve-qpext | 0 | 0.16.0-r6 |
| Wolfi | kserve-qpext-compat | 0 | 0.16.0-r6 |
| Wolfi | kserve-router | 0 | 0.16.0-r6 |
| Wolfi | kserve-router-compat | 0 | 0.16.0-r6 |
| Wolfi | kserve-storage-controller | 0 | 0.16.0-r7 |
| Wolfi | mlflow | 0 | 3.8.0-r0 |
| Wolfi | mlflow-bitnami | 0 | 3.8.0-r0 |
| Wolfi | mlflow-iamguarded-compat | 0 | 3.8.0-r0 |
| Wolfi | open-webui | 0 | 0.6.41-r1 |
| Wolfi | open-webui-compat | 0 | 0.6.41-r1 |
| Wolfi | py3-cassandra-medusa | 0 | 0.26.0-r2 |
| Wolfi | py3-cassandra-medusa-compat | 0 | 0.26.0-r2 |
| openSUSE:Leap 15.6 | python-filelock | 0 | 3.12.2-150400.10.8.1 |
| openSUSE:Leap 16.0 | python-filelock | 0 | 3.18.0-160000.3.1 |
| openSUSE:Tumbleweed | python-filelock | 0 | 3.20.2-1.1 |
| openSUSE:Tumbleweed | python-virtualenv | 0 | 20.36.1-1.1 |
Related vulnerabilities
- CVE-2026-22701 filelock is a platform-independent file lock for Python. Also affects filelock
- CVE-2026-4539 A security flaw has been discovered in pygments up to 2.19.2. Also affects airflow-2
- CVE-2026-30922 pyasn1 is a generic ASN.1 library for Python. Also affects airflow-2
- CVE-2026-27448 pyOpenSSL is a Python wrapper around the OpenSSL library. Also affects airflow-2
- CVE-2026-27459 pyOpenSSL is a Python wrapper around the OpenSSL library. Also affects airflow-2
- CVE-2026-32597 PyJWT is a JSON Web Token implementation in Python. Also affects airflow-2
- CVE-2026-31958 Tornado is a Python web framework and asynchronous networking library. Also affects airflow-2
- CVE-2026-25604 In AWS Auth manager, the origin of the SAML authentication has been used as provided by… Also affects airflow-2