Module 4 · 7 min read
Exfiltration Channels and Zero-Click Egress
How injected instructions turn into data leaving the building, why rendered markdown images are the archetypal zero-click channel, and where the controls that actually work are located.
Data exfiltration through an AI feature requires three things at once: access to something worth taking, attacker instructions in the context, and an egress path out of the system. Analysing an incident or a design in terms of those three legs is a productive habit, because it immediately suggests where to intervene. You cannot reliably remove the second leg, since that is the unsolved injection problem. So the design work concentrates on the first and third: which private data is in reach, and which paths out exist.
- Name the private data in reachWhat is in this context or reachable by the mounted tools? Conversation history, retrieved documents, mailbox contents, records the tool credential can read. If the answer is nothing sensitive, the rest of the analysis is short.
- Assume the instructions leg is already lostDo not spend the analysis arguing about whether a payload can get in. Assume it did. This is the leg you cannot close, and treating it as closed is how designs end up with no boundary at all.
- Enumerate every egress pathCount anything that can cause bytes derived from the context to reach an outside party: rendered resource fetches, link previews, tool calls that make outbound requests, writes into stores others can read, and DNS lookups triggered by any of the above.
- Place a deterministic gate on each pathFor each egress path, identify the code outside the model that constrains it: a renderer allowlist, an egress proxy, a recipient allowlist at the gateway, or the simple absence of the tool from this session.
- Re-test by removing one leg at a timeA control is only load-bearing if removing the leg it guards actually breaks the chain. If the data still gets out with that control in place, you have found a path you had not enumerated.
The archetypal channel is the rendered image reference. A chat interface renders model output as Markdown, so when the model emits an image reference the client fetches that URL to display it. An injected instruction makes the model emit an image whose URL points at an attacker-controlled host and whose path or query string encodes the conversation history, retrieved documents or anything else in the context. What makes this notable is that the victim's own client performs the fetch automatically on render. There is no click, no visible action and nothing for the user to decline. The model does not need a browsing tool, because the model is not doing the fetching. The attacker does not need to have compromised the chat server, because the payload rode in on ordinary content. And the interface may show nothing at all except a broken image icon.
Model is induced to emit markdown of this shape:

Renderer sees an image, issues a GET, and the query string arrives in the
attacker's access log. Zero clicks.
Variants worth checking for in any renderer review:
- autolinked bare URLs
- link targets the user is socially nudged to click
- <img>, <iframe>, <object> if the renderer allows raw HTML
- CSS background-image or webfont references in rich renderers
- favicon, preview-card and link-unfurl fetches performed server-side
- any resource fetch that triggers a DNS lookup on an attacker domainBeyond the renderer, egress paths multiply as soon as tools appear. A mail-send tool is an obvious channel. So is a web-fetch tool, which can carry data in a request it makes on the attacker's behalf. So is a webhook, an outbound API call, a ticket created in a system the attacker can read, a comment added to a shared document, a file written to a synced folder, a calendar invite sent to an external address, a DNS lookup triggered by any resource load, and in some designs even an error message that echoes context back to a caller. When you inventory egress, count anything that can cause bytes derived from the context to reach a party outside the trust boundary.
| Channel | Needs a user action | Needs a tool |
|---|---|---|
| Rendered image or remote resource reference | No | No |
| Link the user is persuaded to click | Yes | No |
| Server-side link unfurl or preview fetch | No | No |
| Outbound mail, webhook or API call | No | Yes |
| Write into a store the attacker can read | No | Yes |
The controls that work are the ones sitting in the render path and the egress path, because those are enforced by code the model does not influence. In the renderer: a strict allowlist or content security policy limiting which domains the client will fetch images and links from; disabling automatic fetching of external resources so that loading requires an explicit user gesture; and rewriting or stripping URLs in model output server-side before render, keeping only references you already know to be safe. In the network path: an egress proxy with a destination allowlist for any tool that makes outbound requests, and a mail gateway that enforces an allowlist of permitted recipients. In the architecture: not mounting egress-capable tools at all in a session that processes untrusted content.
Check yourself
A team disables image rendering entirely in their chat interface after an exfiltration finding. Data still reaches an attacker-controlled host when a manipulated response is displayed, with no user interaction. What is the most likely remaining channel?
Removing one tag does not remove the class. Any renderer feature that fetches a remote resource to display something, including link previews and unfurls generated client-side or server-side, performs the same outbound request. The control has to be a destination allowlist over all remote fetches, not the removal of a single element type.
Controls that hold
- A destination allowlist or content security policy governing every remote fetch the client performs.
- No automatic fetching of external resources: loading requires an explicit user gesture.
- Server-side rewriting or stripping of URLs in model output before render, keeping only known-safe references.
- An egress proxy allowlist for tools, and a recipient allowlist enforced at the mail gateway.
- Not mounting egress-capable tools in a session that processes untrusted content.
Controls that only look like controls
- A system-prompt rule that the model must never include URLs in its responses.
- Switching the interface from Markdown to full HTML rendering.
- Upgrading to a model with stronger safety training.
- Verbose audit logging of every tool call and rendered response.
- Blocking one element type, such as images, while other remote fetches remain enabled.
Apply the three-leg framing to a worked example. An email agent has access to a private inbox, exposure to attacker-supplied message content, and the ability to send mail autonomously. Requiring explicit human confirmation showing recipient and body before any outbound send gates the egress leg at execution. Processing untrusted inbound content in a session with no send-capable tools mounted removes the egress leg entirely for that session. Restricting outbound sends to an allowlist of internal recipients, enforced at the mail gateway rather than in the prompt, constrains the egress leg to destinations that do not help the attacker. Each of those independently breaks the chain, and each is enforced outside the model. Better safety training and more logging do neither.