Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Local Admin Group Changes

Back
Id63aa43c2-e88e-4102-aea5-0432851c541a
RulenameLocal Admin Group Changes
DescriptionThis query searches for changes to the local administrators group.

Blogpost: https://www.verboon.info/2020/09/hunting-for-local-group-membership-changes.
SeverityHigh
TacticsPersistence
TechniquesT1098
Required data connectorsMicrosoftThreatProtection
KindScheduled
Query frequency1h
Query period1h
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Defender XDR/Analytic Rules/Persistence/LocalAdminGroupChanges.yaml
Version1.0.0
Arm template63aa43c2-e88e-4102-aea5-0432851c541a.json
Deploy To Azure
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 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
queryFrequency: 1h
description: |
  This query searches for changes to the local administrators group.
  Blogpost: https://www.verboon.info/2020/09/hunting-for-local-group-membership-changes.  
status: Available
kind: Scheduled
triggerThreshold: 0
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - IdentityInfo
  - DeviceEvents
version: 1.0.0
queryPeriod: 1h
name: Local Admin Group Changes
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Defender XDR/Analytic Rules/Persistence/LocalAdminGroupChanges.yaml
id: 63aa43c2-e88e-4102-aea5-0432851c541a
tactics:
- Persistence
relevantTechniques:
- T1098
severity: High
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
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 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), "")  
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2023-02-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 AddedAccountSID = tostring(parse_json(AdditionalFields).MemberSid)\n| extend LocalGroup = AccountName\n| extend LocalGroupSID = 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",
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Persistence"
        ],
        "techniques": [
          "T1098"
        ],
        "templateVersion": "1.0.0",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}