Local Admin Group Changes
Id | 63aa43c2-e88e-4102-aea5-0432851c541a |
Rulename | Local Admin Group Changes |
Description | This query searches for changes to the local administrators group. Blogpost: https://www.verboon.info/2020/09/hunting-for-local-group-membership-changes. |
Severity | High |
Tactics | Persistence |
Techniques | T1098 |
Required data connectors | MicrosoftThreatProtection |
Kind | Scheduled |
Query frequency | 1h |
Query period | 1h |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Defender XDR/Analytic Rules/Persistence/LocalAdminGroupChanges.yaml |
Version | 1.0.1 |
Arm template | 63aa43c2-e88e-4102-aea5-0432851c541a.json |
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 LocalGroupSID = tostring(parse_json(AdditionalFields).GroupSid)
| extend LocalGroup = AccountName
| extend AddedAccountSID = 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 TimeGenerated, 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))
| extend AccountName = iff(isnotempty(lUserAdded), lUserAdded, DirectoryAccount)
| project TimeGenerated, DeviceName, LocalGroup, LocalGroupSID, AddedAccountSID, UserAdded ,Actor, ActionType, AccountName, laccountdomain
| where DeviceName !contains Actor
// Provide details on actors that added users
// | summarize count() by Actor
// | join ADAZUsers
// on $left.Actor == $right.DirectoryAccount
// | render piechart
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
relevantTechniques:
- T1098
name: Local Admin Group Changes
requiredDataConnectors:
- dataTypes:
- IdentityInfo
- DeviceEvents
connectorId: MicrosoftThreatProtection
entityMappings:
- fieldMappings:
- identifier: FullName
columnName: DeviceName
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
entityType: Host
- fieldMappings:
- identifier: FullName
columnName: UserAdded
- identifier: Name
columnName: AccountName
- identifier: NTDomain
columnName: laccountdomain
entityType: Account
triggerThreshold: 0
id: 63aa43c2-e88e-4102-aea5-0432851c541a
tactics:
- Persistence
version: 1.0.1
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Defender XDR/Analytic Rules/Persistence/LocalAdminGroupChanges.yaml
queryPeriod: 1h
kind: Scheduled
queryFrequency: 1h
severity: High
status: Available
description: |
This query searches for changes to the local administrators group.
Blogpost: https://www.verboon.info/2020/09/hunting-for-local-group-membership-changes.
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 LocalGroupSID = tostring(parse_json(AdditionalFields).GroupSid)
| extend LocalGroup = AccountName
| extend AddedAccountSID = 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 TimeGenerated, 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))
| extend AccountName = iff(isnotempty(lUserAdded), lUserAdded, DirectoryAccount)
| project TimeGenerated, DeviceName, LocalGroup, LocalGroupSID, AddedAccountSID, UserAdded ,Actor, ActionType, AccountName, laccountdomain
| where DeviceName !contains Actor
// Provide details on actors that added users
// | summarize count() by Actor
// | join ADAZUsers
// on $left.Actor == $right.DirectoryAccount
// | render piechart
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
triggerOperator: gt
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/63aa43c2-e88e-4102-aea5-0432851c541a')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/63aa43c2-e88e-4102-aea5-0432851c541a')]",
"properties": {
"alertRuleTemplateName": "63aa43c2-e88e-4102-aea5-0432851c541a",
"customDetails": null,
"description": "This query searches for changes to the local administrators group.\nBlogpost: https://www.verboon.info/2020/09/hunting-for-local-group-membership-changes.\n",
"displayName": "Local Admin Group Changes",
"enabled": true,
"entityMappings": [
{
"entityType": "Host",
"fieldMappings": [
{
"columnName": "DeviceName",
"identifier": "FullName"
},
{
"columnName": "HostName",
"identifier": "HostName"
},
{
"columnName": "DnsDomain",
"identifier": "DnsDomain"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserAdded",
"identifier": "FullName"
},
{
"columnName": "AccountName",
"identifier": "Name"
},
{
"columnName": "laccountdomain",
"identifier": "NTDomain"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Defender XDR/Analytic Rules/Persistence/LocalAdminGroupChanges.yaml",
"query": "let ADAZUsers = IdentityInfo \n| extend DirectoryDomain = AccountDomain \n| extend DirectoryAccount = AccountName \n| extend OnPremSid = AccountSID\n| distinct DirectoryDomain , DirectoryAccount , OnPremSid , AccountCloudSID, AccountUPN, GivenName, Surname;\n // check for any new created or modified local accounts \nlet NewUsers = DeviceEvents\n| where ActionType contains \"UserAccountCreated\" or ActionType contains \"UserAccountModified\"\n| extend lUserAdded = AccountName\n| extend NewUserSID = AccountSid\n| extend laccountdomain = AccountDomain\n| distinct NewUserSID, lUserAdded,laccountdomain;\n// Check for any local group changes and enrich the data with the account name obtained from the previous query\nDeviceEvents \n| where ActionType == 'UserAccountAddedToLocalGroup' \n| extend LocalGroupSID = tostring(parse_json(AdditionalFields).GroupSid)\n| extend LocalGroup = AccountName\n| extend AddedAccountSID = AccountSid\n| extend Actor = trim(@\"[^\\w]+\",InitiatingProcessAccountName)\n// limit to local administrators group\n// | where LocalGroupSID contains \"S-1-5-32-544\"\n| join kind= leftouter (NewUsers)\non $left.AddedAccountSID == $right.NewUserSID\n| project TimeGenerated, DeviceName, LocalGroup,LocalGroupSID, AddedAccountSID, lUserAdded , Actor, ActionType , laccountdomain \n| join kind=leftouter (ADAZUsers)\non $left.AddedAccountSID == $right.OnPremSid\n| extend UserAdded = iff(isnotempty(lUserAdded),strcat(laccountdomain,\"\\\\\", lUserAdded), strcat(DirectoryDomain,\"\\\\\", DirectoryAccount))\n| extend AccountName = iff(isnotempty(lUserAdded), lUserAdded, DirectoryAccount)\n| project TimeGenerated, DeviceName, LocalGroup, LocalGroupSID, AddedAccountSID, UserAdded ,Actor, ActionType, AccountName, laccountdomain \n| where DeviceName !contains Actor\n// Provide details on actors that added users\n// | summarize count() by Actor \n// | join ADAZUsers\n// on $left.Actor == $right.DirectoryAccount \n// | render piechart\n| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)\n| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), \"\")\n",
"queryFrequency": "PT1H",
"queryPeriod": "PT1H",
"severity": "High",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Persistence"
],
"techniques": [
"T1098"
],
"templateVersion": "1.0.1",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}