Possible device code phishing attempts


Description

This query helps hunting for possible device code Phishing attempts

Query · kql

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(EntraIdSignInEvents
  | 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(EntraIdSignInEvents
  | 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)
Raw source Possible device code phishing attempts · KQL
Esc
Published by Azure/Azure-Sentinel ↗, licensed under MIT ↗. Reproduced here unmodified.
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
  - EntraIdSignInEvents
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(EntraIdSignInEvents
    | 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(EntraIdSignInEvents
    | 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

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.