pydicom has a path traversal in FileSet/DICOMDIR ReferencedFileID allows file access outside the File-set root
Date: 2026-03
Affected: pip/pydicom, pip/pydicom
Attack vector: path-traversal
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-32711
CWE: CWE-22
CVSS: 7.8 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

### Summary A crafted `DICOMDIR` can set `ReferencedFileID` to a path outside the File-set root. `pydicom` resolves the path only to confirm that it exists, but does not verify that the resolved path remains under the File-set root. Subsequent public `FileSet` operations such as `copy()`, `write()`, and `remove()+write(use_existing=True)` use that unchecked path in file I/O operations. This allows arbitrary file read/copy and, in some flows, move/delete outside the File-set root. ### Details Verified on `pydicom 3.1.0.dev0`. Relevant logic is in `src/pydicom/fileset.py`: - `RecordNode._file_id` converts `ReferencedFileID` directly to `Path(...)` - `FileSet.load()` checks only `(root / file_id).resolve(strict=True)` to confirm existence - `FileSet.load()` does not verify that the final resolved path is contained within the File-set root - `FileInstance.path` returns `self.file_set.path / self.node._file_id` - `FileSet.copy()` uses `shutil.copyfile(instance.path, dst)` - `FileSet.write()` uses `Path(instance.path).unlink()` and `shutil.move(...)` Because there is no containment check such as `resolved.relative_to(root.resolve(strict=True))`, a malicious `DICOMDIR` can reference: - absolute paths such as `/etc/passwd` - traversal paths such as `../...` - syntactically conformant file IDs that escape via symlinks This is not limited to obviously invalid VR input. Even when `pydicom` emits warnings for invalid `ReferencedFileID` values, the operation is not blocked. I also c


Mitigations:

Architectural layers (MAESTRO):