SSRF Through a Loopback-Trusting Guard to Reach an Internal KV Store
The challenge
This health-check fetcher has a guard: it blocks external hosts but happily allows loopback, because "only internal services live there." That assumption is the bug. Probe the loopback interface, follow the hint the server leaks when you knock on the wrong door, and pull the flag out of the internal key-value store it points you to.
What you'll learn
- Recognize why a loopback-allow SSRF guard is not a security boundary
- Enumerate services listening on the loopback interface through an SSRF primitive
- Read error responses for leaked internal detail (service name, port, URL shape)
- Pivot from a 403 hint to an internal Consul KV API and read a stored secret
- Explain why a positive allow-list of targets and authenticated internal services are the real fixes
Skills tested
Prerequisites
- Solid understanding of HTTP requests, URLs, ports, and the loopback interface
- Familiarity with SSRF as a class and how it reaches internal endpoints
- Awareness that internal infrastructure (service meshes, KV stores) exposes HTTP APIs
How it works
This is a layered server-side request forgery (SSRF). The fetcher does have a guard, but it is a deny-list: it rejects external hosts and waves through anything on loopback, on the assumption that only trusted internal services live there. That assumption is the vulnerability. A deny-list of external hosts says nothing about what is co-located on the box - and modern hosts run a lot: a service-mesh agent, a metadata endpoint, an admin API, a key-value store. Any of those becomes reachable the moment loopback is allowed.
The attack is therefore not a single flip but a small chain of discovery. Probing an admin path on loopback returns a 403, but the error body talks too much: it names a Consul agent on port 8500 and describes the /v1/kv/<key> URL shape its secrets use. Consul's KV HTTP API is unauthenticated by default in many deployments, so listing the keyspace and then reading /v1/kv/flag hands over the stored secret. Each step is informed only by the response to the previous one - this is what makes it a realistic internal-discovery exercise rather than a one-parameter flip.
The root cause is treating where a request goes as a proxy for whether it is allowed. Loopback is not a trust boundary, and internal services that assume the network protects them fall the instant an SSRF reaches them.
Common mistakes
- Trying external URLs and concluding the endpoint is safe when the guard blocks them - the whole point is that loopback is still allowed.
- Skimming past the
403on/admininstead of reading its body, which names the Consul agent, its port, and the KV URL shape. - Assuming an internal infrastructure API like Consul KV requires authentication, when its HTTP API is frequently exposed without it on loopback.
- Reading the KV listing and stopping, rather than following it to the specific
/v1/kv/flagkey that holds the secret.
How to defend against it
Replace the deny-list with a positive allow-list of exact, intended targets, and never treat loopback as inherently safe. Combine that with authentication on every internal service so a network reach alone is not enough.
- Allow only the specific hostnames and paths the fetcher legitimately needs; reject everything else, including loopback and link-local, unless explicitly required.
- Resolve and re-check the destination after DNS resolution to defeat rebinding, and forbid redirects to disallowed targets.
- Require authentication and ACLs on internal APIs - enable Consul ACLs, bind the agent to a restricted interface, and never expose the KV API without a token.
- Make error responses generic: never leak internal service names, ports, or URL schemes in a client-facing error body.
Full solution
Community stats
Related Daily Hacks
Credited contributors
These hackers reported issues, improved content, and helped harden this page.