Tutorials
Investigating a suspicious sign-in in Entra ID, step by step
What to check in the sign-in logs, which KQL queries to run, how to compare against the user's normal behavior, and when to revoke sessions and reset access.
Published 4 min readBeginner
An alert lands in your queue: a risky sign-in for one of your users, from a country they have never logged in from. It might be an attacker holding a stolen password. It might also be the user on holiday, a new VPN, or a mobile carrier routing traffic through another region.
Your job is not to guess. It is to collect enough evidence to make a confident call, and then act in proportion to what you found. This walkthrough follows the order most SOC analysts use, with the KQL to run at each step.
Step 1: read the alert before you query anything
Open the alert or the risk detection in Entra ID Protection and write down four facts: the user, the time, the IP address and the detection type. Detection types such as unfamiliar sign-in properties, anonymous IP address or atypical travel each point you toward a different question.
Then check two things that change the urgency of everything else:
- Did the sign-in succeed? A blocked attempt with the correct password is still serious, but it means the attacker did not get in through this sign-in.
- Was MFA satisfied? A successful single-factor sign-in from an unknown location is far more urgent than one that was challenged and failed.
Step 2: pull the sign-in and the days around it
Start with everything the account did in the last week. Seeing the suspicious sign-in next to normal activity makes anomalies much easier to spot.
SigninLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName =~ "jordan.lee@contoso.com"
| project TimeGenerated, IPAddress, Location, AppDisplayName, ClientAppUsed,
ResultType, ResultDescription,
AuthenticationRequirement, ConditionalAccessStatus,
RiskLevelDuringSignIn,
OS = tostring(DeviceDetail.operatingSystem), Browser = tostring(DeviceDetail.browser)
| order by TimeGenerated descThe highlighted lines are the ones that usually decide the case. Read them like this:
ResultTypeis0for success. Codes such as50126(wrong password) or500121(strong authentication failed) tell you how far the attempt got. Many50126failures across different accounts from one IP address point to a password spray.AuthenticationRequirementshows whether the sign-in needed one factor or several.ConditionalAccessStatustells you whether your Conditional Access policies applied, and whether they blocked the attempt.- The operating system and browser should look like the user's normal devices. A Windows user suddenly signing in from a Linux machine with a scripted user agent deserves attention.
Step 3: compare against the user's normal behavior
One unusual sign-in means little without a baseline. This query summarizes where the user successfully signed in over the previous 30 days, excluding the last day so the suspicious activity does not pollute the baseline.
let user = "jordan.lee@contoso.com";
SigninLogs
| where TimeGenerated between (ago(30d) .. ago(1d))
| where UserPrincipalName =~ user and ResultType == "0"
| summarize SignIns = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
by Location, IPAddress
| order by SignIns descIf the suspicious IP address or country appears here with dozens of sign-ins over several weeks, you are probably looking at normal behavior that the detection did not recognize yet. If it has never appeared, keep going.
Ask the user, but through a separate channel
Contacting the user is a valid investigation step, not a shortcut. Use a channel the attacker cannot control: a phone call to a known number or a message to their manager. Never ask "was this you?" by email to the account you suspect is compromised.
Step 4: look for what happened after the sign-in
Attackers who get in tend to do the same few things quickly: register their own MFA method so they can return, and hide their tracks in the mailbox. Check both.
let user = "jordan.lee@contoso.com";
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName in ("User registered security info", "User changed default security info", "User deleted security info")
| where tostring(TargetResources[0].userPrincipalName) =~ user
| project TimeGenerated, OperationName, Result, InitiatedBy = tostring(InitiatedBy.user.userPrincipalName)let user = "jordan.lee@contoso.com";
OfficeActivity
| where TimeGenerated > ago(7d)
| where Operation in ("New-InboxRule", "Set-InboxRule", "UpdateInboxRules")
| where UserId =~ user
| project TimeGenerated, Operation, ClientIP, ParametersStep 5: decide and contain
At this point you should be able to place the case in one of three buckets: benign (expected travel or a known network), suspicious but unconfirmed, or confirmed compromise. For a confirmed compromise, contain in this order:
Revoke the user's sessions
This invalidates refresh tokens, so the attacker has to sign in again.
revoke-sessions.ps1 Connect-MgGraph -Scopes "User.RevokeSessions.All" Revoke-MgUserSignInSession -UserId "jordan.lee@contoso.com"Reset the password and review MFA methods
Remove any authentication method the user does not recognize, especially one registered during the suspicious window.
Confirm the user as compromised in Identity Protection
This raises the user risk, feeds Microsoft's detections and triggers any risk-based Conditional Access policies you have.
Clean up and document
Delete malicious inbox rules, check for mail forwarding and OAuth app consents, and write down the timeline while it is fresh.
What to write in the ticket
A good investigation note lets someone else reach the same conclusion without redoing your work. Include the alert facts from step 1, the result of the baseline comparison, anything you found after the sign-in, the actions you took with timestamps, and your verdict. When the next alert for this user arrives, that note will save the next analyst most of the work.
Terms in this article
- KQL
- The read-only query language used by Microsoft Sentinel, Defender XDR advanced hunting, Azure Monitor and Azure Data Explorer.
- Multi-factor authentication
- Signing in with two or more different kinds of proof, such as a password plus a phone or security key, so a stolen password alone is not enough.
- Conditional Access
- Microsoft Entra ID's policy engine that decides, at each sign-in, whether to allow access, require MFA or another control, or block.
- Password spray
- An attack that tries a few common passwords against many accounts, staying under lockout limits that per-account guessing would trigger.
- Business email compromise
- Fraud in which an attacker impersonates or takes over a trusted business email account to redirect payments or obtain sensitive data.
Last reviewed on
Keep reading
Certifications
SC-200 study plan for SOC beginners