Add the ssrf module
This commit is contained in:
49
ssrf/docs/02-the-exploit.md
Normal file
49
ssrf/docs/02-the-exploit.md
Normal 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 →](03-allow-not-block.md)
|
||||
Reference in New Issue
Block a user