security
Microsoft Device Code Flow DEBULL Abuse: 3 Fixes (2026)

On This Page (13 sections)
Want to stop Google from tracking your phone and browser? We ran 72-hour Wireshark packet captures and tested open-source replacements for Search, Gmail, Drive, Photos, and Android.
read our complete DeGoogle Starter Pack and benchmark dataQuick fix: Block unmanaged device code logins in Microsoft Entra ID. In Conditional Access, create a policy for Device code flow. Require a compliant or hybrid-joined PC. This stops attackers from using stolen codes on remote machines.
Three months ago, our SIEM flagged an alert in our Microsoft 365 tenant. A developer account finished 14 logins in two minutes from Frankfurt. We are based in Dubai.
We checked the sign-in logs right away. The password was valid. The user had passed MFA. No brute-force attacks showed up in our logs. Yet seconds later, an outside script began pulling emails and SharePoint files.
We were facing a live DEBULL Device Code Flow attack.
Standard phishing tricks users into typing passwords on fake websites. DEBULL is different. It uses Microsoft’s real sign-in page at microsoft.com/devicelogin. This flow was built for smart TVs and media consoles without keyboards. The attacker makes a code and tricks your user into typing it.
Below, we break down how the attack works. We share the exact Entra ID Conditional Access rules, PowerShell audit script, and Sentinel KQL queries we used to secure our tenant.
1. How the DEBULL Attack Chain Works
Attackers use the device code flow to generate real Microsoft codes. They trick victims into signing in on Microsoft’s site. Then, Microsoft sends the session token straight to the attacker’s terminal.
+-----------------------------------------------------------------------------------------+
| DEBULL OAUTH 2.0 DEVICE CODE EXPLOITATION PIPELINE |
+-----------------------------------------------------------------------------------------+
[ Attacker Terminal ] [ Victim Workstation ] [ Entra ID / M365 ]
| | |
1. |-- POST /oauth2/v2.0/devicecode --------->| |
| (Client: Microsoft Office / Graph CLI) | |
|<-- Returns: user_code + device_code -----|----------------------------------|
| | |
2. |-- Phishing Email / Teams Lure ---------->| |
| "Urgent: Enter Code [B7K-9XM-4LQ] at: | |
| https://microsoft.com/devicelogin" | |
| | |
3. | |-- Navigates to REAL URL -------->|
| | microsoft.com/devicelogin |
| | Enters Code: [B7K-9XM-4LQ] |
| | Completes Corporate MFA |
| |<-- "Authentication Successful" --|
| | |
4. |-- Continuous Polling Loop -------------->| |
| POST /oauth2/v2.0/token | |
| (grant_type=urn:ietf:params:oauth:...) | |
|<-- Issues: Access Token + Refresh Token (PRT) ------------------------------|
| |
5. |-- Graph API Exfiltration (Emails, Teams, SharePoint) ---------------------->|
Phase 1: The Attacker Starts the Request
The attacker runs an exploit script like DEBULL or TokenTactician. The tool sends a request to Microsoft’s login server:
POST /organizations/oauth2/v2.0/devicecode HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c
&scope=https://graph.microsoft.com/.default offline_access
Microsoft responds with two codes:
device_code: A private key kept by the attacker’s script.user_code: A short 9-character code (likeB7K-9XM-4LQ) shown to the user.
Phase 2: Sending the Lure
The attacker sends an urgent email or Teams message. It looks like an IT alert: “Security check needed. Go to microsoft.com/devicelogin and enter code B7K-9XM-4LQ.”
Phase 3: The Victim Signs In
The user clicks the link. The URL is 100% real. There are no fake domains or bad certificates. Email security tools trust microsoft.com and deliver the message.
The user enters the code. They approve their normal MFA prompt. Microsoft confirms the sign-in and links it to the waiting device code.
Phase 4: Token Capture
The attacker’s script checks the server every few seconds. Once the user approves the prompt, Microsoft sends the tokens back to the attacker.
The attacker receives both an Access Token and a Primary Refresh Token. The stolen token can read emails, download SharePoint files, and access team data.
2. Attack vs Defense Comparison
Standard email filters fail because the sign-in link is genuine. Only device-based access controls stop this attack.
| Attack Step | Default M365 Setup | Hardened Zero Trust Setup | Security Result |
|---|---|---|---|
| Phishing Email | Gateways trust microsoft.com. Emails hit the inbox. | Inbound filters scan body text for login links with codes. | Warns security teams before users click. |
| Code Entry | Any home PC or phone can approve the code. | Conditional Access blocks logins from unmanaged devices. | Blocks remote attacks. Users cannot sign in on personal devices. |
| MFA Prompt | User approves standard MFA on their phone. | Enforces phishing-resistant keys (FIDO2 or Windows Hello). | Stops token replay from remote IP ranges. |
| Token Theft | Refresh tokens stay valid for up to 90 days. | Token protection binds tokens to verified device IPs. | Drops tokens if the attacker IP changes. |
| Internal Discovery | Attacker queries Graph API to list mailboxes. | Restricts user permissions to view other directory profiles. | Slows down lateral movement inside the tenant. |
3. How We Blocked the Attack in Entra ID
You can stop this threat with a single Conditional Access policy. We restrict device code logins to company-managed hardware.
In the Microsoft Entra Admin Center, go to Protection → Conditional Access → New Policy:
- Policy Name:
SEC-CA-Block-Unmanaged-DeviceCodeFlow. - Users: Select All users. Exclude your emergency break-glass admin accounts.
- Target resources: Select All cloud apps.
- Conditions → Client apps: Pick Mobile apps and desktop clients and Other clients.
- Conditions → Authentication flows: Set Device code flow to Include.
- Grant: Click Grant access, then select:
- Require device to be marked as compliant
- Require Microsoft Entra hybrid joined device
- Choose Require one of the selected controls.
Here is our production JSON policy export:
{
"displayName": "SEC-CA-Block-Unmanaged-DeviceCodeFlow",
"state": "enabled",
"conditions": {
"users": {
"includeUsers": ["All"],
"excludeUsers": ["breakglass-admin-01[at]yourdomain.com"]
},
"applications": {
"includeApplications": ["All"]
},
"authenticationFlows": {
"transferMethods": ["deviceCodeFlow"]
}
},
"grantControls": {
"operator": "OR",
"builtInControls": ["compliantDevice", "domainJoinedDevice"]
}
}
If a user opens microsoft.com/devicelogin on a personal phone, Entra ID checks the device status. Unmanaged devices lack Intune certificates. Entra ID stops the login with error AADSTS53000 (Device is not in required device state).
4. PowerShell Audit Script to Find Stolen Tokens
We wrote audit_entra_device_code.ps1 to scan our tenant. It checks the last 30 days of sign-ins and flags logins from unusual IPs.
<#
.SYNOPSIS
Microsoft Entra ID Device Code Flow Audit & Threat Hunter (PraveenTechWorld 2026).
.DESCRIPTION
Audits the last 30 days of sign-in logs for deviceCode authentication events,
detects multi-IP anomalies, and verifies Conditional Access policy enforcement.
#>
[CmdletBinding()]
param(
[int]$DaysToAudit = 30,
[switch]$ExportCsv
)
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " PraveenTechWorld Entra ID Device Code Security Inspector " -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
# Verify Microsoft.Graph module
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Identity.SignIns)) {
Write-Error "Microsoft.Graph.Identity.SignIns module not found. Run: Install-Module Microsoft.Graph -Scope CurrentUser"
exit 1
}
# Connect with required audit scopes
Write-Host "`n[+] Connecting to Microsoft Graph API..." -ForegroundColor Yellow
Connect-MgGraph -Scopes "AuditLog.Read.All", "Policy.Read.All" -NoWelcome
$startDate = (Get-Date).AddDays(-$DaysToAudit).ToString("yyyy-MM-ddTHH:mm:ssZ")
Write-Host "[+] Querying Entra ID Sign-In logs since: $startDate" -ForegroundColor Yellow
# Query sign-in logs using authenticationProtocol filter
$filter = "createdDateTime ge $startDate and authenticationProtocol eq 'deviceCode'"
try {
$deviceCodeSignins = Get-MgAuditLogSignIn -Filter $filter -All -ErrorAction Stop
} catch {
Write-Warning "Direct filter failed; retrieving general sign-ins and filtering client-side..."
$deviceCodeSignins = Get-MgAuditLogSignIn -Top 1000 | Where-Object { $_.AuthenticationProtocol -eq "deviceCode" }
}
Write-Host "[*] Discovered $($deviceCodeSignins.Count) Device Code sign-in events." -ForegroundColor Cyan
if ($deviceCodeSignins.Count -eq 0) {
Write-Host "`n[OK] No Device Code authentication events recorded in the audit window." -ForegroundColor Green
exit 0
}
# Analyze for multi-IP and anomalous token issuance
$suspiciousEvents = @()
$groupedByUser = $deviceCodeSignins | Group-Object UserPrincipalName
foreach ($group in $groupedByUser) {
$user = $group.Name
$uniqueIPs = $group.Group | Select-Object -ExpandProperty IpAddress -Unique
$successes = $group.Group | Where-Object { $_.Status.ErrorCode -eq 0 }
if ($uniqueIPs.Count -gt 1 -or $successes.Count -gt 3) {
Write-Warning "[ALERT] Suspicious Device Code activity detected for: $user"
Write-Warning " Unique IPs: $($uniqueIPs -join ', ')"
Write-Warning " Successful Token Grants: $($successes.Count)"
$suspiciousEvents += [PSCustomObject]@{
UserPrincipalName = $user
TotalAttempts = $group.Count
SuccessfulGrants = $successes.Count
UniqueIPCount = $uniqueIPs.Count
IPAddresses = ($uniqueIPs -join '; ')
FirstSeen = ($group.Group | Measure-Object -Property CreatedDateTime -Minimum).Minimum
LastSeen = ($group.Group | Measure-Object -Property CreatedDateTime -Maximum).Maximum
}
}
}
Write-Host "`n============================================================" -ForegroundColor Cyan
Write-Host " Audit Summary: $($suspiciousEvents.Count) Accounts Exhibiting High-Risk Patterns " -ForegroundColor Yellow
Write-Host "============================================================" -ForegroundColor Cyan
if ($suspiciousEvents.Count -gt 0) {
$suspiciousEvents | Format-Table -AutoSize
if ($ExportCsv) {
$outPath = ".\Entra_DeviceCode_ThreatReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$suspiciousEvents | Export-Csv -Path $outPath -NoTypeInformation
Write-Host "[+] Exported forensic threat report to: $outPath" -ForegroundColor Green
}
}
How to Run the Tool
Run the script from an admin PowerShell window:
Set-ExecutionPolicy RemoteSigned -Scope Process -Force
.\audit_entra_device_code.ps1 -DaysToAudit 30 -ExportCsv
If you see successful logins from unknown IPs, revoke the user’s active sessions immediately. Go to Users → Select User → Revoke sessions in the admin center.
5. Detecting Attacks with Microsoft Sentinel KQL
Real-time alerts help security teams act before data leaves the tenant. We use two custom KQL rules in Microsoft Sentinel.
Add this rule under Microsoft Sentinel → Analytics → Create Scheduled Query Rule:
// KQL Query: Flag High-Risk DEBULL Device Code Phishing Patterns
// Runs every 1 hour, inspecting 2-hour lookback windows
SigninLogs
| where TimeGenerated >= ago(2h)
| where AuthenticationProtocol =~ "deviceCode"
| extend DeviceDetail = tostring(DeviceDetail.operatingSystem)
| extend Country = tostring(LocationDetails.countryOrRegion)
| summarize
TotalAttempts = count(),
SuccessfulLogins = countif(ResultType == 0),
FailedLogins = countif(ResultType != 0),
DistinctIPs = dcount(IPAddress),
IPList = make_set(IPAddress),
AppList = make_set(AppDisplayName)
by UserPrincipalName, bin(TimeGenerated, 30m)
| where SuccessfulLogins > 0 and (DistinctIPs > 1 or TotalAttempts >= 3)
| extend AccountCustomEntity = UserPrincipalName
| extend IPCustomEntity = tostring(IPList[0])
| project TimeGenerated, AccountCustomEntity, IPCustomEntity, TotalAttempts, SuccessfulLogins, DistinctIPs, IPList, AppList
| sort by TimeGenerated desc
Catching Token Replay
After stealing a token, the attacker calls the Microsoft Graph API from their own server. We catch this with a second query on AADNonInteractiveUserSignInLogs:
// KQL Query: Detect Non-Interactive Graph API Usage Post-Device Login
let deviceLogins = SigninLogs
| where TimeGenerated >= ago(24h)
| where AuthenticationProtocol =~ "deviceCode" and ResultType == 0
| project DeviceLoginTime = TimeGenerated, UserPrincipalName, OriginalIP = IPAddress;
AADNonInteractiveUserSignInLogs
| where TimeGenerated >= ago(24h)
| where AppDisplayName has "Graph" or AppDisplayName has "PowerShell"
| join kind=inner (deviceLogins) on UserPrincipalName
| where TimeGenerated between (DeviceLoginTime .. (DeviceLoginTime + 2h))
| where IPAddress != OriginalIP
| project TimeGenerated, UserPrincipalName, InitialAuthIP = OriginalIP, SubsequentGraphIP = IPAddress, AppDisplayName, ResourceDisplayName
If this query returns rows, an outside server is using an internal user’s token.
6. Fixing Common Workflow Issues
Blocking device code flow can disrupt automated scripts or meeting room hardware. Here is how we resolved both cases:
- CLI Build Pipelines: Developers using automated scripts in Docker often relied on
az login --use-device-code.- Fix: Switch your pipelines to Azure Managed Identities or Service Principals with Certificates. Never use personal user accounts for automated builds.
- Meeting Room Displays and Smart TVs: Conference panels without physical keyboards use device code logins during setup.
- Fix: Put conference hardware into a dedicated Intune group. Enroll them on a private setup network using an admin account.
7. Security Hardening Checklist
Follow this checklist to lock down your Microsoft 365 tenant:
- Scan Past Sign-Ins: Run our PowerShell script to review the last 30 days of logins.
- Enforce Conditional Access: Require compliant devices for all device code sign-ins.
- Revoke Risky Sessions: End active tokens for any user with risky logins.
- Enable Sentinel Alerts: Deploy our KQL queries to spot multi-IP token generation.
- Update Pipelines: Move developer scripts to Managed Identities instead of personal accounts.
For more enterprise security guides from our team, check out:
Get Our Sysadmin & AI Runbooks Direct to Your Inbox
Join 2,500+ engineers receiving our weekly PowerShell automation scripts, root cause analyses, and hardware diagnostic playbooks.
Frequently Asked Questions: Microsoft Device Code Flow DEBULL Abuse: 3 Fixes (2026)
What is Microsoft 365 Device Code Flow phishing?
How do you disable Device Code Flow in Microsoft Entra ID?
Does blocking device code flow break developer PowerShell tools?
Why does multi-factor authentication (MFA) fail to prevent DEBULL token theft?
Official Technical References
- OAuth 2.0 Device Authorization Grant (RFC 8628) — IETF
- Microsoft Entra ID: Device Code Flow Security Guidance — Microsoft Learn
- CISA Alert: Threat Actors Exploiting OAuth Device Code Authentication — Cybersecurity & Infrastructure Security Agency
Add PraveenTechWorld as a preferred source in your Google Search results.
Explore more: Browse all security guides or check related articles below.


