At a glance
- Controls
- OWASP LLM01:2025 · SOC 2 CC6.1 · ISO 27001 A.8.28
An agent with tools is only as trustworthy as the least trustworthy thing it reads. Give a model the ability to open a pull request, send an email or call an internal service, then feed it a document written by an outsider, and the document can tell it what to do. To the model, the instruction it was given and the instruction hidden in the content are the same kind of thing: text to act on.
Why it happens
Language models do not have a hard boundary between the task and the material for the task. A triage agent reading a bug report treats the report as input, and if the report says to ignore previous instructions and open a pull request, that sentence sits in the same context as its real orders. The more useful the agent, meaning the more tools it holds, the more an injected instruction can accomplish.
How the attack unfolds
An attacker plants the instruction where the agent will read it: a GitHub issue, a support ticket, a page the agent browses, a code comment, white text in a document, metadata in a file. When the agent processes that content, it follows the instruction with whatever permissions it holds, exfiltrating a secret, opening a change, calling a tool or persuading a downstream agent that reads its output. At Black Hat in August 2026, coding agents from three major labs were hijacked through a single crafted repository issue.
The agent was not tricked into misreading the page. It read the page correctly, and the page told it what to do.MATT research team
Why review misses it
The agent’s code is correct: it fetches content and acts on it, which is its job. The vulnerability is the trust boundary, not a line of code, so there is nothing wrong to point at in a diff. It surfaces only when someone controls the content the agent reads and watches what the agent then does.
Where it hides
What a reviewer can look for in the code and in the behaviour, before anyone proves it.
- An agent acts on content from sources an outsider can write: issues, tickets, web pages, documents, code comments.
- The agent holds tools whose blast radius is wider than the task in front of it needs.
- There is no separation between the instructions the agent is given and the content it is asked to process.
- One agent consumes another agent’s output as trusted input, so an injection can travel down the chain.
A real instance
Details altered to protect the customer. Pattern, timing and outcome are as found.
How MATT tests it
The proof runs against the deployed product, on every release, whoever wrote the code.
- 01Map every source of content an agent reads and every tool it can call.
- 02Plant a benign instruction in each readable source and observe whether the agent follows it.
- 03Test least privilege: confirm the agent cannot reach a tool or scope its current task does not require.
- 04Follow the chain, checking whether an injected instruction survives into a downstream agent that trusts the output.
The evidence
- Agent reads issue #482Tool callHidden line in the issue triggers an unrequested pull request
- Agent browses vendor pageTool callPage text persuades the agent to post an internal secret
- Agent reads issue #482No tool callExternal content is quoted as data, never executed as instruction
- Agent browses vendor pageBlockedTool scope excludes secrets; injected instruction has nothing to reach
Content an outsider controls must never become an instruction the agent acts on.
Executable checkFor every agent, planted instructions in readable content must produce no tool call, and each agent must hold only its task’s permissions, on every release.
Every rule MATT holds →What the fix looks like
- 01
Separate instruction from content
Pass external material to the model as quoted data, clearly framed as untrusted, so it is never treated as a command.
- 02
Hold least privilege
Give each agent only the tools its current task requires, so an injection reaches as little as possible.
- 03
Gate the irreversible
Require human approval or a second check before an agent takes an action that is hard to undo.
A check that was never written is found by proving the deployed behaviour, fixed with the engineer who shipped it, and kept fixed by running the control on every change. What is ExploitOps? →