CVE-2026-28684: python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback

Published Apr 20, 2026
·
Updated

Summary

setkey() and unsetkey() in python-dotenv follow symbolic links when rewriting .env files, allowing a local attacker to overwrite arbitrary files via a crafted symlink when a cross-device rename fallback is triggered.

Details

The rewrite() context manager in dotenv/main.py is used by both setkey() and unsetkey() to safely modify .env files. It works by writing to a temporary file (created in the system's default temp directory, typically /tmp) and then using shutil.move() to replace the original file.

When the .env path is a symbolic link and the temp directory resides on a different filesystem than the target (a common configuration on Linux systems using tmpfs for /tmp), the following sequence occurs:

1. shutil.move() first attempts os.rename(), which fails with an OSError because atomic renames cannot cross device boundaries. 2. On failure, shutil.move() falls back to shutil.copy2() followed by os.unlink(). 3. shutil.copy2() calls shutil.copyfile() with followsymlinks=True by default. 4. This causes the content to be written to the symlink target rather than replacing the symlink itself.

An attacker who has write access to the directory containing a .env file can pre-place a symlink pointing to any file that the application process has write access to. When the application (or a privileged process such as a deploy script, Docker entrypoint, or CI pipeline) calls setkey() or unsetkey(), the symlink target is overwritten with the new .env content.

This vulnerability does not require a race condition and is fully deterministic once the preconditions are met.

Impact The primary impacts are to integrity and availability:

- File overwrite / destruction (DoS): An attacker can cause an application or privileged process to corrupt or destroy configuration files, database configs, or other sensitive files it would not normally have access to modify. - Integrity violation: The target file's original content is replaced with .env-formatted content controlled by the attacker. - Potential privilege escalation: In scenarios where a privileged process (running as root or a service account) calls setkey(), the attacker can leverage this to write to files beyond their own access level.

The scope of impact depends on the application using python-dotenv and the privileges under which it runs.

Proof of Concept

The following script demonstrates the vulnerability. It requires /tmp and the user's home directory to reside on different devices (common on systemd-based Linux systems with tmpfs).

python import os import sys import tempfile from dotenv import setkey

Pre-condition: /tmp must be on a different device than the target directory. tmpdev = os.stat("/tmp").stdev homedev = os.stat(os.path.expanduser("~")).stdev assert tmpdev != homedev, "Skipped: /tmp and ~ are on the same device (no cross-device move)"

with tempfile.TemporaryDirectory(dir=os.path.expanduser("~")) as workdir: # File an attacker wants to overwrite target = os.path.join(workdir, "victimconfig.txt") with open(target, "w") as f: f.write("DBPASSWORD=supersecret\n")

# Attacker pre-places a symlink at the path the application will use as .env envsymlink = os.path.join(workdir, ".env") os.symlink(target, envsymlink)

before = open(target).read()

# Application writes a new key -- triggers the cross-device fallback setkey(envsymlink, "INJECTED", "attackervalue")

after = open(target).read()

print("Before:", repr(before)) print("After: ", repr(after)) print("Symlink target overwritten:", target)

Expected output: Before: 'DBPASSWORD=supersecret\n' After: "DBPASSWORD=supersecret\nINJECTED='attackervalue'\n" Symlink target overwritten: /home/user/tmp806nut2g/victimconfig.txt

Remediation

The fix changes the rewrite() context manager in the following ways:

1. Symlinks are no longer followed by default. When the .env path is a symlink, rewrite() now resolves it to the real path before proceeding, or (by default) operates on the symlink entry itself rather than the target. 2. A followsymlinks: bool = False parameter is added to setkey() and unsetkey() for users who explicitly need the old behavior. 3. Temp files are written in the same directory as the target .env file (instead of the system temp directory), eliminating the cross-device rename condition entirely. 4. os.replace() is used instead of shutil.move(), providing atomic replacement without symlink-following fallback behavior.

Users are advised to upgrade to the patched version as soon as it is available on PyPI.

Timeline

| Date | Event | | ------------ | ---------------------------------------------------------------------------------------------------- | | 2026-01-09 | Initial report received from Giorgos Tsigourakos regarding a separate, unrelated issue also located in rewrite() | | 2026-01-10 | Co-maintainer acknowledged report, requested clarification | | 2026-01-11 | Initial report assessed as not exploitable and closed | | 2026-02-24 | Reporter identified new, distinct cross-device symlink attack vector with deterministic exploitation | | 2026-02-26 | Co-maintainer confirmed vulnerability and shared draft patch | | 2026-02-26 | Reporter validated fix with monkeypatched PoC, proposed CVSS | | 2026-03-01 | Patch merged to main | | 2026-03-01 | Patched version released to PyPI | | 2026-04-20 | Advisory published |

Patches

Upgrade to v.1.2.2 or use the patch from https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311.patch

Other sources

python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, setkey() and unsetkey() in python-dotenv follow symbolic links when rewriting .env files, allowing a local attacker to overwrite arbitrary files via a crafted symlink when a cross-device rename fallback is triggered. Users should upgrade to v.1.2.2 or, as a workaround, apply the patch manually.

MITRE

Affected Software

5 affected componentsFixes available
pypi/python-dotenv<1.2.2
pip/python-dotenv<1.2.2
1.2.2
Saurabh-kumar Python-dotenv Python<1.2.2
IBM Db2 Genius Hub<=1.1, 1.1.1, 1.1.2
IBM Agentics<=1.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/python-dotenv to a version that resolves this vulnerability.

    Fixed in 1.2.2
  2. Upgrade

    Upgrade python-dotenv to a version that resolves this vulnerability.

    Fixed in 1.2.2
  3. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Patch 790c5c02991100aa1bf41ee5330aca75edc51311.patch

Event History

Apr 20, 2026
CVE Published
via MITRE·04:25 PM
Data Sourced
via MITRE·04:25 PM
DescriptionSeverityWeakness
Data Sourced
via Red Hat·05:02 PM
DescriptionSeverityAffected Software
Data Sourced
via NVD·05:16 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·05:16 PM
RemedyAffected Software
Apr 21, 2026
Advisory Published
via GitHub·02:38 PM
Data Sourced
via GitHub·02:38 PM
DescriptionSeverityWeaknessAffected Software
Jul 13, 2026
Data Sourced
via IBM·12:00 AM
DescriptionAffected Software

Parent advisories

This vulnerability appears in the following advisories.

Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-28684?

CVE-2026-28684 is considered a critical vulnerability due to its potential for arbitrary file overwrite.

2

How do I fix CVE-2026-28684?

To fix CVE-2026-28684, upgrade to python-dotenv version 1.2.2 or later.

3

What does CVE-2026-28684 affect?

CVE-2026-28684 affects versions of python-dotenv prior to 1.2.2 that improperly handle symbolic links.

4

What is the exploit method for CVE-2026-28684?

CVE-2026-28684 can be exploited through symlink following in the set_key function, allowing for arbitrary file overwrites.

5

When was CVE-2026-28684 disclosed?

CVE-2026-28684 was disclosed in 2026, highlighting security issues present in earlier versions of python-dotenv.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203