Exploiting OAuth: Journey to Account Takeover
Most of the web and mobile applications these days use OAuth to secure their authorization endpoints. It allows them to easily grant access to their users to particular resources as per the application’s requirements.
This is a write-up of a chain of vulnerabilities (OAuth Misconfiguration, CSRF, XSS, and Weak CSP) that allowed me to take over a user account using a single interaction.
This was a usual Project Management Web Application, using Microsoft’s OAuth 2.0 to authorize their users to allow them access to the application. Let’s call it -
OAuth 2.0 Flow
An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications.
It is the industry-standard protocol for authorization. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices.
OAuth 2.0 is an authorization protocol and NOT an authentication protocol. As such, it is designed primarily as a means of granting access to a set of resources, for example, remote APIs or user’s data.
The authentication flow of the application was such that when a user visited the Application at
https://victim.com, it redirected them to Microsoft’s Authorize endpoint at https://login.microsoftonline.com/<tenant-name>.onmicrosoft.com/oauth2/v2.0/authorize?p=<policy-name>
This is where the users entered their Email Addresses and the Passwords to authenticate and after a successful OAuth flow, the user was returned to the application showing them the actual Dashboard.
Whenever an OAuth authentication is being used, the first thought crossing the mind of an attacker is to check if the application validates the value of
redirectUrl. This may lead to OAuth token stealing if the token is returned along with the callback request.
The initial request was
which redirected me to the Microsoft login page URL mentioned in the previous section.
So I tried to manipulate the
redirectUrl and changed it with a server that I controlled to see if I receive the tokens but unfortunately, the application was not sending any of the tokens with the callback request which was weird.
On inspecting closely, it was observed that after returning from the OAuth flow, it sent a request to https://app.victim.com/auth/return containing the
token values in the POST body.
The page then redirected me to -
This is the value from the
redirectUrl parameter shown earlier in the initial request. Even though I was not able to get tokens by manipulating the
redirectUrl, an attack could have still been possible if somehow the parameter was vulnerable to an XSS allowing me to directly read the tokens either from the source or from the session storage.
So I modified my payload to close the existing script tag to check if injecting scripts is possible or not. Here’s the URL that I used -
and the application graciously closed the script tag for me and reflected my HTML payload.
From here, it was only one more step of data ex-filtration to my own server to steal the tokens and create a report.
But wait, there’s more. Now comes the part where I was stopped by the Content-Security-Policy. This is how their CSP looked when viewed on Google’s CSP Evaluator -
unsafe-inline mostly does the trick in terms of inline script execution so that’s not an issue.
This could also have been bypassed using
https://www.gstatic.com domain shown above because it hosts Angular Libraries. Here’s how that would have looked -
The thing that troubled me was the data ex-filtration because the
connect-src directive only allowed certain domains to make connections to.
In simple terms, this means I can’t randomly make requests to my own server to receive the tokens.
connect-srcContent Security Policy (CSP) directive guards the several browsers mechanisms that can fetch HTTP Requests. This includes
XMLHttpRequest(XHR / AJAX),
I tried frames and images as well but that didn’t work either because of
image-src attributes -
If you are not allowed to connect to any external host, you can send data directly in the URL (query string) by redirecting the user to your web server. Here’s my final payload -
In the above payload, I’ve used
window.location to redirect the user’s browser to my server and along with the redirection, I’m attaching the tokens present in the page using
And the final result is freshly generated Session Tokens received by my netcat listener
Since this is a combination of multiple vulnerabilities, here’s how it could have been mitigated -
- The initial vulnerability is introduced due to misconfiguration in implementing the OAuth flow’s
redirectUrlparameter which is never validated. This was manipulated with ease which introduced the main bug.
- The endpoint lacked CSRF protection. Along with the URL validation, the endpoint should have implemented a CSRF validation. An extra state parameter that’s generated and validated when they initiate the authentication flow.
- The XSS when setting the user tokens in the session storage. This allowed me to inject scripts to execute my payload. Great resource - OWASP XSS Prevention Cheat Sheet
- Weak CSP Policy allowing
unsafe-inline. Except for one very specific case, you should avoid using the
unsafe-inlinekeyword in your CSP policy. As you might guess it is generally unsafe to use