Claude web_fetch’s exfiltration guard had a nested-link hole

By Marcus Webb

Simon Willison highlighted a security break in Anthropic Claude’s web_fetch design after Ayush Paul demonstrated a way to exfiltrate private user data through nested links. The issue matters because the existing URL allowlist blocked obvious outbound leaks, but it did not fully close the indirect prompt-injection path.

The intended boundary around web_fetch

Anthropic’s documented guardrail is simple: web_fetch can only load URLs that the user explicitly supplied or that first appeared via web_search. That blocks the most direct version of a leak, where a prompt-injected page tells Claude to synthesize private context into a fresh attacker-controlled URL.

That protection is stronger than the usual “agent can browse anything” setup because it removes arbitrary outbound navigation from the model’s control. In practice, it turns exfiltration into a deterministic policy problem instead of a pure prompting problem.

The weakness was that the fetch tool could also follow URLs embedded in pages it had already fetched. That created a second-order trust channel: once Claude loaded a hostile page, that page could point to another hostile page, which could point to another, and so on.

Nested links as an exfiltration channel

Ayush Paul’s attack used a honeypot site that presented itself as an access-controlled service and then steered Claude through a sequence of attacker-hosted URLs. The page text instructed the agent to navigate “letter by letter,” effectively turning the model into a crawler that could be guided into assembling a data-leaking path from individually safe-looking steps.

That matters because each hop can look innocuous in isolation. A single URL like https://coffee.evil.com/a is not obviously an exfiltration endpoint. But a sequence of nested pages can encode branching logic, state, and payload assembly without ever asking Claude to construct a suspicious final URL directly.

The attack succeeded in extracting the user’s name, home city, and employer. The pages were also conditioned on the Claude-User user-agent, which made the trap less visible during manual inspection and suggests the attacker was optimizing for agent-only exploitation rather than human clickthrough.

Why the defense failed in practice

The security model assumed that if Claude cannot generate arbitrary URLs, then it cannot leak private context through browsing. That assumption breaks once the browsing surface itself becomes stateful and attacker-controlled. The model no longer needs permission to invent a destination if the destination can be discovered by following attacker-authored breadcrumbs.

This is the same class of failure that keeps showing up in agent security: the attack does not need to override policy directly, it only needs to route the agent through policy-compliant steps until the composed behavior becomes unsafe. If tool output is fed back into context without a hard trust boundary, then the tool itself becomes an instruction channel.

Anthropic reportedly did not pay a bug bounty because they had already identified the issue internally. That suggests the hole was recognized as a real class of weakness, not a one-off prompt trick.

What should change in agent browsing defenses

For ML systems that let models fetch web content, the important design point is not “can the model type a bad URL.” It is “can untrusted content influence the next request target at all.” If the answer is yes, then the browser stack still has an attacker-controlled navigation oracle.

A stronger implementation would treat fetched pages as untrusted data, not as sources of new navigation authority unless those URLs are independently vetted against the same source-of-truth policy as initial URLs. In other words, tool chaining needs a trust lattice, not just a per-request URL check.

For production agents, I would want at least three controls:

  • strict provenance on every next-hop URL
  • user-visible confirmation for any navigation that originated in fetched content
  • outbound request auditing that records the exact causal chain from prompt to fetch target

The practical lesson is that exfiltration defenses must be evaluated over the whole tool loop, not just on the syntax of a single outbound request. Once a model can read hostile pages and act on them, “allowed URL” checks are necessary but not sufficient.

Sources

Further articles