1
0

Add the ssrf module

This commit is contained in:
2026-08-29 09:31:09 +05:30
parent 0fceb2cd4e
commit 4e37a54e92
28 changed files with 1391 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
[← What SSRF costs you](01-what-ssrf-costs-you.md) · [Module README](../README.md) · [Allow, not block →](03-allow-not-block.md)
# 2. The exploit, start to finish
Run the application with no profile, so there is no `InetAddressFilter` bean at all:
```bash
./scripts/run.sh
./scripts/exploit.sh
```
The transcript is committed at
[`docs/output/exploit-by-profile.txt`](output/exploit-by-profile.txt). The first block is this
one:
```
http://127.0.0.1:8080/internal/credentials FETCHED | {"SecretAccessKey":"wJalrXUtnFEMI-...
http://localhost:8080/internal/credentials FETCHED | {"SecretAccessKey":"wJalrXUtnFEMI-...
http://[::1]:8080/internal/credentials FETCHED | {"SecretAccessKey":"wJalrXUtnFEMI-...
http://172.16.10.3:8080/internal/credentials FETCHED | {"SecretAccessKey":"wJalrXUtnFEMI-...
http://example.com/ FETCHED | <!doctype html>...
```
Four targets, four sets of credentials, and the legitimate outbound call still works. That last
row matters as much as the others: any mitigation has to leave it intact, and one of the
configurations in [chapter 3](03-allow-not-block.md) does not.
## Why four targets and not one
`127.0.0.1` is the one every tutorial blocks. The others are why a hand-written check usually
leaks:
| Target | What it defeats |
|---|---|
| `localhost` | checks written against the literal string `127.0.0.1` |
| `[::1]` | checks that only ever consider IPv4 |
| `172.16.10.3` | checks that stop at loopback and forget RFC 1918 |
The fourth is this container's own address on its network interface. It is the same process,
reached the same way, over a route that a loopback-only rule does not cover. In a real
deployment it is the pod next door.
Blocking by string is hopeless in a way that is easy to underestimate. `0x7f.0.0.1`,
`2130706433`, `127.1`, a DNS name you control that resolves to `127.0.0.1`, and a redirect from a
public URL to a private one all reach loopback without the string `127.0.0.1` appearing anywhere
in the request. This is why the check belongs at address-resolution time and not in a validator
over the URL — which is exactly what `InetAddressFilter` is.
[Allow, not block &rarr;](03-allow-not-block.md)