Home Security Microsoft 365 Device Code Defense
Microsoft 365 Device Code Defense Overview / Threat Model High

Device code phishing against Microsoft 365, how the attack inverts a legitimate OAuth flow

What device code flow is designed for

RFC 8628, the OAuth 2.0 Device Authorization Grant, exists for devices that cannot open a browser. A smart TV that wants to authorize a streaming service. An IoT sensor that needs delegated access to send telemetry. An Azure CLI session on a headless server. The flow works by having the device contact Microsoft to generate a short-lived code, display that code to the user, and tell the user to visit microsoft.com/devicelogin on a separate device, enter the code, and authenticate. Microsoft polls the token endpoint in the background. When the user completes authentication, the device receives a full OAuth token set.

The design assumption is that the device and the user trust each other. The device generates the code. The user enters it. Neither party is deceived about what they are authorizing.

The attack inverts that assumption.

How the inversion works

The attacker calls Microsoft's device code endpoint directly:

POST https://login.microsoftonline.com/common/oauth2/v2.0/devicecodechr(10)client_id=<first-party-app-id>chr(10)scope=https://graph.microsoft.com/.default offline_access

Microsoft responds with a user_code (nine characters, something like BJMRTW66U), a device_code (long opaque string), and a polling interval. The attacker starts polling the token endpoint every few seconds. Then they serve the victim a lure page that shows the code and instructions to enter it on Microsoft's site.

The victim goes to login.microsoftonline.com/common/oauth2/deviceauth. They type or paste the code. They authenticate with their normal credentials and their normal MFA method. When they click Accept, Microsoft's polling response flips from authorization_pending to a full token grant:

  • Access token. Valid 60-90 minutes. Bearer credential scoped to whatever the attacker requested.
  • Refresh token. Valid up to 90 days, rolling. The attacker can silently refresh the access token indefinitely as long as the refresh token has not been revoked and the user's account exists.
  • ID token. Includes email, display name, tenant ID, object ID.

The attacker never sees the password. The victim authenticated to the real Microsoft endpoint with real MFA. From Entra ID's point of view, the authentication was legitimate. The SigninLogs row will show the user's normal IP, their normal device, MFA satisfied, conditional access passed. Nothing about the authentication is anomalous because nothing about the authentication was anomalous. What was anomalous was the authorization — the code the victim entered was generated by the attacker.

What a 90-day refresh token means

The refresh token is the persistence mechanism. With it, the attacker can:

  • Silently retrieve fresh access tokens every 60-90 minutes without user interaction
  • Call Microsoft Graph: read all mail, download all OneDrive files, enumerate the directory
  • On work or school tenants: exchange the access token for new cookies if they have browser infrastructure
  • On consumer accounts (@outlook.com, @hotmail.com): narrower post-capture options, no hybrid token exchange path

The refresh token survives password resets if the user does not also invalidate all sessions. It survives most MFA method changes. The only things that cleanly revoke it are an explicit sign-out from all devices, a Global Admin issuing Revoke-MgUserSignInSession, or a high-risk sign-in risk policy firing.

What changed since Storm-2372

Storm-2372 operated before Microsoft removed the ?otc= query parameter behavior on login.microsoftonline.com/common/oauth2/deviceauth. The original attack could pre-fill the code in the URL so the victim only needed to click once. In our lab we tested four URL variants with Playwright:

  • login.microsoftonline.com/common/oauth2/deviceauth?otc=<CODE> — renders empty input, code not pre-filled
  • microsoft.com/devicelogin?otc=<CODE> — redirects to the above, same result
  • www.microsoft.com/devicelogin?otc=<CODE> — same redirect, same result
  • login.microsoftonline.com/common/oauth2/deviceauth?code=<CODE> — returns raw JSON, no UI

The parameter is preserved in the URL. Microsoft's page ignores it. Auto-fill is dead across every entry point we tested.

This matters for attack friction. The attacker now has to socially engineer the victim into typing or pasting a nine-character code on a Microsoft page. That is a real step the victim has to take with some awareness. A fully automated click-once compromise is no longer available through this flow. The minimum operator effort floor went up.

The attack still works. We ran it end-to-end in the lab and captured a full token set. The lure showed the code, the victim copied it, pasted it on Microsoft's real page, authenticated normally, and the attacker's polling loop returned TOKEN CAPTURED within seconds. The friction is higher. The attack is not broken.

The three attack variants that survive in 2024-2025

Variant 1 — Manual code entry via lure page. Attacker generates the device code, serves a lure page that displays it with a Copy button and a link to Microsoft's deviceauth page. Victim copies the code, clicks the link, pastes the code, authenticates. This is what the lab confirmed working. It requires honest social engineering — the lure has to be believable enough that the victim completes two manual steps. Detection: SigninLogs with AuthenticationProtocol == "deviceCode".

Variant 2 — Full AiTM proxy. The attacker proxies login.microsoftonline.com so the victim's browser session stays within the attacker's domain. The attacker's headless browser holds the device-code-bound session. This regenerates all the engineering complexity of a cookie phishlet and nullifies the simplicity advantage that made device code attractive. Operators who go this route are just running a standard AiTM rig with extra steps. Detection: same AiTM signals (unusual TLS fingerprint, session cookie anomalies, IP mismatch between auth and downstream Graph calls).

Variant 3 — Out-of-band code delivery. No lure URL at all. Attacker calls or messages the victim and reads the code aloud or pastes it in a chat. Pure social engineering. This is the hardest variant to detect from telemetry alone because there is no phishing URL to block. Detection: same SigninLogs signal. Prevention: user awareness training on device code scenarios.

Why phishing-resistant MFA does not stop this

FIDO2 hardware keys, Windows Hello for Business, certificate-based authentication — none of these gate the device code flow. The victim satisfies MFA normally on the real Microsoft sign-in page. The hardware key works exactly as designed. FIDO2 prevents credential replay and real-time phishing of the authentication. It does not prevent a user from typing a code that an attacker generated.

The only thing that stops this at the authentication layer is blocking the device code flow entirely via Conditional Access. That is covered in the mitigations post.

The token scope question

The attacker can only request scopes the app is permitted to acquire. For Microsoft first-party apps (Microsoft Office, Azure CLI, Azure PowerShell), the available scopes are broad: mail read/write, files, directory access. For third-party apps without admin-granted permissions, user-level scopes only.

In our lab we used the Microsoft Office first-party client ID (d3590ed6-...). Multi-tenant, no secret needed, available to any attacker. The scopes it can acquire via delegated device code flow cover essentially the full M365 attack surface for a non-admin user: all mail, all OneDrive files, all calendar, all contacts, directory read.

Want to test your tenant against device code phishing?

We run controlled lab sessions against client tenants to verify whether your CA policies actually block the flow, what your Sentinel detections catch versus miss, and what a successful device code capture looks like in your environment. Fixed scope, fixed price.

Talk to us
Share:
Next Detecting device code phishing in Microsoft Sentinel, one field, one rule

More in Microsoft 365 Device Code Defense