# OAuth to Account takeover

**This information was taken from** [**https://medium.com/a-bugz-life/the-wondeful-world-of-oauth-bug-bounty-edition-af3073b354c1**](https://medium.com/a-bugz-life/the-wondeful-world-of-oauth-bug-bounty-edition-af3073b354c1)\*\*\*\*

## “Quick” Primer <a id="d4a8"></a>

There are a couple different versions, as well as grant types to consider when we talk about OAuth. To read about these, I recommend reading through [https://oauth.net/2/](https://oauth.net/2/) to get a baseline understanding. In this article, we will be focusing on the most common flow that you will come across today, which is the [OAuth 2.0 authorization code grant type](https://oauth.net/2/grant-types/authorization-code/). In essence, OAuth provides developers an authorization mechanism to allow an application to access data or perform certain actions against your account, from another application \(the authorization server\).

For example, let’s say website [https://yourtweetreader.com](https://yourtweetreader.com/) has functionality to display all tweets you’ve ever sent, including private tweets. In order to do this, OAuth 2.0 is introduced. [https://yourtweetreader.com](https://yourtweetreader.com/) will ask you to authorize their Twitter application to access all your Tweets. A consent page will pop up on [https://twitter.com](https://twitter.com/) displaying what permissions are being requested, and who the developer requesting it is. Once you authorize the request, [https://yourtweetreader.com](https://yourtweetreader.com/) will be able to access to your Tweets on behalf of you. Now, this was very high level, and there’s some complexity here. Taking this example, here’s a bit more details on the particular elements which are important to understand in an OAuth 2.0 context:

**resource owner**: The `resource owner` is the user/entity granting access to their protected resource, such as their Twitter account Tweets

**resource server**: The `resource server` is the server handling authenticated requests after the application has obtained an `access token` on behalf of the `resource owner` . In the above example, this would be https://twitter.com

**client application**: The `client application` is the application requesting authorization from the `resource owner`. In this example, this would be https://yourtweetreader.com.

**authorization server**: The `authorization server` is the server issuing `access tokens` to the `client application` after successfully authenticating the `resource owner` and obtaining authorization. In the above example, this would be [https://twitter.com](https://twitter.com/)

**client\_id**: The `client_id` is the identifier for the application. This is a public, non-secret unique identifier.

**client\_secret:** The `client_secret` is a secret known only to the application and the authorization server. This is used to generate `access_tokens`

**response\_type**: The `response_type` is a value to detail which type of token is being requested, such as `code`

**scope**: The `scope` is the requested level of access the `client application` is requesting from the `resource owner`

**redirect\_uri**: The `redirect_uri` is the URL the user is redirected to after the authorization is complete. This usually must match the redirect URL that you have previously registered with the service

**state**: The `state` parameter can persist data between the user being directed to the authorization server and back again. It’s important that this is a unique value as it serves as a CSRF protection mechanism if it contains a unique or random value per request

**grant\_type**: The `grant_type` parameter explains what the grant type is, and which token is going to be returned

**code**: This `code` is the authorization code received from the `authorization server` which will be in the query string parameter “code” in this request. This code is used in conjunction with the `client_id` and `client_secret` by the client application to fetch an `access_token`

**access\_token**: The `access_token` is the token that the client application uses to make API requests on behalf of a `resource owner`

**refresh\_token**: The `refresh_token` allows an application to obtain a new `access_token` without prompting the user

Well, this was meant to be a quick primer but it seems with OAuth, you can’t simply give a brief description. Putting this all together, here is what a real OAuth flow looks like:

1. You visit [https://yourtweetreader.com](https://yourtweetreader.com/) and click the “Integrate with Twitter” button.
2. [https://yourtweetreader.com](https://yourtweetreader.com/) sends a request to [https://twitter.com](https://twitter.com/) asking you, the resource owner, to authorize https://yourtweetreader.com’s Twitter application to access your Tweets. The request will look like:

```text
https://twitter.com/auth
 ?response_type=code
 &client_id=yourtweetreader_clientId
 &redirect_uri=https%3A%2F%2Fyourtweetreader.com%2Fcallback
 &scope=readTweets
 &state=kasodk9d1jd992k9klaskdh123
```

3. You will be prompted with a consent page:

![](https://miro.medium.com/max/1215/1*y66EY3Fn2qn-NPI9nhZC7A.png)

4. Once accepted, Twitter will send a request back to the `redirect_uri` with the `code` and `state` parameters:

```text
https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1&state=kasodk9d1jd992k9klaskdh123
```

5. [https://yourtweetreader.com](https://yourtweetreader.com/) will then take that `code` , and using their application’s `client_id` and `client_secret` , will make a request from the server to retrieve an `access_token` on behalf of you, which will allow them to access the permissions you consented to:

```text
POST /oauth/access_token
Host: twitter.com
...{"client_id": "yourtweetreader_clientId", "client_secret": "yourtweetreader_clientSecret", "code": "asd91j3jd91j92j1j9d1", "grant_type": "authorization_code"}
```

6. Finally, the flow is complete and [https://yourtweetreader.com](https://yourtweetreader.com/) will make an API call to Twitter with your `access_token` to access your Tweets.

## Bug Bounty Findings <a id="323a"></a>

Now, the interesting part! There are many things that can go wrong in an OAuth implementation, here are the different categories of bugs I frequently see:

### Weak redirect\_uri configuration <a id="cc36"></a>

This is probably one of the more common things everyone is aware of when looking for OAuth implementation bugs. The `redirect_uri` is very important because sensitive data, such as the `code` is appended to this URL after authorization. If the `redirect_uri` can be redirected to an attacker controlled server, this means the attacker can potentially takeover a victim’s account by using the `code` themselves, and gaining access to the victim’s data.

The way this is going to be exploited is going to vary by authorization server. Some will only accept the exact same `redirect_uri` path as specified in the client application, but some will accept anything in the same domain or subdirectory of the `redirect_uri` .

Depending on the logic handled by the server, there are a number of techniques to bypass a `redirect_uri` . In a situation where a `redirect_uri` is [https://yourtweetreader.com](https://yourtweetreader.com/)/callback, these include:

* Open redirects: [`https://yourtweetreader.com`](https://yourtweetreader.com/)`/callback?redirectUrl=https://evil.com`
* Path traversal: `https://yourtweetreader.com/callback/../redirect?url=https://evil.com`
* Weak `redirect_uri` regexes: `https://yourtweetreader.com.evil.com`
* HTML Injection and stealing tokens via referer header: `https://yourtweetreader.com/callback/home/attackerimg.jpg`

### Improper handling of state parameter <a id="bda5"></a>

This is by far the most common issue I see in OAuth implementations. Very often, the **`state` parameter is completely omitted or used in the wrong way**. If a state parameter is nonexistent, or a static value that never changes, the OAuth flow will very likely be vulnerable to CSRF. Sometimes, even if there is a `state` parameter, the application might not do any validation of the parameter and an attack will work. The way to exploit this would be to go through the authorization process on your own account, and pause right after authorizing. You will then come across a request such as:

```text
https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1
```

After you receive this request, you can then **drop the request because these codes are typically one-time use**. You can then send this URL to a **logged-in user, and it will add your account to their account**. At first, this might not sound very sensitive since you are simply adding your account to a victim’s account. However, many OAuth implementations are for sign-in purposes, so if you can add your Google account which is used for logging in, you could potentially perform an **Account Takeover** with a single click as logging in with your Google account would give you access to the victim’s account.

You can find an **example** about this in this [**CTF writeup**](https://github.com/gr455/ctf-writeups/blob/master/hacktivity20/notes_surfer.md) and in the **HTB box called Oouch**.

I’ve also seen the state parameter used as an additional redirect value several times. The application will use `redirect_uri` for the initial redirect, but then the `state` parameter as a second redirect which could contain the `code` within the query parameters, or referer header.

One important thing to note is this doesn’t just apply to logging in and account takeover type situations. I’ve seen misconfigurations in:

* Slack integrations allowing an attacker to add their Slack account as the recipient of all notifications/messages
* Stripe integrations allowing an attacker to overwrite payment info and accept payments from the victim’s customers
* PayPal integrations allowing an attacker to add their PayPal account to the victim’s account, which would deposit money to the attacker’s PayPal

### Assignment of accounts based on email address <a id="ebe4"></a>

One of the other more common issues I see is when applications allow “Sign in with X” but also username/password. There are 2 different ways to attack this:

1. If the application does not require email verification on account creation, try creating an account with a victim’s email address and attacker password before the victim has registered. If the victim then tries to register or sign in with a third party, such as Google, it’s possible the application will do a lookup, see that email is already registered, then link their Google account to the attacker created account. This is a “pre account takeover” where an attacker will have access to the victim’s account if they created it prior to the victim registering.
2. If an OAuth app does not require email verification, try signing up with that OAuth app with a victim’s email address. The same issue as above could exist, but you’d be attacking it from the other direction and getting access to the victim’s account for an account takeover.

### Disclosure of Secrets <a id="e177"></a>

It’s very important to recognize which of the many OAuth parameters are secret, and to protect those. For example, leaking the `client_id` is perfectly fine and necessary, but leaking the `client_secret` is dangerous. If this is leaked, the attacker can potentially use the trust and identity of the trusted client application to steal user `access_tokens` and private information/access for their integrated accounts. Going back to our earlier example, one issue I’ve seen is performing this step from the client, instead of the server:

5. [https://yourtweetreader.com](https://yourtweetreader.com/) will then take that `code` , and using their application’s `client_id` and `client_secret` , will make a request from the server to retrieve an `access_token` on behalf of you, which will allow them to access the permissions you consented to.

If this is done from the client, the `client_secret` will be leaked and users will be able to generate `access_tokens` on behalf of the application. With some social engineering, they can also add more scopes to the OAuth authorization and it will all appear legitimate as the request will come from the trusted client application.

### Referer Header leaking Code + State

Once the client has the code and state, if it's reflected inside the Referer header when he browses to a different page, then it's vulnerable.

### Access Token Stored in Browser History

Go to the browser history and check if the access token is saved in there.

## Closing <a id="996f"></a>

There’s plenty of other attacks and things that can go wrong in an OAuth implementation, but these are some of the more common ones that you will see. These misconfigurations are surprisingly common, and a very large quantity of bugs come from these. I intended to keep the “Quick Primer” rather short, but quickly realized all of the knowledge was necessary for the rest of the post. Given this, it makes sense that most developers aren’t going to know all the details for implementing securely. More often than not, these issues are high severity as it involves private data leak/manipulation and account takeovers. I’d like to go into more detail in each of these categories at some point, but wanted this to serve as a general introduction and give ideas for things to look out for!

## OAuth providers Race Conditions

If the platform you are testing is an OAuth provider ****[**read this to test for possible Race Conditions**](race-condition.md).

