CVE-2026-28684
python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
6.6
python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
- Record
CVE-2026-28684- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Weakness
- CWE-59, CWE-61
- Updated
- 2026-11-02
EUVDEUVD-2026-23901
6.6
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
- Record
EUVD-2026-23901- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-mf9w-mj56-hr94
6.6
python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback
Summary
set_key() and unset_key() 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 set_key() and unset_key() 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:
shutil.move()first attemptsos.rename(), which fails with anOSErrorbecause atomic renames cannot cross device boundaries.- On failure,
shutil.move()falls back toshutil.copy2()followed byos.unlink(). shutil.copy2()callsshutil.copyfile()withfollow_symlinks=Trueby default.- 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 set_key() or unset_key(), 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
set_key(), 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).
import os
import sys
import tempfile
from dotenv import set_key
# Pre-condition: /tmp must be on a different device than the target directory.
tmp_dev = os.stat("/tmp").st_dev
home_dev = os.stat(os.path.expanduser("~")).st_dev
assert tmp_dev != home_dev, "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, "victim_config.txt")
with open(target, "w") as f:
f.write("DB_PASSWORD=supersecret\n")
# Attacker pre-places a symlink at the path the application will use as .env
env_symlink = os.path.join(workdir, ".env")
os.symlink(target, env_symlink)
before = open(target).read()
# Application writes a new key -- triggers the cross-device fallback
set_key(env_symlink, "INJECTED", "attacker_value")
after = open(target).read()
print("Before:", repr(before))
print("After: ", repr(after))
print("Symlink target overwritten:", target)
Expected output:
Before: 'DB_PASSWORD=supersecret\n'
After: "DB_PASSWORD=supersecret\nINJECTED='attacker_value'\n"
Symlink target overwritten: /home/user/tmp806nut2g/victim_config.txt
Remediation
The fix changes the rewrite() context manager in the following ways:…
- Record
GHSA-mf9w-mj56-hr94- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Weakness
- CWE-59, CWE-61
- Updated
- 2026-11-02
NVD
6.6
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
- Record
CVE-2026-28684- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Weakness
- CWE-59, CWE-61
- Updated
- 2026-11-02
OSV
6.6
python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
- Record
CVE-2026-28684- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Weakness
- CWE-59, CWE-61
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-28684
6.6
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
- Record
DEBIAN-CVE-2026-28684- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
Show 10 moreShow less
OSVGHSA-mf9w-mj56-hr94
6.6
python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback
Summary
set_key() and unset_key() 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 set_key() and unset_key() 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:
shutil.move()first attemptsos.rename(), which fails with anOSErrorbecause atomic renames cannot cross device boundaries.- On failure,
shutil.move()falls back toshutil.copy2()followed byos.unlink(). shutil.copy2()callsshutil.copyfile()withfollow_symlinks=Trueby default.- 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 set_key() or unset_key(), 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
set_key(), 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).
import os
import sys
import tempfile
from dotenv import set_key
# Pre-condition: /tmp must be on a different device than the target directory.
tmp_dev = os.stat("/tmp").st_dev
home_dev = os.stat(os.path.expanduser("~")).st_dev
assert tmp_dev != home_dev, "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, "victim_config.txt")
with open(target, "w") as f:
f.write("DB_PASSWORD=supersecret\n")
# Attacker pre-places a symlink at the path the application will use as .env
env_symlink = os.path.join(workdir, ".env")
os.symlink(target, env_symlink)
before = open(target).read()
# Application writes a new key -- triggers the cross-device fallback
set_key(env_symlink, "INJECTED", "attacker_value")
after = open(target).read()
print("Before:", repr(before))
print("After: ", repr(after))
print("Symlink target overwritten:", target)
Expected output:
Before: 'DB_PASSWORD=supersecret\n'
After: "DB_PASSWORD=supersecret\nINJECTED='attacker_value'\n"
Symlink target overwritten: /home/user/tmp806nut2g/victim_config.txt
Remediation
The fix changes the rewrite() context manager in the following ways:…
- Record
GHSA-mf9w-mj56-hr94- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Weakness
- CWE-59, CWE-61
- Updated
- 2026-11-02
OSVOESA-2026-2198
6.6
python-dotenv security update
Python-dotenv reads key-value pairs from a .env file and can set them as environment variables. It helps in the development of applications following the 12-factor principles.
Security Fix(es):
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.(CVE-2026-28684)
- Record
OESA-2026-2198- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
OSVOESA-2026-2199
6.6
python-dotenv security update
Python-dotenv reads key-value pairs from a .env file and can set them as environment variables. It helps in the development of applications following the 12-factor principles.
Security Fix(es):
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.(CVE-2026-28684)
- Record
OESA-2026-2199- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
OSVOESA-2026-2324
6.6
python-dotenv security update
Python-dotenv reads key-value pairs from a .env file and can set them as environment variables. It helps in the development of applications following the 12-factor principles.
Security Fix(es):
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.(CVE-2026-28684)
- Record
OESA-2026-2324- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
OSVPYSEC-2026-2270
6.6
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
- Record
PYSEC-2026-2270- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
OSVROOT-APP-PYPI-CVE-2026-28684
6.6
CVE-2026-28684 in python-dotenv - Patched by Root
Root has patched CVE-2026-28684 in the python-dotenv package for Root:PyPI. Multiple fixed versions available.
- Record
ROOT-APP-PYPI-CVE-2026-28684- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-28684
6.6
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() 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.
- Record
UBUNTU-CVE-2026-28684- CVSS 3.1
- 6.6 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
OSVopenSUSE-SU-2026:20952-1 No score
Security update for python-python-dotenv
This update for python-python-dotenv fixes the following issue:
- CVE-2026-28684: Prior to version 1.2.2,
set_key()andunset_key()in python-dotenv follow symbolic links when rewriting.envfiles, allowing a local attacker to overwrite arbitrary files via a crafted symlink (bsc#1262423).
- Record
openSUSE-SU-2026:20952-1- Updated
- 2026-11-02
OSVSUSE-SU-2026:22118-1 No score
Security update for python-python-dotenv
This update for python-python-dotenv fixes the following issue:
- CVE-2026-28684: Prior to version 1.2.2,
set_key()andunset_key()in python-dotenv follow symbolic links when rewriting.envfiles, allowing a local attacker to overwrite arbitrary files via a crafted symlink (bsc#1262423).
- Record
SUSE-SU-2026:22118-1- Updated
- 2026-11-02
OSVSUSE-SU-2026:2724-1 No score
Security update for python-python-dotenv
This update for python-python-dotenv fixes the following issue:
- CVE-2026-28684: follow symbolic links when rewriting
.envfiles (bsc#1262423)
- Record
SUSE-SU-2026:2724-1- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
python-dotenvPyPI |
before 1.2.2 | 1.2.2 |
python-dotenvDebian:12 |
All versions, plus listed versions 0.21.0-11.0.0-11.0.1-11.2.1-11.2.1-2 |
None known |
python-dotenvDebian:13 |
All versions, plus listed versions 1.0.1-11.2.1-11.2.1-2 |
None known |
python-dotenvDebian:14 |
All versions, plus listed versions 1.0.1-11.2.1-11.2.1-2 |
None known |
python-dotenvPyPI |
before 1.2.2, plus listed versions 0.1.00.1.20.1.30.1.50.10.0 Show 44 moreShow less0.10.10.10.20.10.30.10.40.10.50.11.00.12.00.13.00.14.00.15.00.16.00.17.00.17.10.18.00.19.00.19.10.19.20.2.00.20.00.21.00.21.10.3.00.4.00.5.00.5.10.6.00.6.10.6.20.6.30.6.40.6.50.7.00.7.10.8.00.8.10.8.20.9.00.9.11.0.01.0.11.1.01.1.11.2.01.2.1 |
1.2.2 |
python-dotenvopenEuler:24.03-LTS |
before 1.2.2-1.oe2403 | 1.2.2-1.oe2403 |
python-dotenvopenEuler:24.03-LTS-SP1 |
before 1.2.2-1.oe2403sp1 | 1.2.2-1.oe2403sp1 |
python-dotenvopenEuler:24.03-LTS-SP3 |
before 1.2.2-1.oe2403sp3 | 1.2.2-1.oe2403sp3 |
python-dotenvRoot:PyPI |
before 1.2.1+aikido.2 | 1.2.1+aikido.21.1.1+aikido.20.21.1+aikido.11.0.1+aikido.11.1.1+aikido.31.1.1+aikido.41.2.1+aikido.3 |
python-python-dotenvSUSE:Linux Enterprise Server 16.0 |
before 1.1.0-160000.3.1 | 1.1.0-160000.3.1 |
Show 10 moreShow less
| Package | Affected | Fixed |
|---|---|---|
python-python-dotenvSUSE:Linux Enterprise Server for SAP applications 16.0 |
before 1.1.0-160000.3.1 | 1.1.0-160000.3.1 |
python-python-dotenvSUSE:Linux Enterprise Module for Python 3 15 SP7 |
before 1.0.0-150400.9.6.1 | 1.0.0-150400.9.6.1 |
python-dotenvUbuntu:18.04:LTS |
All versions, plus listed versions 0.7.1-1.1 |
None known |
python-dotenvUbuntu:20.04:LTS |
All versions, plus listed versions 0.9.1-1 |
None known |
python-dotenvUbuntu:22.04:LTS |
All versions, plus listed versions 0.15.0-10.19.0-10.19.2-1 |
None known |
python-dotenvUbuntu:24.04:LTS |
All versions, plus listed versions 1.0.0-11.0.1-1 |
None known |
python-dotenvUbuntu:25.10 |
All versions, plus listed versions 1.0.1-1 |
None known |
python-dotenvUbuntu:26.10 |
All versions, plus listed versions 1.0.1-1build11.2.1-2 |
None known |
python-dotenvUbuntu:26.04:LTS |
All versions, plus listed versions 1.0.1-11.0.1-1build1 |
None known |
python-python-dotenvopenSUSE:Leap 16.0 |
before 1.1.0-160000.3.1 | 1.1.0-160000.3.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-28684 is a medium vulnerability (CVSS 6.6) in Saurabh-kumar Python-dotenv, published April 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.25%, the 16th percentile. Fixed in python-dotenv 1.2.2.
References
github.com/theskumar/python-dotenv/security/advisories/GHSA-mf9w-mj56-hr94
github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311
github.com/theskumar/python-dotenv/releases/tag/v1.2.2
nvd.nist.gov/vuln/detail/CVE-2026-28684
github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311.patch
github.com/advisories/GHSA-mf9w-mj56-hr94
Show 12 moreShow less
github.com/CVEProject/cvelistV5/tree/main/cves/2026/28xxx/CVE-2026-28684.json
security-tracker.debian.org/tracker/CVE-2026-28684
github.com/theskumar/python-dotenv
openeuler.org/zh/security/security-bulletins/detail?id=openEuler-SA-2026-2198
openeuler.org/zh/security/security-bulletins/detail?id=openEuler-SA-2026-2199
openeuler.org/zh/security/security-bulletins/detail?id=openEuler-SA-2026-2324
suse.com/support/update/announcement/2026/suse-su-202622118-1
bugzilla.suse.com/1262423
suse.com/security/cve/CVE-2026-28684
suse.com/support/update/announcement/2026/suse-su-20262724-1
ubuntu.com/security/CVE-2026-28684
cve.org/CVERecord?id=CVE-2026-28684
Left out here: the rest of 2 source descriptions. The JSON record has them.