← Back to SOC feed Coverage →

LocalAdminGroupChanges

kql MEDIUM Azure-Sentinel
DeviceEventsIdentityInfo
huntingmicrosoftofficial
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-23T23:00:00Z · Confidence: medium

Hunt Hypothesis

Adversaries may modify local admin group memberships to gain or maintain elevated privileges within a system. SOC teams should proactively hunt for these changes in Azure Sentinel to identify potential privilege escalation attempts and unauthorized access.

KQL Query

let ADAZUsers =  IdentityInfo 
| extend DirectoryDomain = AccountDomain 
| extend DirectoryAccount = AccountName 
| extend OnPremSid = AccountSID
| distinct DirectoryDomain , DirectoryAccount , OnPremSid , AccountCloudSID, AccountUPN, GivenName, Surname;
 // check for any new created or modified local accounts 
let NewUsers =  DeviceEvents
| where ActionType contains "UserAccountCreated"  // or ActionType contains "UserAccountModified"
| extend lUserAdded = AccountName
| extend NewUserSID = AccountSid
| extend laccountdomain = AccountDomain
| distinct NewUserSID, lUserAdded,laccountdomain;
// Check for any local group changes and enrich the data with the account name obtained from the previous query
DeviceEvents 
| where ActionType == 'UserAccountAddedToLocalGroup' 
| extend AddedAccountSID = tostring(parse_json(AdditionalFields).MemberSid)
| extend LocalGroup = AccountName
| extend LocalGroupSID = AccountSid
| extend Actor = trim(@"[^\w]+",InitiatingProcessAccountName)
// limit to local administrators group
//  | where LocalGroupSID contains "S-1-5-32-544"
| join kind= leftouter    (NewUsers)
on $left.AddedAccountSID == $right.NewUserSID
| project Timestamp, DeviceName, LocalGroup,LocalGroupSID, AddedAccountSID, lUserAdded , Actor, ActionType , laccountdomain 
| join kind= leftouter        (ADAZUsers)
on $left.AddedAccountSID == $right.OnPremSid
| extend UserAdded = iff(isnotempty(lUserAdded),strcat(laccountdomain,"\\", lUserAdded), strcat(DirectoryDomain,"\\", DirectoryAccount))
| project Timestamp, DeviceName, LocalGroup,LocalGroupSID, AddedAccountSID, UserAdded , Actor, ActionType  
| where DeviceName !contains Actor 
// Provide details on actors that added users
// | summarize count()  by Actor 
// | join ADAZUsers
// on $left.Actor == $right.DirectoryAccount 
// | render piechart 

Analytic Rule Definition

id: dd2c4f48-b732-4a75-b2c4-b44bacc66d00
name: LocalAdminGroupChanges
description: |
  Author: alex verboon @alexverboon.
  Blogpost: https://www.verboon.info/2020/09/hunting-for-local-group-membership-changes.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - IdentityInfo
  - DeviceEvents
query: |
  let ADAZUsers =  IdentityInfo 
  | extend DirectoryDomain = AccountDomain 
  | extend DirectoryAccount = AccountName 
  | extend OnPremSid = AccountSID
  | distinct DirectoryDomain , DirectoryAccount , OnPremSid , AccountCloudSID, AccountUPN, GivenName, Surname;
   // check for any new created or modified local accounts 
  let NewUsers =  DeviceEvents
  | where ActionType contains "UserAccountCreated"  // or ActionType contains "UserAccountModified"
  | extend lUserAdded = AccountName
  | extend NewUserSID = AccountSid
  | extend laccountdomain = AccountDomain
  | distinct NewUserSID, lUserAdded,laccountdomain;
  // Check for any local group changes and enrich the data with the account name obtained from the previous query
  DeviceEvents 
  | where ActionType == 'UserAccountAddedToLocalGroup' 
  | extend AddedAccountSID = tostring(parse_json(AdditionalFields).MemberSid)
  | extend LocalGroup = AccountName
  | extend LocalGroupSID = AccountSid
  | extend Actor = trim(@"[^\w]+",InitiatingProcessAccountName)
  // limit to local administrators group
  //  | where LocalGroupSID contains "S-1-5-32-544"
  | join kind= leftouter    (NewUsers)
  on $left.AddedAccountSID == $right.NewUserSID
  | project Timestamp, DeviceName, LocalGroup,LocalGroupSID, AddedAccountSID, lUserAdded , Actor, ActionType , laccountdomain 
  | join kind= leftouter        (ADAZUsers)
  on $left.AddedAccountSID == $right.OnPremSid
  | extend UserAdded = iff(isnotempty(lUserAdded),strcat(laccountdomain,"\\", lUserAdded), strcat(DirectoryDomain,"\\", DirectoryAccount))
  | project Timestamp, DeviceName, LocalGroup,LocalGroupSID, AddedAccountSID, UserAdded , Actor, ActionType  
  | where DeviceName !contains Actor 
  // Provide details on actors that added users
  // | summarize count()  by Actor 
  // | join ADAZUsers
  // on $left.Actor == $right.DirectoryAccount 
  // | render piechart 
version: 1.0.0

Required Data Sources

Sentinel TableNotes
DeviceEventsEnsure this data connector is enabled
IdentityInfoEnsure this data connector is enabled

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/Microsoft 365 Defender/Persistence/LocalAdminGroupChanges.yaml