| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | OAuth 2.0 / OpenID Connect / SSRF |
| Difficulty | Practitioner |
| Objective | Register a malicious OAuth client with a crafted logo_uri pointing to the AWS metadata endpoint, then trigger the provider to fetch it and leak the admin IAM secret access key |
SSRF via OpenID Dynamic Client Registration¶
Clicking "My account" redirected through the OAuth flow. The authorization request included scope=openid — OpenID Connect is in use, not bare OAuth. OIDC adds standardized authentication on top of OAuth, and crucially, it standardizes features like dynamic client registration.
After authenticating as wiener:peter, the consent screen loaded. Something subtle in the page source:
<img src="/client/d7egmxuxwr7x6kbtst06d/logo">
The OAuth provider fetches and serves a client logo from a URI associated with the registered client — a server-side fetch. The normal login completed:
Dynamic client registration in OIDC lets applications register themselves programmatically via a /registration endpoint by POSTing their metadata as JSON. Some providers expose this without authentication — meaning an attacker can register an arbitrary client and supply attacker-controlled values for any field the provider subsequently consumes server-side, including URIs. When the provider serves the consent screen, it fetches the logo_uri from the registered client server-side to display the logo. That's a second-order SSRF primitive: register the malicious URI now, trigger the server-side fetch later by requesting the logo endpoint. The SSRF doesn't fire at registration time — it fires when the URI is consumed.
The /.well-known/openid-configuration endpoint is always the first stop with OIDC — it maps the full attack surface:
"registration_endpoint": "https://oauth-0afd0005043d832f8076154502d90030.oauth-server.net/reg"
Working through the error messages on POST /reg:
A minimal valid registration request:
POST /reg HTTP/2
Host: oauth-0afd0005043d832f8076154502d90030.oauth-server.net
Content-Type: application/json
{
"redirect_uris": [
"https://teto.com"
]
}
201 Created — no authentication required. The registration endpoint is completely open. Registering the malicious client with logo_uri pointing at the AWS instance metadata service, accessible from any EC2 instance's own network but not from outside:
POST /reg HTTP/2
Host: oauth-0afd0005043d832f8076154502d90030.oauth-server.net
Content-Type: application/json
{
"redirect_uris": [
"https://teto.com"
],
"logo_uri": "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/"
}
New client registered with client_id: qMy3jQDUKjdFAu_nBhwSj. Requesting the logo for this client triggered the server-side fetch from the OAuth provider's internal network:
GET /client/qMy3jQDUKjdFAu_nBhwSj/logo HTTP/2
The response contained the IAM credentials including the secret access key. Submitting it:
With this the lab is solved