Indirect injection: the kill chain
Lesson 3 of 5 in Prompt Injection: The Vulnerability With No Patch.
Now remove the attacker from the conversation. In indirect prompt injection the hostile instructions are planted in content the agent will read while doing its job — a web page, an email, a PDF, an issue description, a calendar invite, a tool result. The victim does nothing wrong. They ask their agent an ordinary question, and the agent obeys a stranger.
The technique was named and systematised by Greshake et al. (arXiv:2302.12173, February 2023), “Not what you’ve signed up for: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection”. Their framing is the one that matters for agents: an attacker can compromise an application remotely by planting prompts in data the model is likely to retrieve, blurring the data/instruction boundary and enabling data theft, worming, and contamination of the information ecosystem. Their demonstration targets included Bing Chat and code-completion tools.
This is the variant that scales. The attacker needs no account, no session, and no idea who the victim is — only the ability to write text that will eventually be retrieved.
The indirect-injection kill chain — and where each link breaks
- Attacker plants instructions in retrievable content
An email anyone can send, a public issue, a lead form, a shared doc, a wiki edit, a web page. Break here: treat every field a stranger can write as untrusted, and reduce the surface — but accept you cannot own the internet.
- Agent retrieves it — RAG hit, tool call, inbox scan
Retrieval is triggered by the victim’s legitimate request. Break here: scope retrieval to sources with known writers; label provenance; keep untrusted corpora out of contexts that also hold secrets.
- Content lands in context as ordinary tokens
No provenance channel exists, so the planted text sits alongside your system prompt. Injections need not be human-visible — one-pixel fonts, white-on-white text, HTML comments, and invisible-comment features all parse fine.
- Detection layer flags it?
Classifiers, spotlighting, provenance heuristics. Valuable — and probabilistic. Design the rest of the chain assuming this node says “no”.
- Model treats planted text as an instruction
Aim Labs calls this LLM scope violation: untrusted input steering the model to attend to trusted, privileged data without user consent. There is no reliable control at this node — which is the whole point of the module.
- Agent calls a tool with the user’s privileges
The strongest break. The tool the injection needs may simply not exist in this session, or be read-only, or be scoped to one repo, or require an approval. Least privilege is the only control the attacker cannot talk their way past.
- Data leaves via an outbound channel
Rendered image URL, markdown link, HTTP request, a PR on a public repo, an outbound email. Break here: egress control — allowlist destinations, strip or refuse remote image/link rendering. This is what most vendors actually patched.
- Attacker receives the data
Often just their own web-server logs. Note the victim sees a normal, helpful answer — which is why traces and egress logs, not user reports, are how you find this.
The agent-specific twist is that tool output is content too. When an agent calls a tool, the result comes back into the same undifferentiated stream — so anything that can shape a tool result can inject. That is tool-output poisoning: a search result whose snippet carries instructions, an API response with a hostile error_message, a scraped page, a file listing, a database row a customer filled in. With MCP the surface widens again, because the tool definitions themselves — names and descriptions written by whoever published the server — enter the context before any call happens.
Willison’s warning is the practical one: mixing tools from different sources raises risk precisely because each new server is a new writer of text your model trusts. The supply-chain module takes that apart; here, hold one rule — a tool result is untrusted input, and privileged actions must never be authorised by it.
| Surface | Who can write to it | Containment that actually holds |
|---|---|---|
Inbound email | Anyone on the internet, unauthenticated. | Do not put mail retrieval in the same session as broad private-data access plus an outbound channel; strip active content; log egress. |
Web pages the agent browses | Anyone who can publish, plus anyone who can influence what ranks. | Browse in a sandbox with no credentials, allowlist destinations, and keep fetched text out of privileged sessions. |
Issues, PR descriptions, code comments | Any contributor — and on public repos, any stranger. | One-repo-per-session scoping, read-only tokens for review tasks, no auto-publishing of agent output. |
Uploaded documents and CRM fields | Customers, leads, vendors — often through a public form. | Treat every user-supplied field as hostile regardless of length; restrict where agent output may be sent, by allowlist rather than by reputation. |
Tool results (search, API, file, DB) | Whoever controls the upstream data or service. | Never let a tool result authorise a privileged action; validate structure; keep results out of the decision path for money, deletion, or identity. |
MCP tool names and descriptions | Whoever published or updated the server. | Pin versions, review descriptions on change, isolate servers by trust tier — see the supply-chain and MCP security module. |
Agent memory and shared scratchpads | Any earlier session, any collaborating agent — including a poisoned one. | Treat memory writes as an action needing validation; expire and scope memory; never store instructions retrieved from untrusted content. |
Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.