Lab: Close enough (OAuth redirect_uri validation)
Difficulty: medium. Time: 20 to 40 minutes. Run it: node redirect-prefix.mjs (lab folder), then open http://localhost:4102/.
The scenario
Section titled “The scenario”A notes app is a public OAuth client with no PKCE. Its registered redirect URI is http://localhost:4102/cb. A victim is already signed in at the identity provider and opens any link you send them (POST /victim/visit). An attacker listener on 127.0.0.1:4103 records every request it receives.
Goal: get the victim’s authorization code, exchange it at /token, and read the victim’s note from /api/me.
Hint 1
Ask the server which redirect_uri values it accepts. Try values that are not the registered one and read the error text.
Hint 2
The check compares the beginning of the string. A URL is not just a string: which part of it decides where a browser connects?
What this teaches
Section titled “What this teaches”Redirect URI matching must be exact. A “starts with our origin” or “contains our domain” check lets an attacker build a URL that passes the check but sends the code to a host they control. Strict matching is also why AADSTS50011 exists: the annoying error is the protection working.
Solution and fix (spoilers)
Use http://localhost:[email protected]:4103/steal as the redirect_uri. It starts with the allowed origin, but everything before the @ is user info, so the browser connects to 127.0.0.1:4103. Send the victim the /authorize link, read the code from the listener’s /log, exchange it at /token, then call /api/me.
The fix is in the --fixed build: compare the whole URI for equality against the registered value. PKCE would also have made a stolen code useless on its own.
Detection angle
Section titled “Detection angle”Failed redirect URI matches show up as AADSTS50011 in the sign-in logs. A burst of them for one application from one IP is someone probing the matching rule; see the KQL on the AADSTS50011 page.
Sources
Section titled “Sources”- Microsoft: Redirect URI (reply URL) restrictions and limitations. Redirect URIs are case-sensitive, a mismatch gives AADSTS50011, and wildcard URIs “should be avoided due to security implications”. Microsoft ignores the port only for localhost URIs.
- RFC 9700: Best Current Practice for OAuth 2.0 Security, section 2.1: authorization servers must use exact string matching for redirect URIs.