XSS + OAuth configuration errors = Token theft and account takeover

In this article, I will talk about how I managed to find an Account Takeover (ATO) vulnerability through OAuth configuration errors and steal authorization tokens.

The story began with the discovery of an XSS vulnerability on the sub1.target.com subdomain. The problem was that there was little pay for bugs on this subdomain, so our goal was to use XSS to escalate to an account takeover (ATO) on the main app.target.com app, where the rewards were significantly higher.

After examining sub1.target.com's authentication mechanism, we found that it used a JWT token based on cookies, while app.target.com authentication was carried out through a JWT token passed in the Authorization header. The JWT values ​​on these two subdomains were different, however, to our surprise, the JWT from sub1.target.com also worked for authentication on app.target.com!

So, if we could compromise the JWT auth token from sub1.target.com, we could use it to log into app.target.com.

Unfortunately, the cookie-based JWT token on sub1.target.com was protected by the HttpOnly flag, making it impossible to retrieve it using XSS. However, while analyzing the target resource's OAuth flow, we discovered another way to leak these JWT tokens!

We found a request whose response contained a JWT token from another subdomain – auth.target.com.

After detailed analysis, we discovered some interesting behavior in the previous request. If you change the value of the response_mode parameter from post to get, the server response changes. Moreover, we could set the state and nonce parameters to any value, and the server would still return an authentication token.

Now that we have the XSS on sub1.target.com, we can create an iframe that loads the OAuth endpoint on auth.target.com. Fortunately, at this stage, no X-Frame-Options or CSP headers have been set to prevent this endpoint from being displayed in a frame. However, the Single Origin Policy (SOP) will prevent the contentWindow iframe object from being accessed from another origin.

We considered the possibility that both subdomains could set document.domain = 'target.com', which would result in their “same origin” state. This technique was used to relax SOPs between subdomains. If both subdomains explicitly set the document.domain property to the parent target.com domain, they will share the same origin, allowing Cross-origin access.

However, this technique is no longer supported by modern browsers such as Chrome.

Instead, we discovered that when navigating to https://auth.target.com/oauth?client_id=redacted&redirect_uri=https://auth.target.com/endpoint.jsp&response_mode=get&response_type=code&scope=redacted&state=redacted&nonce=redactedwe were redirected to https://sub1.target.com#token=. As a result, the page is now considered to have the same origin, allowing us to access the window's location.href property and extract the token from the URL fragment.

var auth = document.createElement('iframe');
auth.setAttribute('src', 'https://auth.target.com/oauth?client_id=redacted&redirect_uri=https://auth.target.com/endpoint.jsp&response_mode=get&response_type=code&scope=redacted&state=redacted&nonce=redacted');
auth.setAttribute('id', 'lol');
token = new URLSearchParams(document.getElementById("lol").contentWindow.location.hash.split('#')[1]).get('token');

After initiating a request to get a token from a URL fragment, we can send it to our server.

Since the JWT auth token was stolen during authentication on auth.target.com using XSS on sub1.target.com, we can now include the stolen JWT auth token in requests to change victims' email addresses on app.target.com , which will achieve (ATO).

The full course of the attack can be represented as follows:

My tg channel.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *