Einführung in SSRF
Mögliche Angriffe und was man (trotz PHP) dagegen tun kannMalte WesselsÜber mich
• Doktorand @ TU Braunschweig• Beisitzer @ Datenanfragen.de e.V.
• CTF @ CyberTaskForce Zero

https://www.heise.de/news/KI-Tool-Kritische-Sicherheitsluecken-in-TorchServe-9325638.html

https://www.volexity.com/blog/2021/03/02/active-exploitation-of-microsoft-exchange-zero-day-vulnerabilities/
https://www.heise.de/news/KI-Tool-Kritische-Sicherheitsluecken-in-TorchServe-9325638.html

https://www.volexity.com/blog/2021/03/02/active-exploitation-of-microsoft-exchange-zero-day-vulnerabilities/
https://www.heise.de/news/KI-Tool-Kritische-Sicherheitsluecken-in-TorchServe-9325638.html

https://www.bleepingcomputer.com/news/security/newest-ivanti-ssrf-zero-day-now-under-mass-exploitation/

SSRF: Server-Side Request Forgery

SSRF: Server-Side Request Forgery
SSR : Server-Side Request

SSRs in echt

SSRs in echt

SSRs in echt

Beispiel• Wir zeigen Link auf unserer Seite (example.org) an• Dafür eine Link-Preview• Damit es einfach bleibt: nur den Titel• https://winterkongress.ch → „Winterkongress 2024“

<?php$url = $_GET["url"];$content = file_get_contents($url);
preg_match("/\<title\>(.+)\<\/title\>/", $content, $matches);echo $matches[1];https://example.org/?url=https://winterkongress.ch<?php$url = $_GET["url"];$content = file_get_contents($url);
preg_match("/\<title\>(.+)\<\/title\>/", $content, $matches);echo $matches[1];https://example.org/?url=https://winterkongress.ch<?php$url = $_GET["url"];$content = file_get_contents($url);
preg_match("/\<title\>(.+)\<\/title\>/", $content, $matches);echo $matches[1];https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch<?php$url = $_GET["url"];$content = file_get_contents($url);
preg_match("/\<title\>(.+)\<\/title\>/", $content, $matches);echo $matches[1];https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch<!DOCTYPE html><html lang="de"><head> […]<title>Winterkongress 2024</title>[…]

<?php$url = $_GET["url"];$content = file_get_contents($url);
preg_match("/\<title\>(.+)\<\/title\>/", $content, $matches);echo $matches[1];https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch<!DOCTYPE html><html lang="de"><head> […]<title>Winterkongress 2024</title>[…]

<?php$url = $_GET["url"];$content = file_get_contents($url);
preg_match("/\<title\>(.+)\<\/title\>/", $content, $matches);echo $matches[1];https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch

Winterkongress 2024<!DOCTYPE html><html lang="de"><head> […]<title>Winterkongress 2024</title>[…]

<?php$url = $_GET["url"];$content = file_get_contents($url);
preg_match("/\<title\>(.+)\<\/title\>/", $content, $matches);echo $matches[1];https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch

Winterkongress 2024

Server-SideRequest<!DOCTYPE html><html lang="de"><head> […]<title>Winterkongress 2024</title>[…]

Was kann da so schief gehen?

😈

Was kann da so schief gehen?

😈

Server-Side-Request-Forgery (SSRF)

It allows an attacker to coerce the application to send a
crafted request to an unexpected destination
https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/

Hinter der Firewall

DB, etc.

FirewallNicht alleine im Netzwerk?

DB, etc.
😈
FirewallNicht alleine im Netzwerk?

DB, etc.
😈
FirewallNicht alleine im Netzwerk?

DB, etc.
😈
FirewallNicht alleine im Netzwerk?

DB, etc.
😈
FirewallNicht alleine im Netzwerk?

DB, etc.
😈
FirewallNicht alleine im Netzwerk?

DB, etc.
😈
FirewallNicht alleine im Internet?

https://example.or
g/?url=https://tar
get.com/😈

Nicht alleine im Internet?

https://example.or
g/?url=https://tar
get.com/😈

https://target.com/

Nicht alleine im Internet?

https://example.or
g/?url=https://tar
get.com/😈

https://target.com/

Was machen wir jetzt dagegen?
• Netzwerkrequests finden und absichern

Was machen wir jetzt dagegen?
• Netzwerkrequests finden und absichern

https://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen

Unerwartete Funktionalität• Es gibt viele Funktionen die URLs als „Datei“ akzeptieren

Unerwartete Funktionalität• Es gibt viele Funktionen die URLs als „Datei“ akzeptieren• getimagesize(), file_get_contents(), …

Unerwartete Funktionalität• Es gibt viele Funktionen die URLs als „Datei“ akzeptieren• getimagesize(), file_get_contents(), …
• get_headers() macht GET request

Unerwartete Funktionalität• Es gibt viele Funktionen die URLs als „Datei“ akzeptieren• getimagesize(), file_get_contents(), …
• get_headers() macht GET request• opendir() öffnet auch remote Verzeichnisse

Unerwartete Funktionalität• Es gibt viele Funktionen die URLs als „Datei“ akzeptieren• getimagesize(), file_get_contents(), …
• get_headers() macht GET request• opendir() öffnet auch remote Verzeichnisse• Früher: Requests durch XML parser• „External Entity“Verteidigung auf Stringebene?

Verteidigung auf Stringebene?
• str_contains($input, "localhost")

Verteidigung auf Stringebene?
• str_contains($input, "localhost")• 127.0.0.1

Verteidigung auf Stringebene?
• str_contains($input, "localhost")• 127.0.0.1• str_contains($input, "127.0.0.1")

Verteidigung auf Stringebene?
• str_contains($input, "localhost")• 127.0.0.1• str_contains($input, "127.0.0.1")• http://0x7F000001, etc.

Verteidigung auf Stringebene?
• str_contains($input, "localhost")• 127.0.0.1• str_contains($input, "127.0.0.1")• http://0x7F000001, etc.

• Unicode

Verteidigung auf Stringebene?
• str_contains($input, "localhost")• 127.0.0.1• str_contains($input, "127.0.0.1")• http://0x7F000001, etc.

• Unicode⑦

Verteidigung auf Stringebene?
• str_contains($input, "localhost")• 127.0.0.1• str_contains($input, "127.0.0.1")• http://0x7F000001, etc.

• Unicode⑦

Verteidigung auf Stringebene?
• str_contains($input, "localhost")• 127.0.0.1• str_contains($input, "127.0.0.1")• http://0x7F000001, etc.

• Unicode⑦

Domain prüfen

URL

parse

Host

Server-Side-Request

Domain prüfen• Regex? Z.B. /http://.*example\.com/

URL

parse

Host

Server-Side-Request

Domain prüfen• Regex? Z.B. /http://.*example\.com/• http://evil.com?attack=example.com

URL

parse

Host

Server-Side-Request

Domain prüfen• Regex? Z.B. /http://.*example\.com/• http://evil.com?attack=example.com

URL

parse

Host

Server-Side-Request

Domain prüfen• Regex? Z.B. /http://.*example\.com/• http://evil.com?attack=example.com• Besser: parse_url($url, PHP_URL_HOST)

URL

parse

Host

Server-Side-Request

Domain prüfen• Regex? Z.B. /http://.*example\.com/• http://evil.com?attack=example.com• Besser: parse_url($url, PHP_URL_HOST)

URL

parse

Host

Server-Side-Request

Beispiel Allowlist

Beispiel Allowlist
1. $input= "https://app.com/link/page.html"

Beispiel Allowlist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

Beispiel Allowlist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

3. $allowedhosts = ["app.com", "good.example"]

Beispiel Allowlist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)

Beispiel Allowlist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)
5. if ($isAllowed) {file_get_contents($input);}Beispiel Allowlist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)= "app.com"
3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)
5. if ($isAllowed) {file_get_contents($input);}Beispiel Allowlist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)= "app.com"
3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)= true5. if ($isAllowed) {file_get_contents($input);}Beispiel Allowlist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)= "app.com"
3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)= true5. if ($isAllowed) {file_get_contents($input);}Beispiel Allowlist
1. $input
2. $host= "https://localhost/attack"= parse_url($input, PHP_URL_HOST)

3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)
5. if ($isAllowed) {file_get_contents($input);}Beispiel Allowlist
1. $input
2. $host= "https://localhost/attack"= parse_url($input, PHP_URL_HOST)= "localhost"
3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)
5. if ($isAllowed) {file_get_contents($input);}Beispiel Allowlist
1. $input
2. $host= "https://localhost/attack"= parse_url($input, PHP_URL_HOST)= "localhost"
3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)= false
5. if ($isAllowed) {file_get_contents($input);}Beispiel Allowlist
1. $input
2. $host= "https://localhost/attack"= parse_url($input, PHP_URL_HOST)= "localhost"
3. $allowedhosts = ["app.com", "good.example"]
4. $isAllowed= in_array($host, $allowedhosts)= false
5. if ($isAllowed) {file_get_contents($input);}URL Parser Confusion

Beispiel von Orange Tsai @ Blackhat‘17:
https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

URL Parser Confusion• Unterschiede zwischen mehreren Parsern können ausgenutzt werden• http://1.1.1.1 &@2.2.2.2# 3.3.3.3/

Beispiel von Orange Tsai @ Blackhat‘17:
https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

URL Parser Confusion• Unterschiede zwischen mehreren Parsern können ausgenutzt werden• http://1.1.1.1 &@2.2.2.2# 3.3.3.3/

Beispiel von Orange Tsai @ Blackhat‘17:
https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

URL Parser Confusion• Unterschiede zwischen mehreren Parsern können ausgenutzt werden• http://1.1.1.1 &@2.2.2.2# 3.3.3.3/

Beispiel von Orange Tsai @ Blackhat‘17:
https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

URL Parser Confusion• Unterschiede zwischen mehreren Parsern können ausgenutzt werden• http://1.1.1.1 &@2.2.2.2# 3.3.3.3/

Beispiel von Orange Tsai @ Blackhat‘17:
https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

URL Parser Confusion• Unterschiede zwischen mehreren Parsern können ausgenutzt werden• http://1.1.1.1 &@2.2.2.2# 3.3.3.3/• → nur einen Parser nehmen

Beispiel von Orange Tsai @ Blackhat‘17:
https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

URL Parser Confusion• Unterschiede zwischen mehreren Parsern können ausgenutzt werden• http://1.1.1.1 &@2.2.2.2# 3.3.3.3/• → nur einen Parser nehmen• In der Vergangenheit gab es sogar PHP-interne Differenzen• parse_url vs readfile• parse_url vs curl

Beispiel von Orange Tsai @ Blackhat‘17:
https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

Allowlist: Einen guten Parser

Beispiel Denylist

Beispiel Denylist
1. $input= "https://app.com/link/page.html"

Beispiel Denylist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

Beispiel Denylist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

3. $deniedhosts= ["localhost", "evil.com"]Beispiel Denylist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

3. $deniedhosts
4. $isDenied= ["localhost", "evil.com"]= in_array($host, $allowedhosts)

Beispiel Denylist
1. $input
2. $host= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)

3. $deniedhosts
4. $isDenied= ["localhost", "evil.com"]= in_array($host, $allowedhosts)

5. if (!$isDenied) {file_get_contents($input);}Beispiel Denylist
1. $input
2. $host

3. $deniedhosts
4. $isDenied= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)= "app.com"= ["localhost", "evil.com"]= in_array($host, $allowedhosts)

5. if (!$isDenied) {file_get_contents($input);}Beispiel Denylist
1. $input
2. $host

3. $deniedhosts
4. $isDenied= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)= "app.com"= ["localhost", "evil.com"]= in_array($host, $allowedhosts)= false
5. if (!$isDenied) {file_get_contents($input);}Beispiel Denylist
1. $input
2. $host

3. $deniedhosts
4. $isDenied= "https://app.com/link/page.html"= parse_url($input, PHP_URL_HOST)= "app.com"= ["localhost", "evil.com"]= in_array($host, $allowedhosts)= false
5. if (!$isDenied) {file_get_contents($input);}Beispiel Denylist
1. $input
2. $host= "https://localhost/attack.html"= parse_url($input, PHP_URL_HOST)

3. $deniedhosts
4. $isDenied= ["localhost", "evil.com"]= in_array($host, $allowedhosts)

5. if (!$isDenied) {file_get_contents($input);}Beispiel Denylist
1. $input
2. $host

3. $deniedhosts
4. $isDenied= "https://localhost/attack.html"= parse_url($input, PHP_URL_HOST)= "localhost"= ["localhost", "evil.com"]= in_array($host, $allowedhosts)

5. if (!$isDenied) {file_get_contents($input);}Beispiel Denylist
1. $input
2. $host

3. $deniedhosts
4. $isDenied= "https://localhost/attack.html"= parse_url($input, PHP_URL_HOST)= "localhost"= ["localhost", "evil.com"]= in_array($host, $allowedhosts)= true5. if (!$isDenied) {file_get_contents($input);}Beispiel Denylist
1. $input
2. $host

3. $deniedhosts
4. $isDenied= "https://localhost/attack.html"= parse_url($input, PHP_URL_HOST)= "localhost"= ["localhost", "evil.com"]= in_array($host, $allowedhosts)= true5. if (!$isDenied) {file_get_contents($input);}Host Denylist
In Liste
URL

parse

Host
sonst
SSR

Host Denylist
In Liste
URL

parse

Host
sonst
SSR

Host Denylist
In Liste
URL

parse

Host
sonst
URL

DNS

IP

Host Denylist
In Liste
URL

parse

Host
sonst
URL

DNS

IP

IP-Denylist via DNS

IP-Denylist via DNS
1. $input= "https://fbi.com/attack.html"

IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)

IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)

3. $ip= gethostbyname($host);

IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)

3. $ip= gethostbyname($host);

4. $deniedIps= ["127.0.0.1"]

IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)

3. $ip= gethostbyname($host);

4. $deniedIps
5. $isDenied= ["127.0.0.1"]= in_array($host, $deniedIps)

IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)

3. $ip= gethostbyname($host);

4. $deniedIps
5. $isDenied= ["127.0.0.1"]= in_array($host, $deniedIps)

6. if (!$isDenied) {file_get_contents($input);}IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)

3. $ip= "fbi.com"= gethostbyname($host);

4. $deniedIps
5. $isDenied= ["127.0.0.1"]= in_array($host, $deniedIps)

6. if (!$isDenied) {file_get_contents($input);}IP-Denylist via DNS
1. $input
2. $host
3. $ip4. $deniedIps
5. $isDenied= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)= "fbi.com"= gethostbyname($host);= "127.0.0.1"= ["127.0.0.1"]= in_array($host, $deniedIps)

6. if (!$isDenied) {file_get_contents($input);}IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)= "fbi.com"
3. $ip= gethostbyname($host);= "127.0.0.1"
4. $deniedIps= ["127.0.0.1"]
5. $isDenied= in_array($host, $deniedIps)= true6. if (!$isDenied) {file_get_contents($input);}IP-Denylist via DNS
1. $input
2. $host= "https://fbi.com/attack.html"= parse_url($input, PHP_URL_HOST)= "fbi.com"
3. $ip= gethostbyname($host);= "127.0.0.1"
4. $deniedIps= ["127.0.0.1"]
5. $isDenied= in_array($host, $deniedIps)= true6. if (!$isDenied) {file_get_contents($input);}

Fix: DNS
In Liste
URL

parse Host DNS

IP
sonst
SSR

Fix: DNS
In Liste
URL

parse Host DNS

IP
sonst
SSR

Fix: DNS?
In Liste
URL

parse Host DNS

IP
sonst
URL

DNS

IP

Fix: DNS?
In Liste
URL

parse Host DNS

IP
sonst
URL

DNS

IP

Fix: DNS?
In Liste
URL

parse Host DNS

IP
sonst
URL

DNS

IP

Beispielsangriff

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us
2. DNS-Abfrage für Check: IP = 1.1.1.1

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us
2. DNS-Abfrage für Check: IP = 1.1.1.1
3. Check bestanden

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us
2. DNS-Abfrage für Check: IP = 1.1.1.1
3. Check bestanden
4. SSR wird ausgelöst

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us
2. DNS-Abfrage für Check: IP = 1.1.1.1
3. Check bestanden
4. SSR wird ausgelöst
5. Time-To-Live (TTL) des DNS-Cache-Eintrags läuft aus

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us
2. DNS-Abfrage für Check: IP = 1.1.1.1
3. Check bestanden
4. SSR wird ausgelöst
5. Time-To-Live (TTL) des DNS-Cache-Eintrags läuft aus
6. DNS Abfrage für SSR: IP = 127.0.0.1

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us
2. DNS-Abfrage für Check: IP = 1.1.1.1
3. Check bestanden
4. SSR wird ausgelöst
5. Time-To-Live (TTL) des DNS-Cache-Eintrags läuft aus
6. DNS Abfrage für SSR: IP = 127.0.0.1
7. SSR an 127.0.0.1

Beispielsangriff
1. Domain: 7f000001.01010101.rbndr.us
2. DNS-Abfrage für Check: IP = 1.1.1.1
3. Check bestanden
4. SSR wird ausgelöst
5. Time-To-Live (TTL) des DNS-Cache-Eintrags läuft aus
6. DNS Abfrage für SSR: IP = 127.0.0.1
7. SSR an 127.0.0.1

DNS Pinning!

URL

DNS

IP

SSR mit IPhttps://symfony.com/doc/current/http_client.html#ssrf-server-side-request-forgery-handlinghttps://github.com/j0k3r/safecurl

https://github.com/j0k3r/safecurl

Denylist: DNS Pinning!

Allow und Deny Listen• Maßnahmen nur so gut wie die Listen• Allowlist ist besser• Falls ich vorher alles zu erreichende kenne• Aber sind die Domains unter meiner Kontrolle?....

• Falls nicht:• Alles „ unerwünschte“ ausschließen• Oft nicht nur lokale IPs• Nicht leicht: IP ranges, cloud IPs, IPv6SSRF Filmabend

https://app.com/?url=ftp://gratisfilme.to/film.mkv

https://www.php.net/manual/de/wrappers.php

Curl Protocols

PHP supports libcurl […] libcurl currently supports the http, https, ftp,
gopher, telnet, dict, file, and ldap protocols
https://www.php.net/manual/en/intro.curl.php

Curl Protocols

PHP supports libcurl […] libcurl currently supports the http, https, ftp,
gopher, telnet, dict, file, and ldap protocols
https://www.php.net/manual/en/intro.curl.php

Spaß mit Gopher

gopher://example.org/0file.txt

Spaß mit Gopher

gopher://example.org/0file.txt

Spaß mit Gopher

gopher://example.org/0file.txt
file.txt

Spaß mit Gopher
gopher://127.0.0.1:25/_MAIL%20FRO
M:hello%40%E2%82example.org%0ARCP
T%20To:target%40example.org%0ADAT
A%0AFrom:hello%40%E2%82example.or
g%0ASubject:SSRFMail%0AMessage:Message-Body%0A.

Spaß mit Gopher
gopher://127.0.0.1:25/_MAIL
FROM:hello@example.org
RCPT To:target@example.org
DATA
From:hello@example.org
Subject:SSRF-Mail
Message:Message-Body.

Spaß mit Gopher
gopher://127.0.0.1:25/_MAIL
FROM:hello@example.org
RCPT To:target@example.org
DATA
From:hello@example.org
Subject:SSRF-Mail
Message:Message-Body.

Spaß mit Gopher
MAIL FROM:hello@example.org
RCPT To:target@example.org
DATA
From:hello@example.org
Subject:SSRF-Mail
Message:Message-Body.

Spaß mit Gopher
MAIL FROM:hello@example.org
RCPT To:target@example.org
DATA
From:hello@example.org
Subject:SSRF-Mail
Message:Message-Body.

Spaß mit Gopher

Protokolle festlegen!
Meist: Nur HTTP(S)Denial-Of-Service (DoS)

https://example.org/?url=
https://evil.com

😈

https://evil.com/

😈

Denial-Of-Service (DoS)

https://example.org/?url=
https://evil.com

😈

https://evil.com/

ZIP-Bombe😈

Denial-Of-Service (DoS)

https://example.org/?url=
https://evil.com

😈

https://evil.com/

ZIP-Bombe😈

Andere DoSenAndere DoSenRate-Limits!
Für Input, Output, sowie Rechenzeit.

Redirects

😈

Redirects

😈

😈

Redirects

😈

😈
Redirect: localhost

Redirects

😈

😈
Redirect: localhost

Redirects

😈

😈
Redirect: localhost

Redirects ausschalten
Oder immer wieder erneut prüfen

GeldOrigin-Wäsche
• Wir sollten nicht zum Verstecken des Ursprungs von Content genutzt
werden können
• Web-Security-Mechanismen• Diverse Best-Practice-Regeln• Result der SSR nicht 1:1 zurück geben, also z.B. wrappen• Access-Control-Allow-Origin: *• Content-Disposition Header auf attachment setzen

Antwort sicher ausgebenZurück zur Linkvorschau

Szenario: Screenshot
https://example.org/?url=https://winterkongress.chSzenario: Screenshot
https://example.org/?url=https://winterkongress.chSzenario: Screenshot
https://example.org/?url=https://winterkongress.ch

https://winterkongress.chSzenario: Screenshot
https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch<!DOCTYPE html><html lang="de"><head> […]<title>Winterkongress 2024</title>[…]

Szenario: Screenshot
https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch<!DOCTYPE html><html lang="de"><head> […]<title>Winterkongress 2024</title>[…]
logo_digiges.svg…

Szenario: Screenshot
https://example.org/?url=https://winterkongress.ch

https://winterkongress.ch<!DOCTYPE html><html lang="de"><head> […]<title>Winterkongress 2024</title>[…]
logo_digiges.svg…

Server-Side Browser

Server-Side Browser• Parsen und führen Antwort der SSR aus!

Server-Side Browser• Parsen und führen Antwort der SSR aus!
• Browser haben sehr viel Angriffsfläche

Server-Side Browser• Parsen und führen Antwort der SSR aus!
• Browser haben sehr viel Angriffsfläche

Generalisiert

https://myapp.example/?ur
l=https://evil.com

😈

https://evil.com/

😈

Generalisiert

https://myapp.example/?ur
l=https://evil.com

😈

https://evil.com/

😈

Generalisiert

https://myapp.example/?ur
l=https://evil.com

😈

https://evil.com/

😈

Clients up-to-date halten!
Insbesondere Browser.

Takeaways• Allgemeine Einstellungen beachten (HTTPS only, no redirects, etc.)• Solide [allow|deny] Liste bauen• Szenario A: Allow list → Relativ sicher• Szenario B: Deny list → IP pinning• Non-application level• Auf Netzwerkebene trennen•…

Danke für Eure Aufmerksamkeit!

@maltee:chaos.social
malte.wessels@tu-braunschweig.de

Icons:• CC BY 4.0 DEED / Fontawesome• Iconmonstr