{"id":"CVE-2026-22871","aliases":["GHSA-xg9w-vg3g-6m68","PYSEC-2026-1430"],"url":"https://o3.security/vulnerability/CVE-2026-22871","summary":"GuardDog Path Traversal Vulnerability Leads to Arbitrary File Overwrite and RCE","details":"## Summary\n\nA **path traversal vulnerability** exists in GuardDog's `safe_extract()` function that allows malicious PyPI packages to write arbitrary files outside the intended extraction directory, leading to **Arbitrary File Overwrite** and **Remote Code Execution** on systems running GuardDog.\n\n**CWE:** CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)\n\n## Details\n\n### Vulnerable Code\n\n**File:** `guarddog/utils/archives.py`\n\n```python\nelif zipfile.is_zipfile(source_archive):\n    with zipfile.ZipFile(source_archive, \"r\") as zip:\n        for file in zip.namelist():\n            # Note: zip.extract cleans up any malicious file name\n            # such as directory traversal attempts This is not the\n            # case of zipfile.extractall\n            zip.extract(file, path=os.path.join(target_directory, file))  # ❌ VULNERABLE\n```\n\n### Root Cause\n\nThe comment about `zip.extract()` fooled me at first :) then I noticed the `os.path.join()` call. \nThe vulnerability stems from **incorrect usage of Python's `zipfile.ZipFile.extract()` API**:\n\n- The `path` parameter should be the **target directory**, not a full file path\n- `extract()` automatically appends the member name to the path\n- By passing `os.path.join(target_directory, file)`, GuardDog causes the filename to be appended **twice**\n- This breaks zipfile's built-in path traversal sanitization\n\n### Attack Vector\n\n1. Attacker creates malicious wheel with path traversal filenames\n2. Uploads to PyPI or distributes directly\n3. Package scan: `guarddog pypi scan malicious-pkg`\n4. GuardDog downloads and extracts the package\n5. Malicious files written to arbitrary locations\n6. Code execution could be achieved\n\n## Impact\n\nImpact depends on how GuardDog is running and under which environment.\n\n### Critical Scenarios\n\n1. **Immediate Code Execution**\n   - Write to `~/.bashrc` → executes on next shell\n   - Write to `~/.profile` → executes on login\n\n2. **Persistent Backdoors**\n   - Write to `~/.ssh/authorized_keys` → SSH access\n   - Write to `/etc/cron.d/malicious` → scheduled execution (if root)\n   - Write to systemd user services → persistent execution\n\nand more...\n\n## Credits\n\n**Reported by:** Charbel (dwbruijn)","published":"2026-01-13T20:46:57.324Z","modified":"2026-10-28T02:31:14.285976971Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"guarddog","fixedVersion":"2.7.1"}],"fix":{"url":"https://github.com/DataDog/guarddog/commit/9aa6a725b2c71d537d3c18d1c15621395ebb879c","label":"DataDog/guarddog@9aa6a72"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/22xxx/CVE-2026-22871.json"},{"type":"ADVISORY","url":"https://github.com/DataDog/guarddog/security/advisories/GHSA-xg9w-vg3g-6m68"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-22871"},{"type":"FIX","url":"https://github.com/DataDog/guarddog/commit/9aa6a725b2c71d537d3c18d1c15621395ebb879c"},{"type":"PACKAGE","url":"https://github.com/DataDog/guarddog"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:31:14.285976971Z"}}