{"repro_id":"REPRO-2026-00084","version":2,"title":"Unstructured has Path Traversal via Malicious MSG Attachment that Allows Arbitrary File Write","repro_type":"security","status":"published","description":"A Path Traversal vulnerability in the `partition_msg` function allows an attacker to write or overwrite arbitrary files on the filesystem when processing malicious MSG files with attachments.","root_cause":"## Summary\nThe unstructured library’s `partition_msg` attachment handling writes attachment payloads to a temporary directory using the attachment filename directly. When an MSG attachment filename contains path traversal sequences (e.g., `../../../../tmp/pwned`), `os.path.join(tmp_dir, filename)` escapes the temporary directory and writes to arbitrary filesystem locations. This occurs before any attachment partitioning, enabling arbitrary file overwrite when `process_attachments=True`.\n\n## Impact\n- Package/component affected: `unstructured` partitioning of MSG files (`unstructured.partition.msg` / `_AttachmentPartitioner`).\n- Affected versions: <= 0.18.17 (tested on 0.18.15). Patched in 0.18.18 per advisory.\n- Risk level and consequences: Critical. Arbitrary file overwrite on the host (e.g., overwriting configs/cron/python packages), potentially leading to code execution or data loss.\n\n## Root Cause\n`_AttachmentPartitioner._iter_elements()` in `unstructured/partition/msg.py` uses:\n```\ndetached_file_path = os.path.join(tmp_dir_path, self._attachment_file_name)\nwith open(detached_file_path, \"wb\") as f:\n    f.write(self._file_bytes)\n```\n`self._attachment_file_name` comes from `oxmsg` attachment `file_name` (PID_ATTACH_LONG_FILENAME) and is not sanitized. When it contains `../` sequences, `os.path.join` produces a path outside the intended temp directory, enabling traversal and arbitrary file write. The fix in 0.18.18 likely sanitizes or strips path separators before writing.\n\n## Reproduction Steps\n1. Run `repro/reproduction_steps.sh`.\n2. The script clones unstructured, checks out tag 0.18.15, installs deps, copies `fake-email-multiple-attachments.msg`, and overwrites the attachment filename stream (`__substg1.0_3707001F`) with `../../../../tmp/pwned` (UTF-16LE) using `olefile`.\n3. It calls `partition_msg(..., process_attachments=True)`, which writes the attachment payload to `/tmp/pwned` before failing on missing image deps.\n4. Expected evidence: `/tmp/pwned` exists and has non-zero size after the call.\n\n## Evidence\n- Script output (from `repro/reproduction_steps.sh`):\n  - `partition error: <class 'ImportError'> partition_image() is not available...`\n  - `/tmp/pwned exists: True`\n  - `/tmp/pwned size: 96226`\n- Environment: Ubuntu container with Python venv; unstructured 0.18.15 installed from local repo; python-oxmsg 0.0.2.\n\n## Recommendations / Next Steps\n- Fix: sanitize attachment filenames by stripping path separators or enforcing basename before writing to temp dir.\n- Upgrade: update to unstructured >= 0.18.18.\n- Tests: add regression test that attachment filenames containing `../` are sanitized and cannot escape temp directory.\n\n## Additional Notes\n- Repro script is idempotent: it overwrites the same test MSG and removes `/tmp/pwned` before running.\n- Even when attachment partitioning fails due to missing optional image dependencies, the vulnerable file write already occurred, so evidence is still produced.","ghsa_id":"GHSA-GM8Q-M8MV-JJ5M","cve_id":"CVE-2025-64712","package":{"name":"unstructured","ecosystem":"pypi","affected_versions":"<=0.18.17","fixed_version":"0.18.18"},"reproduced_at":"2026-02-13T15:08:30.739433+00:00","duration_secs":290.0,"tool_calls":193,"handoffs":2,"quality":{"idempotent_verified":false,"community_verifications":0},"published_at":"2026-02-13T15:08:31.198724+00:00","retracted":false,"artifacts":[{"path":"repro/rca_report.md","filename":"rca_report.md","size":2965,"category":"analysis"},{"path":"repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":1968,"category":"reproduction_script"},{"path":"vuln_variant/rca_report.md","filename":"rca_report.md","size":3691,"category":"analysis"},{"path":"vuln_variant/reproduction_steps.sh","filename":"reproduction_steps.sh","size":2301,"category":"reproduction_script"},{"path":"bundle/ticket.md","filename":"ticket.md","size":1405,"category":"ticket"},{"path":"coding/verify_steps.sh","filename":"verify_steps.sh","size":1475,"category":"other"},{"path":"coding/summary_report.md","filename":"summary_report.md","size":1984,"category":"documentation"}]}