DEV Community

Davi
Davi

Posted on Originally published at blog.mago.team

Working: Device Flow Phishing -- The OAuth Attack That Uses the Real Login Page

Every other OAuth phishing attack requires a fake login page. Device code phishing sends the victim to microsoft.com.

The device authorization grant is the only OAuth flow where an attacker initiates authentication on behalf of the victim. The victim never touches attacker-controlled infrastructure. She goes directly to the real authorization server, authenticates with her own credentials, and the attacker receives the token. Storm-2372, Russia's SVR-aligned threat cluster, ran this technique at scale against governments, defense contractors, and NGOs from August 2024 through February 2025. By February 2026, the EvilTokens PhaaS platform had commoditized the full attack toolkit.

Device flow is built on deliberate security tradeoffs

RFC 8628 removes redirect URIs, PKCE, and state parameters from the authorization flow. These removals are not design oversights. They are deliberate tradeoffs to make the flow viable on constrained devices without browsers, such as smart TVs and IoT consoles.

The mechanism runs in 3 phases. The client registers with the authorization server and receives a device_code, user_code, and verification_uri. The URI returned is always a legitimate provider domain: microsoft.com/devicelogin, accounts.google.com/device. The client begins polling the token endpoint. The user visits the verification URI on any device, types the 8-character user_code, and authenticates with her credentials.

3 security tradeoffs are written explicitly into the spec. First: the absence of a redirect URI removes the PKCE attack surface but also removes the client binding that redirect URIs provide in other grant types. Second: the user_code uses 8 characters from a 20-character base-20 alphabet, resulting in approximately 34.5 bits of entropy. That value was designed for humans to type on TV remotes. Third: rate limits on the polling endpoint are SHOULD, not MUST. The slow_down mechanism adds 5 seconds to the interval per throttle response, with no hard lockout defined in the spec.

The design is consistent with the RFC's original requirements. The attack surface did not emerge from a design error. It emerged from applying this flow to high-privilege contexts: Microsoft 365 access tokens with directory administration and mail read scopes.

The pending code pool is the real brute-force surface

A verification URI with a 15-minute window and no rate limit on user_code entry accepts 15 minutes of guesses against a 34.5-bit space. RFC 8628 ยง5.4 recommends rate limiting on the verification URI but does not require it.

The verification URI is a separate attack surface from the token endpoint. At high-scale providers like Azure AD, thousands of device authorizations are pending simultaneously. Each pending code represents an active session awaiting user approval. The pool size increases the effective attack surface for brute force.

The rate limiting mechanism in the RFC operates on the token endpoint, not the verification URI. The slow_down error adds 5 seconds per polling interval per response. No hard cutoff is defined in the spec. Servers that apply only this mechanism do not protect the verification URI as an independent attack surface.

An attacker who guesses a valid user_code during its validity window can approve the flow for any pending session tied to that code. The attack does not require knowledge of the specific victim. Oauth.com documents that high-scale provider pools number in the thousands simultaneously, reducing effective entropy in practice.

Illicit consent grant: victim authenticates at the real provider, attacker receives the token

The attack inverts the classic phishing model. In traditional phishing, the victim types credentials into a fake page controlled by the attacker. In device code phishing, the attacker needs no infrastructure in the authentication path.

The technical flow in 6 steps:

  1. The attacker POSTs to the /devicecode endpoint of the target server with any registered client_id. The Microsoft Office client_id, for example, is public.
  2. The server returns user_code, verification_uri, and device_code with an expiration window.
  3. The attacker sends the user_code and verification URI to the victim via email, Teams, Signal, or SMS, framed as a legitimate access request: "Please authenticate your device at microsoft.com/devicelogin. Code: ABCD-1234."
  4. The victim visits the verification URI. The domain is real: microsoft.com, google.com, github.com. No attacker infrastructure is in the path.
  5. The victim types the code, authenticates with real credentials, and completes MFA if required.
  6. The attacker's polling loop receives access_token and refresh_token. Entra ID refresh tokens carry a default validity of up to 90 days.

MFA is bypassed because the victim completes it. She authenticated with real credentials at the real provider. Nothing in the flow looks like phishing to URL-based detection systems, because the only domain shown to the victim belongs to the legitimate provider.

Storm-2372 ran device code phishing as a sustained state-sponsored access operation from August 2024

Microsoft Security Blog published the disclosure on February 13, 2025. The campaign started in August 2024. Targets included government, defense, NGO, telecom, and energy organizations across Europe, North America, Africa, and the Middle East.

The social engineering used impersonation via Microsoft Teams and Signal. Messages delivered user_codes framed as legitimate access requests from trusted colleagues or contacts. The absence of attacker infrastructure in the authentication path makes this technique ideal for deniable initial access operations by nation-state actors.

After the public disclosure, Storm-2372 adapted. The group shifted to using the Microsoft Authentication Broker client_id to establish persistent device registrations. Device registration grants primary authentication capability without requiring new user approval. Microsoft reclassified Storm-2372 as a Midnight Blizzard sub-cluster in July 2026.

Volexity tracked 2 additional Russia-aligned clusters, UTA0304 and UTA0307, running the same technique independently. By February 2026, the EvilTokens PhaaS platform had commoditized the full attack toolkit. The CSA Research Note published in March 2026 documented 340+ affected M365 organizations and a 37x surge in enterprise account takeover via device code phishing.

CVE-2023-2585: what happens when device_code and client_id are not bound

Keycloak versions 10.0.0 through 21.1.1 did not bind the device_code to the client_id that initiated the flow. A client could request a device_code, hand the user_code to the victim for verification, then exchange the device_code using a different, higher-privilege client_id. CVSS 3.5, CWE-358. Patched in 21.1.2.

MOGWAI LABS independently verified the root cause. RFC 8628 does not require client_id binding in the device authorization response. The vulnerability is not an arbitrary code bug. It is a spec gap that Keycloak did not fill.

Impact scales with how many administrative OAuth clients have the device grant enabled. Keycloak defaults to disabling the device grant for new clients, which limited blast radius in practice. Installations that explicitly enabled the grant for administrative clients were fully exposed through version 21.1.1.

Defense requires hardening controls the RFC only recommends

The RFC 8628 security model assumes the teams that build authorization servers will add hard limits beyond the spec's SHOULD-level guidance. 4 controls close the surfaces documented in the cases above.

Set expires_in below 5 minutes. The default 15-minute window was designed for smart TVs. Phishing operations on government networks exploit exactly that window.

Enforce rate limits per IP and per session on the verification URI, separate from the polling endpoint. The 2 surfaces are distinct and need independent limits. Rate limiting only the token endpoint does not close the verification URI attack surface.

Conditional Access policies in Entra ID can block device code flow for privileged accounts and require compliant device registration for all other cases. Disable the device authorization grant by default for applications that do not need it. The correct posture is opt-in, not opt-out.

The MAGO Intel tool tests authorization server configurations for device flow rate limiting, user_code entropy, and client_id binding enforcement during API security assessments.


Storm-2372's post-disclosure pivot to the Microsoft Authentication Broker client_id shows the attack adapts faster than detection rules. The structural advantage does not disappear with defender awareness. The victim authenticates at the real provider and the attacker receives the token. That advantage only disappears when the authorization server requires bindings the RFC left optional.

Top comments (0)