T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/skills_audit.py:108- Finding
External Symlinks Can Expose Host Files Through Audit Snapshots
- Content
View full analysis
list[Path]: files: list[Path] = [] for p in dir_path.rglob("*"): if p.is_file(): if "/.git/" in str(p) or "/__pycache__/" in str(p): continue if p.suffix == ".pyc": continue files.append(p) files.sort(key=lambda x: str(x)) return files ``` ```python repo = ensure_snapshots_repo() dest = repo / "skills" # rsync: mirror skills_dir to snapshots/skills, excluding .git and __pycache__ if dest.exists(): shutil.rmtree(dest) shutil.copytree( skills_dir, dest, ignore=shutil.ignore_patterns(".git", "__pycache__", "*.pyc"), ) ``` ### Technical Analysis The auditor recursively processes files without rejecting symbolic links or verifying that each resolved path remains beneath the audited Skill directory. `Path.is_file()` follows a symbolic link when its target is a regular file. Subsequent calls that hash or read that path also follow the link. In addition, `shutil.copytree()` uses `symlinks=False` by default, which copies the contents of a file referenced by a symbolic link rather than preserving the link itself. Consequently, a Skill controlled by an untrusted author can contain a link to any file readable by the account running the auditor. The linked target may then be: 1. Read while calculating hashes or conducting static analysis. 2. Copied into `~/.openclaw/skills-audit/snapshots/skills/`. 3. Committed to the local Git snapshot history. 4. Exposed later through diff inspection or another process that reads the snapshot. The behavior crosses the intended boundary of `workspace/skills` and is not necessary for static auditing. ### Attack Path 1. An attacker publishes a Skill containin ...[truncated 1316 chars]- Remediation
View remediation
