T02 · Agent Memory Poisoning
Error
- Location
- optimizer.py:89
- Finding
- Persistent Agent Memory Poisoning Through Untrusted Summary Content<![CDATA[ ## Vulnerability Details **File Location**: `optimizer.py:89-94`, with attacker-controlled input introduced at `optimizer.py:143-153` **Vulnerability Type**: Persistent agent memory poisoning **Risk Level**: High ### Complete Code Snippet ```python for arc in recent_archives: ts = arc.get('timestamp', 'Unknown') tok = arc.get('token_count', 0) summ = arc.get('summary', '').strip() new_section += f"### Snapshot: {ts}\n" new_section += f"- **Tokens:** {tok}\n" new_section += f"- **Summary:** {summ}\n\n---\n\n" ``` The summary originates from a command-line argument and is persisted without validation: ```python parser.add_argument("--summary", required=True, help="Context summary text") parser.add_argument("--tokens", type=int, default=0, help="Current token count") parser.add_argument("--session-id", default="main", help="Session ID") parser.add_argument("--reset", action="store_true", help="Trigger reset.sh after archiving") args = parser.parse_args() ensure_dirs() # Notify Start try: subprocess.run(["openclaw", "message", "send", "--message", f"🔄 Iniciando otimização de contexto ({args.tokens} tokens)..."], check=False) except Exception: pass print(f"Creating archive for session {args.session_id} ({args.tokens} tokens)...") filepath, timestamp = create_archive(args.summary, args.tokens, args.session_id) ``` The resulting content is written to long-term memory: ```python with open(MEMORY_FILE, 'w', encoding='utf-8') as f: f.write(final_content) ``` ### Technical Analysis The required `--summary` argument is treated as trusted content. It is stored in a JSON archive and subsequently interpolated verbatim into `~/openclaw/MEMORY.md`. Because no Markdown escaping, structural validation, trust labeling, or instruction filtering is applied, a caller can include additional headings, directives, or prompt-like instructions in the summary. Those instructions can escape the intended `- **Summary:**` field and be ...[truncated 1810 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat all summary text as untrusted data rather than agent instructions. 2. Escape Markdown control characters and prevent injected headings, separators, links, and code blocks from altering the memory structure. 3. Store summaries in a structured, non-executable format and render them only as quoted or fenced data. 4. Add a strict schema and maximum length for archive fields. 5. Separate trusted long-term rules from untrusted conversation summaries. 6. Require explicit user approval before promoting generated summaries into long-term memory. 7. Apply prompt-injection detection and reject summaries containing attempts to define agent rules, tool instructions, or authority claims. 8. Mark imported content with clear trust metadata that downstream agents must not interpret as instructions. 9. Sanitize existing archives before rebuilding `MEMORY.md`, because fixing only new input would leave previously stored payloads active. 10. Use atomic writes and maintain a validated backup of the last trusted memory state. ]]>
