← Back to SOC feed Coverage →

Possible device code phishing attempts

kql MEDIUM Azure-Sentinel
T1566
UrlClickEvents
huntingmicrosoftofficialphishing
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-05-13T11:00:00Z · Confidence: medium

Hunt Hypothesis

Adversaries may attempt to steal device codes through phishing to gain unauthorized access to user accounts. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify and mitigate potential credential compromise and lateral movement.

KQL Query

let suspiciousUserClicks = materialize(UrlClickEvents
  | where ActionType in ("ClickAllowed", "UrlScanInProgress", "UrlErrorPage") or IsClickedThrough != "0"
  | where UrlChain has_any ("microsoft.com/devicelogin", "login.microsoftonline.com/common/oauth2/deviceauth")
  | extend AccountUpn = tolower(AccountUpn)
  | project ClickTime = Timestamp, ActionType, UrlChain, NetworkMessageId, Url, AccountUpn);
//Check for Risky Sign-In in the short time window
let interestedUsersUpn = suspiciousUserClicks
  | where isnotempty(AccountUpn)
  | distinct AccountUpn;
let suspiciousSignIns = materialize(AADSignInEventsBeta
  | where ErrorCode == 0
  | where AccountUpn in~ (interestedUsersUpn)
  | where RiskLevelDuringSignIn in (10, 50, 100)
  | extend AccountUpn = tolower(AccountUpn)
  | join kind=inner suspiciousUserClicks on AccountUpn
  | where (Timestamp - ClickTime) between (-2min .. 7min)
  | project Timestamp, ReportId, ClickTime, AccountUpn, RiskLevelDuringSignIn, SessionId, IPAddress, Url
);
//Validate errorCode 50199 followed by success in 5 minute time interval for the interested user, which suggests a pause to input the code from the phishing email
let interestedSessionUsers = suspiciousSignIns
  | where isnotempty(AccountUpn)
  | distinct AccountUpn;
let shortIntervalSignInAttemptUsers = materialize(AADSignInEventsBeta
  | where AccountUpn in~ (interestedSessionUsers)
  | where ErrorCode in (0, 50199)
  | summarize ErrorCodes = make_set(ErrorCode) by AccountUpn, CorrelationId, SessionId
  | where ErrorCodes has_all (0, 50199)
  | distinct AccountUpn);
suspiciousSignIns
| where AccountUpn in (shortIntervalSignInAttemptUsers)

Analytic Rule Definition

id: ad76e484-f159-4d23-99ee-e734f0b8b60b
name: Possible device code phishing attempts
description: |
  This query helps hunting for possible device code Phishing attempts
description-detailed: |
  This query helps hunting for possible device code Phishing attempts. More information: https://www.microsoft.com/en-us/security/blog/2025/02/13/storm-2372-conducts-device-code-phishing-campaign/
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - UrlClickEvents
  - AADSignInEventsBeta
tactics:
  - InitialAccess
relevantTechniques:
  - T1566
query: |
  let suspiciousUserClicks = materialize(UrlClickEvents
    | where ActionType in ("ClickAllowed", "UrlScanInProgress", "UrlErrorPage") or IsClickedThrough != "0"
    | where UrlChain has_any ("microsoft.com/devicelogin", "login.microsoftonline.com/common/oauth2/deviceauth")
    | extend AccountUpn = tolower(AccountUpn)
    | project ClickTime = Timestamp, ActionType, UrlChain, NetworkMessageId, Url, AccountUpn);
  //Check for Risky Sign-In in the short time window
  let interestedUsersUpn = suspiciousUserClicks
    | where isnotempty(AccountUpn)
    | distinct AccountUpn;
  let suspiciousSignIns = materialize(AADSignInEventsBeta
    | where ErrorCode == 0
    | where AccountUpn in~ (interestedUsersUpn)
    | where RiskLevelDuringSignIn in (10, 50, 100)
    | extend AccountUpn = tolower(AccountUpn)
    | join kind=inner suspiciousUserClicks on AccountUpn
    | where (Timestamp - ClickTime) between (-2min .. 7min)
    | project Timestamp, ReportId, ClickTime, AccountUpn, RiskLevelDuringSignIn, SessionId, IPAddress, Url
  );
  //Validate errorCode 50199 followed by success in 5 minute time interval for the interested user, which suggests a pause to input the code from the phishing email
  let interestedSessionUsers = suspiciousSignIns
    | where isnotempty(AccountUpn)
    | distinct AccountUpn;
  let shortIntervalSignInAttemptUsers = materialize(AADSignInEventsBeta
    | where AccountUpn in~ (interestedSessionUsers)
    | where ErrorCode in (0, 50199)
    | summarize ErrorCodes = make_set(ErrorCode) by AccountUpn, CorrelationId, SessionId
    | where ErrorCodes has_all (0, 50199)
    | distinct AccountUpn);
  suspiciousSignIns
  | where AccountUpn in (shortIntervalSignInAttemptUsers)
version: 1.0.0

Required Data Sources

Sentinel TableNotes
UrlClickEventsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/Microsoft 365 Defender/Email and Collaboration Queries/Phish/Possible device code phishing attempts.yaml