Accounts are being added to and then rapidly removed from privileged groups, indicating potential lateral movement or privilege escalation by an adversary. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify and mitigate early-stage compromise attempts that may evade standard detection mechanisms.
KQL Query
let WellKnownLocalSID = "S-1-5-32-5[0-9][0-9]$";
let WellKnownGroupSID = "S-1-5-21-[0-9]*-[0-9]*-[0-9]*-5[0-9][0-9]$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1102$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1103$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-498$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1000$";
let AC_Add =
(union isfuzzy=true
(SecurityEvent
// Event ID related to member addition.
| where EventID in (4728, 4732,4756)
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| parse EventData with * '"MemberName">' * '=' AccountAdded ",OU" *
| where isnotempty(AccountAdded)
| extend GroupAddedTo = TargetUserName, AddingAccount = Account
| extend AccountAdded_GroupAddedTo_AddingAccount = strcat(AccountAdded, "||", GroupAddedTo, "||", AddingAccount )
| project AccountAdded_GroupAddedTo_AddingAccount, AccountAddedTime = TimeGenerated
),
(WindowsEvent
// Event ID related to member addition.
| where EventID in (4728, 4732,4756)
| extend TargetSid = tostring(EventData.TargetSid)
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| parse EventData.MemberName with * '"MemberName">' * '=' AccountAdded ",OU" *
| where isnotempty(AccountAdded)
| extend TargetUserName = tostring(EventData.TargetUserName)
| extend AddingAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend GroupAddedTo = TargetUserName
| extend AccountAdded_GroupAddedTo_AddingAccount = strcat(AccountAdded, "||", GroupAddedTo, "||", AddingAccount )
| project AccountAdded_GroupAddedTo_AddingAccount, AccountAddedTime = TimeGenerated
)
);
let AC_Remove =
( union isfuzzy=true
(SecurityEvent
// Event IDs related to member removal.
| where EventID in (4729,4733,4757)
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| parse EventData with * '"MemberName">' * '=' AccountRemoved ",OU" *
| where isnotempty(AccountRemoved)
| extend GroupRemovedFrom = TargetUserName, RemovingAccount = Account
| extend AccountRemoved_GroupRemovedFrom_RemovingAccount = strcat(AccountRemoved, "||", GroupRemovedFrom, "||", RemovingAccount)
| project AccountRemoved_GroupRemovedFrom_RemovingAccount, AccountRemovedTime = TimeGenerated, Computer, AccountRemoved = tolower(AccountRemoved),
RemovingAccount, RemovingAccountLogonId = SubjectLogonId, GroupRemovedFrom = TargetUserName, TargetDomainName
),
(WindowsEvent
// Event IDs related to member removal.
| where EventID in (4729,4733,4757)
| extend TargetSid = tostring(EventData.TargetSid)
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| parse EventData.MemberName with * '"MemberName">' * '=' AccountRemoved ",OU" *
| where isnotempty(AccountRemoved)
| extend TargetUserName = tostring(EventData.TargetUserName)
| extend RemovingAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend GroupRemovedFrom = TargetUserName
| extend AccountRemoved_GroupRemovedFrom_RemovingAccount = strcat(AccountRemoved, "||", GroupRemovedFrom, "||", RemovingAccount)
| extend RemovedAccountLogonId= tostring(EventData.SubjectLogonId)
| extend TargetDomainName = tostring(EventData.TargetDomainName)
| project AccountRemoved_GroupRemovedFrom_RemovingAccount, AccountRemovedTime = TimeGenerated, Computer, AccountRemoved = tolower(AccountRemoved),
RemovingAccount, RemovedAccountLogonId, GroupRemovedFrom = TargetUserName, TargetDomainName
));
AC_Add
| join kind = inner AC_Remove
on $left.AccountAdded_GroupAddedTo_AddingAccount == $right.AccountRemoved_GroupRemovedFrom_RemovingAccount
| extend DurationinSecondAfter_Removed = datetime_diff ('second', AccountRemovedTime, AccountAddedTime)
| where DurationinSecondAfter_Removed > 0
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend RemovedAccountName = tostring(split(AccountRemoved, @"\")[1]), RemovedAccountNTDomain = tostring(split(AccountRemoved, @"\")[0])
| extend RemovingAccountName = tostring(split(RemovingAccount, @"\")[1]), RemovingAccountNTDomain = tostring(split(RemovingAccount, @"\")[0])
| project-away DomainIndex
id: 7efc75ce-e2a4-400f-a8b1-283d3b0f2c60
name: Account added and removed from privileged groups
description: |
'Identifies accounts that are added to a privileged group and then quickly removed, which could be a sign of compromise.'
severity: Low
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098
- T1078
query: |
let WellKnownLocalSID = "S-1-5-32-5[0-9][0-9]$";
let WellKnownGroupSID = "S-1-5-21-[0-9]*-[0-9]*-[0-9]*-5[0-9][0-9]$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1102$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1103$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-498$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1000$";
let AC_Add =
(union isfuzzy=true
(SecurityEvent
// Event ID related to member addition.
| where EventID in (4728, 4732,4756)
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| parse EventData with * '"MemberName">' * '=' AccountAdded ",OU" *
| where isnotempty(AccountAdded)
| extend GroupAddedTo = TargetUserName, AddingAccount = Account
| extend AccountAdded_GroupAddedTo_AddingAccount = strcat(AccountAdded, "||", GroupAddedTo, "||", AddingAccount )
| project AccountAdded_GroupAddedTo_AddingAccount, AccountAddedTime = TimeGenerated
),
(WindowsEvent
// Event ID related to member addition.
| where EventID in (4728, 4732,4756)
| extend TargetSid = tostring(EventData.TargetSid)
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| parse EventData.MemberName with * '"MemberName">' * '=' AccountAdded ",OU" *
| where isnotempty(AccountAdded)
| extend TargetUserName = tostring(EventData.TargetUserName)
| extend AddingAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend GroupAddedTo = TargetUserName
| extend AccountAdded_GroupAddedTo_AddingAccount = strcat(AccountAdded, "||", GroupAddedTo, "||", AddingAccount )
| project AccountAdded_GroupAddedTo_AddingAccount, AccountAddedTime = TimeGenerated
)
);
let AC_Remove =
( union isfuzzy=true
(SecurityEvent
// Event IDs related to member removal.
| where EventID in (4729,4733,4757)
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| parse EventData with * '"MemberName">' * '=' AccountRemoved ",OU" *
| where isnotempty(AccountRemoved)
| extend GroupRemovedFrom = TargetUserName, RemovingAccount = Account
| extend AccountRemoved_GroupRemovedFrom_RemovingAccount = strcat(AccountRemoved, "||", GroupRemovedFrom, "||", RemovingAccount)
| project AccountRemoved_GroupRemovedFrom_RemovingAccount, AccountRemovedTime = TimeGenerated, Computer, Account
| Sentinel Table | Notes |
|---|---|
SecurityEvent | Ensure this data connector is enabled |
WindowsEvent | Ensure this data connector is enabled |
Scenario: Scheduled Job Configuration
Description: A system administrator configures a scheduled job that temporarily adds an account to a privileged group to perform maintenance and then removes it immediately.
Filter/Exclusion: Exclude events where the account is part of a known scheduled job (e.g., task scheduler or cron job) and the action is logged under a specific task name (e.g., MaintenanceTask-UserCleanup).
Scenario: User Account Synchronization
Description: An enterprise identity management tool (e.g., Azure AD Connect, Okta) synchronizes user accounts and temporarily adds/removes users from privileged groups during sync.
Filter/Exclusion: Exclude events where the account is managed by an identity provider (e.g., AzureAD, Okta) and the action is associated with a synchronization task.
Scenario: Privileged Session Management
Description: A security tool (e.g., Microsoft Defender for Identity, CrowdStrike) temporarily adds an account to a privileged group to grant elevated access during a security investigation.
Filter/Exclusion: Exclude events where the account is associated with a security tool (e.g., Microsoft Defender, CrowdStrike) and the action is logged under a specific security task.
Scenario: Group Policy Refresh
Description: A Group Policy Object (GPO) refresh causes an account to be added and then removed from a privileged group as part of a policy update.
Filter/Exclusion: Exclude events where the action is triggered by a Group Policy refresh (e.g., gpupdate /force) and the account is part of a GPO-managed group.
Scenario: Temporary Access for Support
Description: A support technician is granted temporary access to a privileged group to resolve an issue and is quickly removed after the task is completed.
*Filter/Ex