T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/flow_voice.py:78
- Finding
- Unsafe Pickle Deserialization Through Traversable Profile Path## Vulnerability Details **File Location**: `scripts/flow_voice.py`, lines 78–86 **Vulnerability Type**: Unsafe deserialization combined with path traversal **Risk Level**: High ### Vulnerable Code ```python def load_profile(name: str) -> object: """Load a saved voice profile.""" path = PROFILES_DIR / f"{name}.pkl" if not path.exists(): print(f" ❌ No profile found for '{name}' at {path}", file=sys.stderr) print(f" 💡 Clone a voice first: --sample ref.wav --name {name}", file=sys.stderr) sys.exit(1) with open(path, "rb") as f: encoded = pickle.load(f) print(f" ✅ Profile loaded: {name}") return encoded ``` ### Technical Analysis The `--voice` argument is inserted directly into a path without restricting path separators, parent-directory components, absolute paths, or other special path syntax. Appending `.pkl` does not prevent traversal. For example, a profile name containing `../../../../tmp/payload` can resolve outside `PROFILES_DIR`. The selected file is then passed to `pickle.load()`. Python pickle is an executable serialization format: a crafted pickle can invoke attacker-selected callables during deserialization. Consequently, loading a profile is not merely a data operation and must only be performed on trusted, integrity-protected files. The combination of attacker-selectable file resolution and unsafe deserialization creates a direct local code-execution primitive when an attacker can supply the `--voice` value and make a malicious pickle accessible to the process. ### Attack Path 1. An attacker creates or places a malicious pickle file at a location readable by the Skill process, such as `/tmp/payload.pkl`. 2. The attacker invokes the Skill, or induces the Agent to invoke it, with a traversal value such as: ```text --voice ../../../../tmp/payload ``` 3. `PROFILES_DIR / f"{name}.pkl"` resolves to the external malicious file. ...[truncated 855 chars]
- Remediation
- ## Remediation Suggestions 1. Replace pickle with a non-executable serialization format supported by the model representation, such as a strictly validated tensor or structured data format. 2. If pickle cannot be removed, treat profile files as trusted executable artifacts and never load files based on unrestricted user paths. 3. Restrict profile identifiers with an allowlist: ```python import re if not re.fullmatch(r"[A-Za-z0-9_-]+", name): raise ValueError("Invalid profile name") ``` 4. Resolve the candidate path and enforce containment: ```python profiles_root = PROFILES_DIR.resolve() path = (profiles_root / f"{name}.pkl").resolve() if path.parent != profiles_root: raise ValueError("Profile path escapes the profile directory") ``` 5. Ensure the profile directory and files are owned by the Agent account and are not writable by untrusted users. 6. Consider signing profiles or storing an integrity hash in trusted metadata before loading them. 7. Run voice processing in a sandbox with minimal filesystem, network, and subprocess permissions to limit the impact of any deserialization compromise.
