EchoLeak, end to end

Lesson 4 of 5 in The Agent Threat Model.

Taxonomies are easy to nod along to. Walk one real chain instead, slowly, and the abstractions turn into design decisions you recognise.

EchoLeak is the best-documented public example, and it is worth your time for one reason above all: the victim did nothing. No link clicked, no attachment opened, no plugin installed, no unusual permission granted. The only requirement was that the target used an agent wired to their own data — which was, of course, the product working as designed.

The chain, step by step — and where it could have been broken

  1. 1 · Attacker emails the victim

    No relationship, no privileges, no user interaction required. The message is phrased as guidance to a person, which helps it read as benign to filters and to a human skimming.

  2. 2 · The email sits in the mailbox

    Nothing executes. The payload is patient: it is waiting for a retrieval system to pick it up. This is what distinguishes indirect injection from a phishing click.

  3. 3 · Victim asks Copilot an unrelated question

    The trigger is ordinary product usage. The victim never sees the attacker’s email and never knows a chain started.

  4. 4 · RAG retrieves the email into context

    BREAK HERE: retrieval trust tiering. Untrusted external mail and privileged internal documents entering the same context window with the same status is the actual defect.

  5. 5 · LLM scope violation

    Untrusted text steers the model to attend to privileged data — Aim’s term. The model has no way to weigh instruction provenance, exactly as lesson one describes.

  6. 6 · Sensitive context pushed to an external destination

    BREAK HERE: egress control. Every disclosed chain of this family ends at an outbound channel the attacker can observe. Restricting destinations is the most reliable single fix.

  7. 7 · Data gone, no user-visible event

    Zero-click means zero signal for the victim. Detection has to come from traces and egress logs, because there is no human in the loop to notice.

EchoLeak mapped onto the trifecta — and the containment for each leg
Trifecta legIn EchoLeakContainment that would have cut itCost of that containment

Leg 1 — private data

Everything Copilot could retrieve for that user through Microsoft Graph: mail, OneDrive, SharePoint, Teams history.

Task-scoped retrieval instead of identity-scoped: narrow the corpus per request, and keep the highest-sensitivity sources out of contexts that also ingest external content.

High. This leg is the product value — which is why vendors rarely cut it, and why the other two legs carry the defence.

Leg 2 — untrusted content

Inbound email from an arbitrary sender, retrieved as context. Unremovable: an inbox is an attacker-writable data store by definition.

Trust tiering in the retriever — label external-origin chunks, keep them out of the same context as privileged material, or process them in a separate session.

Moderate, and mostly engineering. This is the leg most teams have not even inventoried.

Leg 3 — external communication

The outbound channel the chain used to move data out of the tenant.

Egress control: destination allowlists, no rendering of model-authored URLs, no outbound requests from a context that has touched untrusted input.

Low. This is why real-world fixes across this whole family have concentrated here — Willison notes vendors usually respond by locking down the exfiltration vector.

The action itself

Zero-click: no human ever saw a decision point.

A human or a deterministic validator in front of any egress from a privileged context — the Rule of Two answer when you cannot cut a leg.

Real UX cost, which is exactly why it must be reserved for the actions that close a chain rather than sprayed across every tool.

Now the part that makes EchoLeak worth studying rather than merely worth reading: it is not special. Change the untrusted surface and the egress channel and you have a different named disclosure with the same skeleton. Each of the following was reported by researchers, each was fixed, and each maps onto the same three legs.

GitHub MCP “toxic agent flow” — Invariant Labs, 26 May 2025

A malicious issue in a user’s public repository carries injected instructions. The user later asks their agent to review open issues; the agent pulls private-repository data and leaks it into a pull request on the public repo. The demo — Claude Desktop with Claude 4 Opus — leaked a private project name, relocation plans and salary details.

Invariant stated explicitly that this was not a flaw in the GitHub MCP server code: it is an architectural, agent-level issue, which means no server-side patch could fix it. Their proposed mitigations were granular least-privilege controls such as one-repository-per-session policies, plus continuous monitoring of agent-to-MCP traffic. They also observed that many users click "Always allow" on tool confirmations, deleting the human checkpoint they were relying on. Legs: private repos + attacker-writable issues + a public PR as the egress channel.

ShadowLeak — Radware, 18 September 2025

A zero-click, service-side exfiltration flaw in ChatGPT’s Deep Research agent when connected to Gmail and web browsing: a single crafted email carried injection instructions hidden in HTML (tiny fonts, white-on-white text), and the agent collected inbox PII and sent it, Base64-encoded, to an attacker URL from OpenAI’s own cloud infrastructure.

That last detail is the lesson. Because the request originated service-side rather than from the victim’s browser, it was invisible to local and enterprise network defences — so "we monitor egress at our perimeter" was not a control here. Reported via Bugcrowd on 18 June 2025; fixed by OpenAI in early August 2025 and acknowledged as resolved on 3 September 2025. Legs: mailbox + inbound email + an outbound request from the vendor’s infrastructure.

AgentFlayer — Zenity Labs, 6 August 2025

Against ChatGPT Connectors (Google Drive, SharePoint, GitHub and others): a document poisoned with an invisible injection — 1px white font — is uploaded by the victim for summarisation. The injection directs the agent to search the connected Drive for API keys and embed them as URL parameters in a rendered image, which fires a request to the attacker’s endpoint with no click.

The defence-evasion detail matters: Zenity bypassed OpenAI’s url_safe image-rendering mitigation by hosting the exfil image on a major cloud’s blob storage and reading the leaked parameters out of its request logs. Allowlisting a reputable domain is not egress control if attackers can rent space on it. Legs: Drive connector + uploaded document + image-rendering channel.

CamoLeak — Legit Security, 8 October 2025

In GitHub Copilot Chat: an injection hidden in a pull-request description using GitHub’s own invisible comments feature, combined with a bypass of GitHub’s content-security policy that abused Camo, GitHub’s own image proxy. Because Camo only fetches GitHub-signed URLs, the researcher pre-generated a signed URL per character and had Copilot render leaked text as an ordered sequence of invisible images. Rated CVSS 9.6 by Legit Security (their own score; no public CVE record was found). GitHub’s fix was to disable image rendering in Copilot Chat entirely.

The structural point: Copilot "operates with the same permissions as the user", so everything the victim could read was leakable. That sentence describes most agents you will build. Legs: private repos + PR content + a signed-proxy image channel.

ForcedLeak — Noma Labs, 25 September 2025 · GitLab Duo — Legit Security, 22 May 2025

ForcedLeak (Salesforce Agentforce, rated CVSS 9.4 by Noma Security): an indirect injection arrives in the Description field of a public Web-to-Lead form; when an employee later asks the agent about that lead, CRM data is exfiltrated via an image URL to a domain that was still on Salesforce’s CSP img-src allowlist but had expired and become purchasable. Salesforce re-secured the domain and shipped patches enforcing Trusted URLs. Allowlists need an owner and an expiry review.

GitLab Duo: hidden prompts planted in merge-request descriptions, comments, commit messages, issue descriptions and source code were all ingested by Duo; a single hidden comment sufficed to leak private source code and inject untrusted HTML into responses. GitLab’s fix blocked Duo from rendering unsafe tags such as img or form pointing outside gitlab.com — leg 3 again. Note the pattern across these two and CamoLeak: code-review and CRM surfaces are attacker-writable input, and HTML/image rendering is a general-purpose exfiltration channel.

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.