T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- scripts/upload_to_oss.py:62
- Finding
- Directory Uploads Follow Symbolic Links Outside the Selected Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upload_to_oss.py`, lines 62–77 **Vulnerability Type**: Symbolic-link traversal during archive creation **Risk Level**: Medium ### Vulnerable Code ```python def iter_files(path: Path) -> Iterable[Path]: for item in sorted(path.rglob("*")): if item.is_file(): yield item def make_archive(paths: list[Path]) -> Path: timestamp = dt.datetime.now(dt.UTC).strftime("%Y%m%dT%H%M%SZ") archive = Path(tempfile.gettempdir()) / f"openclaw-artifact-{timestamp}-{uuid.uuid4().hex[:8]}.zip" with zipfile.ZipFile(archive, "w", compression=zipfile.ZIP_DEFLATED) as zf: for source in paths: if source.is_dir(): base = source.parent for file_path in iter_files(source): zf.write(file_path, file_path.relative_to(base).as_posix()) else: zf.write(source, source.name) return archive ``` ### Technical Analysis `Path.is_file()` follows symbolic links by default. Consequently, a symbolic link encountered by `path.rglob("*")` is treated as a regular file when its target is a file. The subsequent `ZipFile.write()` operation opens the symbolic-link target and stores its contents in the archive. The implementation does not reject symbolic links and does not resolve each candidate path and verify that the resolved target remains within the user-selected directory. A directory prepared by an untrusted party can therefore include links to files outside the intended upload boundary. This does not grant access to files that the running process cannot already read. However, it can cause readable local data outside the selected artifact directory to be unintentionally packaged and transmitted to OSS. ### Attack Path 1. An attacker gains the ability to influence the contents of a directory that will be uploaded. 2. The attacker creates a symbolic link inside that directory, for example: ```text ...[truncated 1013 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Reject symbolic links while enumerating directory contents: ```python def iter_files(path: Path) -> Iterable[Path]: for item in sorted(path.rglob("*")): if item.is_symlink(): raise SystemExit(f"Symbolic links are not allowed: {item}") if item.is_file(): yield item ``` 2. Apply a containment check in addition to rejecting links: ```python root = source.resolve() candidate = file_path.resolve(strict=True) if not candidate.is_relative_to(root): raise SystemExit(f"Path escapes upload directory: {file_path}") ``` 3. Perform the containment check immediately before opening each file to reduce time-of-check/time-of-use exposure. 4. If symbolic links must be supported, archive the link metadata rather than dereferencing the target, and clearly document that behavior. 5. Add regression tests covering links to: - Files outside the selected directory. - Files within the selected directory. - Broken links. - Chained symbolic links. ]]>
