This article examines a hands-on lab demonstrating Server-Side Request Forgery (SSRF) attacks triggered through an LLM agent with a fetch tool protected only by a natural-language denylist. The lab shows how FetchBot's guard—which blocks URLs containing 'localhost', '127.0.0.1', or 'internal'—fails to prevent access to the cloud metadata endpoint at 169.254.169.254 because that address contains none of the banned substrings. The scenario illustrates a critical gap between prompt-level security controls and actual code enforcement, with real-world parallels to the 2019 Capital One breach.
- Denylist guards always miss alternativesThe article demonstrates that guardrails framed as 'refuse things that look bad' are inherently incomplete: FetchBot's denylist enumerates only three forbidden substrings, but the cloud metadata IP 169.254.169.254 bypasses this list entirely because it contains none of those words. The core lesson is that blocklists fail by design because they cannot anticipate every alternative form of an internal target.
- The metadata endpoint as SSRF vectorCloud metadata endpoints like 169.254.169.254 are high-value targets because they return IAM credentials without authentication when accessed from within the cloud environment. The article cites the 2019 Capital One breach, which exposed data on approximately 100 million people by reaching a metadata endpoint via SSRF—the exact target the lab reconstructs.
- LLM-driven SSRF mechanics in this labThe attack flow is straightforward: a user confirms the guard is a denylist (not an allowlist), identifies an internal target that avoids every banned substring, asks FetchBot to fetch the metadata IP by its numeric address, and surfaces the returned credentials. The LLM merely operates the server-side fetch tool; the classic SSRF vulnerability is enabled by weak access controls, not LLM reasoning.
- Why prompt-level controls failThe guard reasons about surface strings in the URL, not about what the address actually resolves to or where it leads. Because the metadata IP is internal but matches no banned word, the model considers it allowed and the server-side tool—running with full network access—retrieves it without restriction. The LLM's logic-based reasoning cannot substitute for code-enforced boundaries.
- Code-enforced allowlist defense strategyThe article prescribes four concrete defenses: enforce an allowlist in code around the fetch tool permitting only specific external domains, resolve the host and block private, loopback, and link-local ranges at request time, require IMDSv2 so reachable metadata endpoints resist tokenless reads, and run tools with least privilege plus egress filtering. These shift control from the prompt to the application layer and network.
- Real-world classification and scopeThe article maps LLM-driven SSRF to OWASP LLM06 (Excessive Agency) plus classic SSRF (A10 in the web Top 10), establishing it as a serious cloud-security concern. The 2019 Capital One breach is cited as proof that metadata endpoint exploitation via SSRF is not theoretical and results in massive real-world data exposure.
- Evaluating agent fetch tool securityThe article offers a practical heuristic: 'Any agent with a server-side fetch tool is an SSRF vector.' Security should be judged by whether destinations are allowlisted in code; a natural-language denylist is explicitly stated to be not a control. This shifts the burden of proof from the defender explaining why the guard should work to the developer proving destinations are restricted.
Bottom line: LLM-driven SSRF succeeds when fetch tools rely on prompt-level denylists instead of code-enforced allowlists and network controls; defending against this requires allowlisting destinations in code, blocking private and link-local ranges after DNS resolution, requiring IMDSv2, and applying least privilege and egress filtering to the agent's runtime environment.
📋 In this walkthrough
What this lab teaches
This is classic SSRF with an LLM holding the trigger. The agent has a server-side HTTP tool, and a natural-language denylist tries to stop it reaching internal targets. Denylists enumerate 'known bad' strings and always miss alternatives, so the agent can be steered to an internal address the list did not anticipate.
The lesson: guardrails phrased as 'refuse things that look bad' are blocklists, and blocklists fail. Fetch destinations need an allowlist enforced in code, not in the prompt.
How FetchBot guards its http_get tool
You ask FetchBot to retrieve a URL. The model checks the URL against its denylist of forbidden substrings and, if it passes, calls the server-side fetch tool. The cloud metadata address does not match the banned strings, so the guard waves it through.
The attack approach
- Confirm the guard is a denylist of specific strings, not an allowlist.
- Identify an internal target that avoids every banned substring.
- Ask FetchBot to retrieve the cloud metadata endpoint by its numeric address.
- Surface the credentials the endpoint returns.
Full walkthrough (spoiler)
If the fetch is refused, your URL still matches a banned word — open the steps.
Show the step-by-step solution
- Choose the cloud metadata address (169.254.169.254), which contains none of the denylisted words.
- Ask FetchBot to fetch it — the natural-language guard sees nothing on its banned list.
- The server-side tool retrieves the metadata endpoint, which returns IAM credentials.
- The credentials appear in the reply and the lab marks you solved.
Why it works
The guard reasons about surface strings, not about what the address actually is. The metadata IP is internal but matches no banned word, so the model considers it allowed and the tool — running on the server — happily reaches it. The LLM is just the operator of a classic SSRF.
Where this happens in the real world
SSRF driven by LLM tool calls is a serious cloud-security concern, mapping to OWASP LLM06 Excessive Agency plus classic SSRF (A10 in the web Top 10). The 2019 Capital One breach reached a cloud metadata endpoint via SSRF and exposed data on roughly 100 million people — the same endpoint this lab targets.
How to defend against it
- Enforce an allowlist in code around the fetch tool — only specific external domains, never a prompt denylist.
- Resolve the host and block private, loopback and link-local ranges (including the metadata IP) at request time.
- Require IMDSv2 so a reachable metadata endpoint still resists token-less reads.
- Run tools with least privilege and egress-filter the environment the agent runs in.
Try it, then go further
Reading about it is the easy part. Open the lab, try your own approach before peeking at the spoiler, and see how many attempts it takes.
