Confused Deputy: Google IdP Account Takeover via Device Code Flow Hijacking

zerodaysbroker1 pts0 comments

Confused Deputy: Google IdP Universal Account Takeover via Device Code Flow Hijacking // weirdmachine64

← ./research

TL;DR

This one started from setting up the YouTube app on my PS5. The device authorization grant (RFC 8628) it uses, the flow TVs, consoles, and CLIs rely on when they don't have a browser of their own, turned out to hide two stacked bugs in Google's implementation.

Two bugs stack together. First, the session that anchors a device-code sign-in is fully transferable: copy the sign-in URL from one browser to another and the second browser's login satisfies the first device's poll. Second, the authorization server never binds client_id and scope to the device_code server-side, so both can be swapped in the URL after the fact. Chain the two together with the prompt=none parameter and any link, opened by a victim who has ever used "Sign in with Google" anywhere, silently hands over an access token for an arbitrary Google-registered client, no click, no consent screen, no 2FA prompt, almost no trace in the victim's account activity.

Reported to Google's VRP on Feb 25, 2026, initially closed twice as "won't fix": social engineering, reopened after a one-click PoC, fixed by Mar 28, 2026, and rewarded $13,337. Details on that back-and-forth are in the disclosure timeline below.

1. Intro

Most of the well-known attacks on OAuth go after the client or the resource server: a malicious app, an open redirect, a signing-algorithm mix-up. They leave the authorization server itself alone, because it's the one party in the protocol that's supposed to be unshakeable, the thing every other trust decision is anchored to. This is a story about going after that assumption directly, in the one corner of OAuth that's explicitly designed to let the login happen on a completely different screen: the device authorization grant.

It started as a mundane afternoon setting up a TV app on a game console, and it ended with a way to silently take over accounts on virtually any site that offers "Sign in with Google." Getting from one to the other took two separate findings stacked on top of each other, a rejected report, and a fix to the fix. What follows is that story, roughly in the order it actually happened, blockers included.

2. The Device Authorization Grant

Most OAuth flows assume the device asking for access has a browser sitting right there to redirect through. RFC 8628 exists for the case where it doesn't: a smart TV, a games console, a headless CLI. The shape is different from the usual redirect dance:

The device calls the authorization server directly (POST /device/code) and gets back a device_code (secret, stays on the device) and a user_code (short, shown on screen).

The device displays the user_code and tells the user to go to a URL, google.com/device in Google's case, on any other browser.

The user opens that URL on their phone or laptop, types the code, signs in, and consents.

Meanwhile the device has been polling POST /token with its device_code. Once the user finishes step 3, the next poll returns an access token.

The OAuth 2.0 device authorization grant: the device gets a device_code and user_code, the user authenticates on a separate browser, and the device polls for a token in the background. (credit: Microsoft).<br>The whole point of the design is that the device and the browser doing the authenticating can be, and usually are, two completely different pieces of hardware. That's also exactly what makes this flow interesting to attack: the protocol already expects the login to happen somewhere else. The only thing holding the model together is that the "somewhere else" has to be a browser the legitimate device owner is sitting at.

That's the assumption. The rest of this write-up is what happened when I went looking for the place where Google's implementation stops enforcing it.

3. Setting Up YouTube TV on a PS5

I was setting up the YouTube app on my PS5, ordinary first-run setup. The console has no keyboard and no way to type a password comfortably with a controller, so it does the sensible thing: it shows a short user_code on screen and tells you to go sign in on your phone instead. I typed the code into google.com/device, signed into Google, approved the consent screen, and a few seconds later the PS5 was logged in.

The YouTube TV "Add your Google Account" screen: a QR code and a short user_code, with instructions to finish sign-in on a phone.<br>Nothing about that felt unusual as a user, but the flow itself was intriguing: a screen with no keyboard asking me to authenticate on a completely separate device, and coming back logged in seconds later. That disconnect between where I typed my password and where the session actually landed is what made me want to look at it more closely. Behind the scenes, that's:

POST https://oauth2.googleapis.com/device/code → device_code + user_code.

The PS5 polling POST https://oauth2.googleapis.com/token with that device_code.

My phone's browser...

device google browser code authorization screen

Related Articles