T02 · Agent Memory Poisoning
Error
- Location
- synthesize.py:146
- Finding
- Persistent Agent Instruction Injection Through Generated Skill Files<![CDATA[ ## Vulnerability Details **File Location**: `synthesize.py:146-157`, `synthesize.py:195-197`, and `synthesize.py:304-305` **Vulnerability Type**: Persistent instruction injection into an agent-loadable skill **Risk Level**: High ### Vulnerable Code ```python # synthesize.py:146-157 steps_section = "\n## Steps\n\n" if steps: for i, step in enumerate(steps, 1): steps_section += f"{i}. {step.get('text', '').strip()}\n" else: steps_section += "*No steps recorded — observation was empty.*\n" # Trigger phrases triggers = [ f'"{slug}"', f'"run {slug}"', f'"do {slug}"', f'"replay {slug}"', ] ``` ```python # synthesize.py:195-197 ## Notes from Synthesis {synthesis_notes if synthesis_notes else "No additional notes."} ``` ```python # synthesize.py:304-305 with open(skill_path, "w") as f: f.write(skill_md) ``` ### Technical Analysis The synthesizer inserts recorded step text and the command-line `--notes` value directly into a persistent `SKILL.md` file without escaping Markdown, isolating the content as untrusted data, or validating it against an expected workflow schema. An attacker-controlled observation can contain Markdown headings, forged metadata, or instructions directed at the agent. For example, a recorded step could introduce a new section instructing the agent to disregard its intended workflow, access unrelated files, or execute additional actions. Because the generated file is a permanent OpenClaw-compatible skill, the injected content can affect later sessions whenever the skill is loaded. The project documentation states that users review and approve generated workflows before they are saved. The implemented `synthesize()` path does not contain an approval gate: it writes `SKILL.md` and `run.sh` immediately. A preview is optional and occurs after synthesis, so it does not prevent persistence. ### Attack Path 1. An attacker influences recorded narration, imported observation data, or the `--notes` arg ...[truncated 1114 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat all observation text and synthesis notes as untrusted data rather than executable agent instructions. 2. Store observations in a strict structured format with separate fields for actions, explanations, parameters, and commands. 3. Escape or reject Markdown constructs that can create headings, front matter, HTML blocks, links, or instruction-like sections. 4. Place raw narration in a clearly delimited quoted or encoded data block that the agent is explicitly instructed not to interpret as policy. 5. Validate generated skills against an allowlisted schema before saving them. 6. Generate files in a temporary staging directory and show the exact resulting content to the user. 7. Require an explicit approval action before atomically moving the generated skill into the active workflow library. 8. Ensure preview mode occurs before persistence rather than after files have already been written. 9. Consider storing raw observations separately from the agent-loadable skill and generating only normalized, reviewed actions in `SKILL.md`. ]]>
