MDE ‐ User added removed from local groups - mattnovitsch/M365 GitHub Wiki
I've seen the question asked a lot "How do I know if a user was added to a local group?". The answer is we detect and track that kind of information in Microsoft Defender for endpoint. The solution though is multi-part and has to take into account is the account being added local, Active Directory, and/or Entra.
We don't keep track of accounts and SIDs for all the endpoints, just if there is activity with them such as a login or creation. In theory, if someone is adding an account to a group, they will more than likely be logging into it in a similar timeframe so between the three queries I have provided you should be able to get the SID and account name. There were a couple of "gotchas" in the mix.
- If the account is local, we wouldn't have a SID in Entra ID or On-prem AD to compare to.
- If the account was on-prem or Entra ID, the information for what the customer is looking for won't be available without MDI deployed.
- The identity information in the IdentityInfo table for the account doesn't match up to the SID column in the DeviceEvents table.
a. DeviceEvents SID =AccountSid
b. IdentityInfo SID = CloudSID or OnPremSid
I came up with three different queries to resolve this issue.
The first one will find if a local account is created then added to a group. You can see from the screenshot that I was able to get the accounts that were recently created and added to groups.
//Find newly created local account added to group
DeviceEvents
| where ActionType contains "UserAccountAddedToLocalGroup"
| extend Fields=parse_json(AdditionalFields)
| extend AddToGroup = tostring(Fields.GroupName)
| extend GroupDomainName = tostring(Fields.GroupDomainName)
| join kind=inner (DeviceEvents
| where ActionType contains "UserAccountCreated"
| where AccountName <> ""
| distinct AccountName, AccountSid
) on AccountSid
| extend InitiatedAction=strcat (InitiatingProcessAccountDomain, "\", InitiatingProcessAccountName)
| project Timestamp, DeviceName, ActionType, InitiatedAction,AccountAdded=AccountName1, AddToGroup, GroupDomainName
The second query is looking for any cloud or on-prem account added to a group recently. Since we may not know when the account was created.
//Find cloud or on-prem accounts added to group
DeviceEvents
| extend placeholder=1
| where ActionType contains "UserAccountAddedToLocalGroup"
| extend Fields=parse_json(AdditionalFields)
| extend AddToGroup = tostring(Fields.GroupName)
| extend GroupDomainName = tostring(Fields.GroupDomainName)
| join kind=inner (IdentityInfo
| extend placeholder=1
| distinct AccountName, OnPremSid, CloudSid, placeholder
) on placeholder
| where AccountSid == OnPremSid or AccountSid == CloudSid
| extend InitiatedAction=strcat (InitiatingProcessAccountDomain, "\", InitiatingProcessAccountName)
| project Timestamp, DeviceName, ActionType, InitiatedAction,AccountAdded=AccountName1, AddToGroup, GroupDomainName
The third query would tell you if the local account logged in and if it was recently added to a group.
//Find local account added to group
DeviceEvents
//| where DeviceName contains "surface"
| where ActionType contains "UserAccountAddedToLocalGroup"
| extend Fields=parse_json(AdditionalFields)
| extend AddToGroup = tostring(Fields.GroupName)
| extend GroupDomainName = tostring(Fields.GroupDomainName)
| join kind=inner (DeviceLogonEvents
| where AccountName <> ""
| distinct AccountName, AccountSid
) on AccountSid
| extend InitiatedAction=strcat (InitiatingProcessAccountDomain, "\", InitiatingProcessAccountName)
| project Timestamp, DeviceName, ActionType, InitiatedAction, AccountAdded=AccountName1, AddToGroup, GroupDomainName, AdditionalFields
This should cover most basis for finding when an account was either created on an endpoint from a newly created local account, your on-prem domain, your Entra environment, and/or a local account that was logged into.
KQL can be downloaded from here: MDE-FindAccountsAddedtoGroups