Vulnerability DatabaseCVE-2026-112938

CVE-2026-112938: 
Python vulnerability analysis and mitigation

Overview

CVE-2026-112938 is a race condition enabling symbolic link following in the Python filelock library's StrictSoftFileLock and AsyncStrictSoftFileLock classes. A local attacker with write access to a shared directory can exploit a TOCTOU (time-of-check to time-of-use) flaw to redirect lock claim file creation into an attacker-chosen directory. The vulnerability affects filelock versions >= 3.30.0 and <= 4.2.0 (pip package, vendor: tox-dev), and was reported on October 15, 2026, with a fix published October 17, 2026. It carries a CVSS v3.1 base score of 5.8 (Medium) (GitHub Advisory, Security Advisory).

Technical details

The root cause is CWE-363 (Race Condition Enabling Link Following): StrictSoftFileLock._acquire checks <path>.filelock with a single lstat call, then subsequently accesses <path>.filelock/claims by pathname for all subsequent operations (mkdir, scan, publish, link, unlink). While _open_directory passes O_NOFOLLOW, this flag only protects the final claims path component — the kernel will still follow a symbolic link in any earlier component (i.e., .filelock itself). An attacker exploits this window by atomically swapping .filelock for a symlink pointing to a target directory using renameat2(RENAME_EXCHANGE) in a tight loop; when the swap lands between the lstat and the subsequent mkdir, the lock user's process creates claims/ and claim files inside the attacker-chosen target directory with the lock user's privileges. Additionally, the claims property and force_break() resolve the path with no check at all, enabling two no-race attack variants. The vulnerability was introduced with commit 650c17c (PR #659, 2026-07-14) and first released in version 3.30.0 (GitHub Advisory, Security Advisory).

Impact

Successful exploitation allows a local attacker to cause the lock user's process (potentially running as root) to create a claims directory and claim files with the lock user's privileges inside an attacker-chosen directory. The primary availability impact is high: if the symlink targets another strict lock's .filelock directory, stranded claim files cause that lock to time out for every process until an operator manually force-breaks each one, effectively denying the lock to all users. Confidentiality impact is low — through the claims property, an attacker can learn one file name per planted link from a directory only the lock user can list. Integrity impact is also low — the lock user's process creates directories and fixed-format claim files in the attacker-chosen location, but does not overwrite or truncate existing data. The force_break() variant can additionally place two processes simultaneously inside a different strict lock's critical section without either being notified (GitHub Advisory, Security Advisory).

Exploitability

No public proof-of-concept exploit code or in-the-wild exploitation has been reported. The vulnerability was discovered by Anthropic's OSS Scanner (automated analysis) and reported on October 15, 2026. Exploitation requires a local unprivileged account on the same host and high attack complexity — the renameat2(RENAME_EXCHANGE) swap must land within a narrow window between two system calls, though the attacker's loop approach succeeded in 1–31 attempts (about 1.5 seconds) in testing. The CVE status is listed as "Reserved" and no EPSS score or CISA KEV catalog entry is available at this time (GitHub Advisory, Security Advisory).

Exploitation steps

  1. Reconnaissance: Identify a target system where filelock >= 3.30.0 and <= 4.2.0 is installed and a privileged process (e.g., root or a service account) uses StrictSoftFileLock on a path in a shared, non-sticky directory (e.g., /dev/shm/ or a world-writable project directory).
  2. Setup: As the attacker, create a symlink decoy pointing to a target directory the lock user can write (e.g., another strict lock's .filelock directory or a private directory): os.symlink('/home/victim/private', '/dev/shm/work.lock.decoy').
  3. Launch swap loop: Continuously and atomically exchange the lock's coordination directory name with the symlink using renameat2(RENAME_EXCHANGE), so the name alternates between the real directory and the symlink with every syscall iteration.
  4. Race exploitation: Wait for the swap to land between the victim process's lstat check (which sees the real directory) and the subsequent mkdir .filelock/claims call (which now follows the symlink into the target directory). This typically succeeds within 1–31 lock acquisition attempts.
  5. Observe impact: The lock user's process creates claims/ and claim files (intent-v1-*.claim, held-v1-*.claim) inside the attacker-chosen target directory with the lock user's privileges. If the target is another strict lock's .filelock directory, that lock becomes permanently timed out until an operator manually force-breaks each stranded claim.
  6. (Optional) No-race variants: Plant a symlink at .filelock/claims directly (no race needed) to trigger SoftFileLockProtocolError that leaks a file name from the lock user's private directory, or plant a symlink at the lock path to abuse force_break() and break a live lock's critical section exclusion (GitHub Advisory, Security Advisory).

Indicators of compromise

  • File System: Unexpected claims/ directory (owned by the lock user, mode 0755) appearing inside directories unrelated to the lock's intended path (e.g., inside another lock's .filelock directory or a private directory).
  • File System: Presence of intent-v1-*.claim, held-v1-*.claim, or .held-v1-*.claim.private-v1-*.tmp files in unexpected locations outside the intended lock directory.
  • File System: A symbolic link (.decoy or similar) appearing alongside the lock's .filelock coordination directory in a shared directory.
  • Logs: Repeated SoftFileLockProtocolError or FileNotFoundError exceptions in application logs referencing the lock path, indicating failed acquisition attempts during the swap loop.
  • Logs: Timeout errors from StrictSoftFileLock on a lock that no process is actively holding, suggesting stranded claim files are blocking acquisition.
  • Process: A process running renameat2 syscalls in a tight loop on the .filelock directory name (observable via strace or auditd syscall monitoring) (GitHub Advisory, Security Advisory).

Mitigation and workarounds

Upgrade filelock to version 4.2.1 or later, which fixes the vulnerability by opening .filelock with O_NOFOLLOW and accessing claims relative to that file descriptor (using dir_fd/openat), so a symlink swapped in at either level fails the operation instead of redirecting it. The fix also sets the sticky bit on both .filelock and claims directories and applies the lock's mode to them. If immediate upgrade is not possible, apply these workarounds: keep strict locks in a directory no other user can write; in sticky shared directories (e.g., /tmp), pre-create .filelock as the service user before any other account can; do not run the blind recovery loop from docs/tutorials.rst on locks in shared directories without first verifying claim ownership as described in docs/how-to.rst; and treat unexpected SoftFileLockProtocolError quoting an unknown claim name as a potential sign of exploitation (Security Advisory, Release 4.2.1).

Community reactions

The vulnerability was discovered by Anthropic's OSS Scanner (automated analysis) and reported to the maintainers on October 15, 2026. The maintainer (gaborbernat) opened a fix PR within approximately two hours of receiving the report and published the patched release 4.2.1 on October 17, 2026, demonstrating a rapid response. The advisory notes that this vulnerability shares only the bug class with a prior related issue (GHSA-j8f7-rjxc-mr56), but differs in that the symlink sits one path component above the final one and is swapped in after the check rather than resting statically. No significant broader community or media reactions have been identified beyond the GitHub advisory and associated tooling coverage (GitHub Advisory, Security Advisory).

Additional resources

Linux Distribution fix status

Fix availability across major Linux distributions and their releases.

Debian

Fixed

bookworm

python-filelock

Affected

sid

python-filelock: 4.2.2-1

Fixed

trixie

python-filelock

Affected

Ubuntu

Unknown

bionic (esm-apps)

python-filelock

Unknown

devel

python-filelock

Unknown

focal (esm-apps)

python-filelock

Unknown

jammy

python-filelock

Unknown

jammy (esm-apps)

python-filelock

Unknown

noble

python-filelock

Unknown

noble (esm-apps)

python-filelock

Unknown

resolute

python-filelock

Unknown

Source: This report was generated using AI

Related Python vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-113663HIGH8.8
  • Python logoPython
  • autogen
NoYesNov 05, 2026
CVE-2026-58273HIGH8.1
  • Python logoPython
  • rucio
NoYesNov 05, 2026
CVE-2026-112938MEDIUM5.8
  • Python logoPython
  • litellm-fips
NoYesNov 05, 2026
GHSA-6p33-92hw-4v86MEDIUM4
  • Python logoPython
  • extract-msg
NoYesNov 06, 2026
CVE-2026-112913LOW3.3
  • Python logoPython
  • nemo
NoYesNov 05, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management