T02 · Agent Memory Poisoning
- Location
scripts/extract.py:175- Finding
Untrusted Conversation Content Can Poison Persistent Agent Instructions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/extract.py:175-178,scripts/persister.py:78-91, andscripts/persister.py:104-120
Vulnerability Type: Persistent agent-memory instruction injection
Risk Level: HighVulnerable Code
scripts/extract.py:175-178classifies conversation text containing system-related keywords as a system rule and assigns instruction-bearing files as persistence targets:python elif any(kw.lower() in content for kw in KEYWORD_CATEGORIES["system"]): fact["category"] = "system" fact["targets"] = ["tools_md", "agents_md"]scripts/persister.py:78-91appends the complete, unescaped conversation text directly toTOOLS.md:python def append_to_tools(fact: Dict, verify: bool = True) -> None: """Append system rule to TOOLS.md.""" path = Path("/home/admin/.openclaw/workspace/TOOLS.md") if not path.exists(): return entry = f"\n> **[{datetime.now().date()}]** {fact['content']}" with open(path, "a", encoding="utf-8") as f: f.write(entry) f.flush() if verify: with open(path, "r", encoding="utf-8") as f: assert fact["content"] in f.read()scripts/persister.py:104-120routes facts classified as system rules to that unsafe persistence sink:python def persist_facts(facts: List[Dict], config: Dict) -> None: """Route facts to their target files.""" for fact in facts: targets = fact.get("targets", ["memory_md"]) for target in targets: if target == "ontology": update_ontology(fact) elif target == "warm_memory": append_to_warm_memory(fact, verify=config["persistence"]["verify_after_write"]) elif target == "user_md": update_user_md(fact, verify=config["persistence"]["verify_after_write"]) elif target == "memory_md": ...[truncated 3017 chars]- Remediation
View remediation
Remediation Suggestions
- Do not automatically convert ordinary conversation content into persistent agent rules. Disable routing to
TOOLS.mdandAGENTS.mdfor automatically extracted facts. - Store extracted system-related statements in a non-instructional data file using a strict schema containing fields such as
content,source,timestamp,author,confidence, andapproved. - Require explicit confirmation from an authenticated, trusted administrator before promoting any extracted statement into an instruction-bearing file.
- Implement a restrictive allowlist of supported rule types and serialize approved values from predefined enums or templates rather than writing free-form text.
- Reject or quarantine content containing prompt-control language, role declarations, tool directives, embedded Markdown instructions, links, or requests to ignore existing policies.
- Clearly delimit stored conversation text as untrusted data and ensure the agent's instruction hierarchy explicitly prohibits interpreting memory records as commands.
- Add provenance and integrity controls so entries derived from conversation logs cannot be confused with administrator-authored rules.
- Use atomic writes, backups, and an audit log for all changes to instruction-bearing files.
- Add security tests proving that messages containing system keywords cannot modify
TOOLS.md,AGENTS.md, or other files loaded as agent instructions without explicit approval. - Review and remove any existing automatically generated entries from
TOOLS.mdthat cannot be tied to an authorized administrative action.
- Do not automatically convert ordinary conversation content into persistent agent rules. Disable routing to
