Add hunting queries: Entra ID account takeover hunting pack (3 queries)#14335
Open
descambiado wants to merge 3 commits into
Open
Add hunting queries: Entra ID account takeover hunting pack (3 queries)#14335descambiado wants to merge 3 commits into
descambiado wants to merge 3 commits into
Conversation
Detects successful device code flow sign-ins where the ASN has not been seen for that user in the preceding 30 days. Targets device code phishing (T1528, T1078.004) where an attacker initiates the flow and tricks the victim into completing authentication.
… one hour Correlates SP creation with admin consent or app role assignment within a 1-hour window. Post-compromise persistence pattern where an attacker with Application Admin rights registers an app and immediately grants it tenant-wide permissions (T1528, T1098.003).
…e accounts Detects a single actor resetting passwords for 3+ distinct accounts within one hour. Targets attackers with User Administrator or Helpdesk Admin rights using bulk password resets for account takeover (T1098, T1078.004).
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds three new Microsoft Sentinel hunting queries focused on Entra ID account takeover and post-compromise persistence patterns, using only core Entra ID/Azure AD data sources.
Changes:
- Added a SigninLogs-based hunt for successful device code sign-ins from an ASN not previously seen for the user.
- Added an AuditLogs-based hunt correlating new service principal creation with rapid admin consent/app role assignment.
- Added an AuditLogs-based hunt detecting bulk password resets by a single actor within an hour.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| Hunting Queries/MultipleDataSources/DeviceCodeSignInFromUnseenASN.yaml | New SigninLogs-based device code phishing hunt using unseen ASN history per user. |
| Hunting Queries/AuditLogs/NewServicePrincipalGrantedAdminConsent.yaml | New AuditLogs correlation hunt for rapid post-registration admin consent/app role assignment on a new SP. |
| Hunting Queries/AuditLogs/BulkPasswordResetByActor.yaml | New AuditLogs hunt for bulk admin-initiated password resets by a single actor over a short window. |
Comment on lines
+4
to
+8
| Identifies successful device code flow sign-ins where the autonomous system number | ||
| has not been observed for that user in the preceding 30 days. Device code phishing | ||
| involves an attacker initiating a device code request and tricking a target into | ||
| completing authentication, giving the attacker a valid session token without knowing | ||
| the user's credentials. |
Comment on lines
+4
to
+7
| Identifies service principals that received an app role assignment or admin consent | ||
| within one hour of being registered in the tenant. Registering an application and | ||
| immediately granting it admin consent is a technique used to establish persistent | ||
| OAuth access after compromising a privileged account. |
Comment on lines
+4
to
+7
| Identifies a single actor resetting passwords for three or more distinct accounts | ||
| within a one-hour window. An attacker with User Administrator or Helpdesk Administrator | ||
| privileges may reset multiple account passwords in rapid succession to establish control | ||
| over targeted users before the activity is detected. |
| | where Result =~ "success" | ||
| | project | ||
| SpCreatedTime = TimeGenerated, | ||
| SpId = tostring(TargetResources[0].id), |
| "Consent to application" | ||
| ) | ||
| | where Result =~ "success" | ||
| | extend ConsentTargetId = tostring(TargetResources[0].id) |
| isnotempty(tostring(InitiatedBy.user.ipAddress)), | ||
| tostring(InitiatedBy.user.ipAddress), | ||
| tostring(InitiatedBy.app.ipAddress)) | ||
| | join kind=inner NewSP on $left.ConsentTargetId == $right.SpId |
Comment on lines
+37
to
+41
| | extend TargetUpn = tostring(TargetResources[0].userPrincipalName) | ||
| | where isnotempty(Actor) and Actor != TargetUpn | ||
| | summarize | ||
| ResetCount = dcount(TargetUpn), | ||
| TargetAccounts = make_set(TargetUpn, 20), |
Comment on lines
+45
to
+50
| by Actor, bin(TimeGenerated, correlationWindow) | ||
| | where ResetCount >= resetThreshold | ||
| | extend AccountName = iff(Actor has "@", tostring(split(Actor, "@")[0]), Actor) | ||
| | extend AccountUPNSuffix = iff(Actor has "@", tostring(split(Actor, "@")[1]), "") | ||
| | project | ||
| TimeGenerated, |
Comment on lines
+45
to
+50
| by Actor, bin(TimeGenerated, correlationWindow) | ||
| | where ResetCount >= resetThreshold | ||
| | extend AccountName = iff(Actor has "@", tostring(split(Actor, "@")[0]), Actor) | ||
| | extend AccountUPNSuffix = iff(Actor has "@", tostring(split(Actor, "@")[1]), "") | ||
| | project | ||
| TimeGenerated, |
Comment on lines
+56
to
+57
| FirstReset, | ||
| LastReset, |
Collaborator
|
Hi @descambiado, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three hunting queries targeting Entra ID account takeover techniques: device code phishing, post-compromise application persistence, and bulk credential resets by privileged actors.
Queries
1. DeviceCodeSignInFromUnseenASN (
Hunting Queries/MultipleDataSources/)Detects successful device code flow sign-ins from autonomous system numbers not seen for the user in the preceding 30 days. Device code phishing (used by Midnight Blizzard and other threat actors) involves an attacker initiating the OAuth device code flow and tricking a target into completing authentication — the attacker receives a valid session token without knowing the user's password or satisfying MFA directly.
Differentiated from the existing
Possible device code phishing attempts.yamlwhich requires M365 DefenderUrlClickEventsandEntraIdSignInEvents. This query runs on the standard Azure AD connector (SigninLogs) with no additional prerequisites.MITRE: T1528, T1078.004
2. NewServicePrincipalGrantedAdminConsent (
Hunting Queries/AuditLogs/)Correlates service principal creation with admin consent or app role assignment within a 1-hour window for the same SP. An attacker with Application Administrator rights who creates an app and immediately grants it tenant-wide permissions is a pattern documented in NOBELIUM/Midnight Blizzard intrusions.
Differentiated from
CredentialsAddAfterAdminConsentedToApp[Nobelium].yaml(requires M365 DefenderCloudAppEvents) andConsentToApplicationDiscovery.yaml(general consent discovery, no SP age correlation). This query uses onlyAuditLogs.MITRE: T1528, T1098.003
3. BulkPasswordResetByActor (
Hunting Queries/AuditLogs/)Identifies a single actor resetting passwords for three or more distinct accounts within one hour. An attacker with User Administrator or Helpdesk Administrator privileges may perform bulk password resets to take over targeted accounts before the activity triggers an alert.
MITRE: T1098, T1078.004
Validation
in~(nohas_any)descambiadoon all three