EvolitBlogContact

Device Code Flow Phishing: How Attackers Steal M365 Tokens While Bypassing MFA — and How to Stop Them

Device code flow phishing lets attackers take over M365 accounts even when MFA is fully enforced — the victim authenticates on a real Microsoft page, the attacker gets the tokens. Storm-2372 has been running this campaign since August 2024. Learn how to detect existing compromise in your Entra ID logs, block the attack via Conditional Access, and avoid breaking Teams Rooms in the process.

Device Code Flow Phishing: How Attackers Steal M365 Tokens While Bypassing MFA — and How to Stop Them

TL;DR: Device code flow phishing lets attackers take over M365 accounts even when MFA is fully enforced. The victim authenticates on a real Microsoft page — the attacker gets the tokens. Storm-2372 (Russia-linked) has been running this campaign since August 2024, with scale exploding through PhaaS kits in 2026. Blocking via Conditional Access takes five minutes, but the edge cases around Teams Rooms and admin tooling will bite you if you skip the prep work.

The Problem

A user files a ticket: suspicious sign-ins from Southeast Asia at 3 AM. You pull the Entra ID sign-in logs — successful authentication, MFA completed, no risk flag. Impossible travel detection did not fire. How?

This is device code flow phishing. The user received a message — via Teams, WhatsApp, or email — asking them to enter a short code at microsoft.com/devicelogin to confirm access to a document or meeting invite. They went to the real Microsoft page, typed the code, signed in with their real password, and approved the MFA prompt. A completely legitimate authentication flow. Except the code was generated by the attacker, and the access tokens went straight to the attacker's polling server.

Since August 2024, Microsoft has been tracking Storm-2372 — a Russia-linked threat actor — actively exploiting this technique against governments, NGOs, technology firms, defense contractors, and energy companies across Europe, North America, and the Middle East. In February 2025 the campaign evolved: attackers began abusing the Microsoft Authentication Broker client ID (29d9ed98-a469-4536-ade2-f981bc1d605e) to register attacker-controlled devices and steal Primary Refresh Tokens (PRTs), which provide SSO-level access across all M365 resources.

By 2026, Phishing-as-a-Service kits like EvilTokens have automated device code phishing at industrial scale. Lure templates impersonate Adobe Acrobat Sign, DocuSign, OneDrive shared files, SharePoint access requests, email quarantine notices, and calendar invites. Hundreds of organizations are compromised daily.

Why This Works

Device code flow (RFC 8628) was designed for a legitimate and specific purpose: enabling authentication on devices without keyboards or browsers — printers, Smart TVs, Xbox consoles, IoT devices, Microsoft Teams Rooms conference systems.

The legitimate flow:

  1. The device (e.g., a smart TV) sends a request to Entra ID and receives a device_code (8-10 characters) and a user_code
  2. The TV displays: "Go to microsoft.com/devicelogin and enter code XXXX-XXXX"
  3. The user authenticates in their browser — with password and MFA prompt
  4. The TV polls the token endpoint every few seconds and receives access_token + refresh_token after successful user auth

The attack vector: Step 1 can be executed by anyone, not just a legitimate device. An attacker generates the device code, sends the user_code to the victim via phishing, and waits for the tokens to arrive. The victim sees login.microsoftonline.com — the real site, with Microsoft's TLS certificate. They authenticate normally. Tokens go to the attacker.

Critical technical details that make detection hard:

  • Resulting tokens carry amr: ["mfa"] in their claims — Entra ID sign-in logs show a normal, MFA-protected sign-in with no anomaly indicator.
  • The device code expires in 15 minutes, but attackers generate fresh codes and send additional lures until one lands.
  • Once the attacker holds a refresh token, it is valid for 90 days by default and silently renews access tokens without any further user interaction.
  • The new February 2025 variant: after receiving tokens, Storm-2372 authenticates through Microsoft Authentication Broker to register an attacker-controlled device and obtain a PRT — which gives SSO access that persists even if you revoke the original refresh token.

Remediation: Step by Step

Step 1: Check if Microsoft Already Deployed the Managed Policy

Since 2025, Microsoft has been rolling out a managed Conditional Access policy called "Block device code flow" to tenants as part of the Secure Future Initiative. Do not assume it is protecting you — check its actual state:

  1. Entra Admin Center → Protection → Conditional Access → Policies
  2. Look for "Block device code flow" — it may be labeled "Microsoft-managed"
  3. Check the state: Report-only or On

If it is in Report-onlyyou are not protected. The policy is logging what it would block, not actually blocking anything. Switch it to On after reading the edge cases section below.

Step 2: Hunt for Existing Compromise in Sign-in Logs

Before implementing the block, determine whether device code phishing has already hit your tenant. Run this KQL in Microsoft Sentinel or Log Analytics:

SigninLogs
| where TimeGenerated > ago(90d)
| where AuthenticationProtocol == "deviceCode"
| project TimeGenerated, UserPrincipalName, AppDisplayName,
          IPAddress, Location, RiskLevelDuringSignIn
| order by TimeGenerated desc

Expected (benign) results: sign-ins from Azure CLI, Az PowerShell, Connect-MgGraph — from admin IP addresses during business hours. Investigate immediately: sign-ins from unfamiliar countries, unknown application names, or outside working hours.

Step 3: Build an Exclusion Group Before Blocking

Identify every account that legitimately uses device code flow in your environment:

  • Teams Rooms resource accounts on Android-based devices
  • Teams Phone, Teams Panel, Teams Display device accounts
  • Printers and IoT devices authenticating against M365
  • Service accounts used by Azure CLI or PowerShell automation (temporary — migrate these off device code)

Create a dedicated group in Entra ID: SG-DeviceCodeFlow-Allowed. Add only those accounts plus your break-glass accounts. Do not skip this step — enabling the policy without it will generate incident tickets from meeting room users within the hour.

Step 4: Create the Blocking Policy via Portal

  1. Entra ID → Conditional Access → New policy
  2. Name: Block Device Code Flow — All Users
  3. Users: All users
  4. Exclude: SG-DeviceCodeFlow-Allowed + break-glass accounts
  5. Conditions → Authentication flows → Configure: Yes → Device code flow: Yes
  6. Grant → Block access
  7. State: Report-only for 7-14 days

During the report-only period, open the Conditional Access Insights and Reporting workbook and check which sign-ins would have been blocked. Add any unexpected legitimate accounts to the exclusion group before going live.

Step 5: Deploy via PowerShell for Repeatability

# Requires: Microsoft.Graph.Identity.SignIns module
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess","Policy.Read.All"

$excludeGroupId = (Get-MgGroup -Filter "displayName eq 'SG-DeviceCodeFlow-Allowed'").Id

$params = @{
    displayName = "Block Device Code Flow - All Users"
    state = "enabledForReportingButNotEnforced"
    conditions = @{
        users = @{
            includeUsers  = @("All")
            excludeGroups = @($excludeGroupId)
        }
        authenticationFlows = @{
            transferMethods = "deviceCodeFlow"
        }
    }
    grantControls = @{
        operator        = "OR"
        builtInControls = @("block")
    }
}

New-MgIdentityConditionalAccessPolicy -BodyParameter $params

After 14 days in report-only mode, promote to enforced:

$policy = Get-MgIdentityConditionalAccessPolicy |
    Where-Object { $_.DisplayName -eq "Block Device Code Flow - All Users" }

Update-MgIdentityConditionalAccessPolicy `
    -ConditionalAccessPolicyId $policy.Id `
    -BodyParameter @{ state = "enabled" }

Step 6: Revoke Compromised Sessions

If Step 2 surfaced suspicious sign-ins, take immediate action:

# Revokes all refresh tokens — attacker access dies on next token refresh
Revoke-MgUserSignInSession -UserId "user@contoso.com"

# Audit devices registered by this user in the last 30 days
Get-MgUserRegisteredDevice -UserId "user@contoso.com" |
    Select-Object DisplayName, OperatingSystem, ApproximateLastSignInDateTime

Critical timing note: Revoke-MgUserSignInSession invalidates refresh tokens immediately but does nothing to existing access tokens. Those remain valid for up to 1 hour. During that window an attacker holding a valid access token can still call Microsoft Graph, Exchange Online, or SharePoint APIs.

Common Traps and Edge Cases

Trap 1: Teams Rooms and Teams Phones Go Offline

This is the most common post-deployment incident, and it happened at scale when Microsoft rolled out the managed policy in 2025. Teams Android devices — Teams Rooms on Android, Teams Phones, Teams Panels, Teams Displays — all use device code flow to authenticate their resource accounts. These devices do not support interactive browser authentication.

If devices have gone offline after your policy activated:

  1. Identify affected resource accounts: Get-CsOnlineUser | Where-Object { $_.AccountType -eq "ResourceAccount" }
  2. Add them to SG-DeviceCodeFlow-Allowed
  3. Wait for policy propagation (up to 10 minutes), then reboot devices 1-3 times
  4. If rebooting does not restore sign-in, a factory reset is required — the device stores invalid authentication state that a simple reboot cannot clear

Trap 2: You Lock Out Azure CLI, Az PowerShell, and Graph PowerShell

az login, Connect-AzAccount -UseDeviceCode, Connect-MgGraph -UseDeviceCode, Connect-ExchangeOnline -UseDeviceCode — all use device code flow. The admin who deploys the block policy on Monday may find their automation scripts failing on Tuesday. The error: AADSTS53003: Access has been blocked by Conditional Access policies.

Long-term fix: migrate automation to Managed Identity (for Azure-hosted workloads) or Service Principal + certificate — neither uses device code flow. For interactive sessions, switch to browser-based auth: Connect-MgGraph without -UseDeviceCode opens a browser window for interactive sign-in. During migration, temporarily add automation accounts to the exclusion group.

Trap 3: The Microsoft-Managed Policy Is Not Enforced by Default

Three facts every admin needs to know:

  • Its default state upon first deployment to your tenant is Report-only — Microsoft does not switch it to enforced on your behalf
  • Once you manually change its state, Microsoft stops auto-managing it and the policy stays exactly where you left it
  • Rollout is gradual and staggered — your tenant may not have the policy while a neighboring tenant does

Do not rely on it as your sole protection. Create an independent policy under your own control and treat the Microsoft-managed one as a secondary signal.

Trap 4: Blocking Device Code Flow Does Not Undo Existing PRT Compromises

Since February 2025, Storm-2372 has evolved to use the Microsoft Authentication Broker during device code phishing to register an attacker-controlled device and obtain a Primary Refresh Token. A PRT provides SSO access to all M365 resources. If an attacker obtained a PRT before your policy was activated, the block does nothing retroactively.

Audit device registrations from the past 30 days:

Get-MgDevice -All |
    Where-Object { $_.ApproximateLastSignInDateTime -gt (Get-Date).AddDays(-30) } |
    Select-Object DisplayName, OperatingSystem, ApproximateLastSignInDateTime,
        @{Name="Owner"; Expression={
            (Get-MgDeviceRegisteredOwner -DeviceId $_.Id).AdditionalProperties.userPrincipalName
        }} |
    Sort-Object ApproximateLastSignInDateTime -Descending

Any device registered in the last month that you do not recognize — especially with operating systems outside your standard fleet — is a candidate for immediate deletion and further investigation. After deleting a suspicious device, revoke the associated user's sign-in sessions.

Summary

  • Device code flow phishing bypasses MFA — the victim performs real, legitimate MFA; tokens go to the attacker; Entra ID logs show a clean sign-in with no anomaly indicators
  • Storm-2372 (Russia-linked) has been running this campaign since August 2024; the attack surface exploded through PhaaS kits in 2026
  • Blocking via Conditional Access requires Entra ID P1 minimum — included in M365 Business Premium, E3, and E5 plans
  • Before turning on the policy: run the KQL hunt across 90 days of sign-in logs and build an exclusion group for Teams Rooms resource accounts
  • The Microsoft-managed policy defaults to Report-only — verify its state is actually On, do not assume
  • After any suspected incident: revoke sign-in sessions, then audit device registrations from the past 30 days — the PRT-theft variant requires this extra step