Query Details
let _ADGroups =
_GetWatchlist('SID-AuditADObjects')
| where Notes has ("[GroupActivity]")
| project GroupSID = SID, SAMAccountName, Auditors = Auditor, MonitoredEventID, Severity
| mv-apply split(MonitoredEventID, " ") to typeof(string) on (
summarize
take_any(*),
ValidEventIDs = make_list_if(MonitoredEventID, isnotempty(MonitoredEventID) and not(MonitoredEventID startswith "-")),
ExcludedEventIDs = make_list_if(replace_string(MonitoredEventID, "-", ""), isnotempty(MonitoredEventID) and MonitoredEventID startswith "-")
)
;
let _ExpectedActivityADGroups =
_GetWatchlist('Activity-ExpectedSignificantActivity')
| where Activity == "ADGroupEventID"
| project
Activity = Auxiliar,
ActorAccount = ActorPrincipalName,
GroupSid = DestinationResource
;
let _ExpectedADGroupDomains = toscalar(
_GetWatchlist('Activity-ExpectedSignificantActivity')
| where Activity == "MonitorADGroupFromDomain"
| summarize make_list(Auxiliar)
);
let _AccountOperators = toscalar(
_GetWatchlist("Activity-ExpectedSignificantActivity")
| where Activity == "AccountOperator"
| summarize make_list(ActorPrincipalName)
);
let add_member_eventids = dynamic([4728, 4732, 4756]);
let remove_member_eventids = dynamic([4729, 4733, 4757]);
SecurityEvent
| where isnotempty(TargetSid)
// Remove local group events where the domain of the actor account is not the expected tenant domain
| where not(EventID in (4732, 4733) and SubjectDomainName !in (_ExpectedADGroupDomains))
// Remove group enumeration events where the domain of the actor account is not the expected tenant domain
| where not(EventID in (4799) and SubjectDomainName !in (_ExpectedADGroupDomains))
// Remove local group event where account "NT AUTHORITY\NETWORK SERVICE" is added to "IIS_IUSRS" group
| where not(EventID in (4732, 4733) and MemberSid == "S-1-5-20" and TargetSid == "S-1-5-32-568")
| lookup kind=inner _ADGroups on $left.TargetSid == $right.GroupSID
| as _AuxiliarEvents
| join kind=leftanti (
_AuxiliarEvents
| where EventID in (array_concat(add_member_eventids, remove_member_eventids))
| extend EventID = case(
EventID in (4728, 4729), 4737,
EventID in (4732, 4733), 4735,
EventID in (4756, 4757), 4755,
int(null)
)
) on Account, SourceComputerId, EventID, SubjectLogonId, TargetSid
| where not(array_length(ValidEventIDs) > 0 and not(ValidEventIDs has tostring(EventID)))
| where not(array_length(ExcludedEventIDs) > 0 and ExcludedEventIDs has tostring(EventID))
| as _FilteredAuxiliarEvents
| lookup kind=leftouter (
_FilteredAuxiliarEvents
| where EventID in (4735, 4737, 4755)
| project Account, SourceComputerId, EventID, SubjectLogonId, TargetUserName
| join kind=inner (
SecurityEvent
| where EventID == 5136
| project Account, SourceComputerId, SubjectLogonId, OperationType, EventData
| mv-apply Auxiliar = parse_xml(EventData)["EventData"]["Data"] on (
summarize BagToUnpack = make_bag(pack(tostring(Auxiliar["@Name"]), tostring(Auxiliar["#text"])))
)
| evaluate bag_unpack(BagToUnpack, columnsConflict="keep_source") : (Account:string, SourceComputerId:string, SubjectLogonId:string, OperationType:string, EventData:string, OpCorrelationID:string, ObjectDN:string, ObjectGUID:string, AttributeLDAPDisplayName:string, AttributeValue:string)
| extend TargetUserName = extract(@"(?i:CN\=)([^,]+)", 1, ObjectDN)
) on Account, SourceComputerId, SubjectLogonId, TargetUserName
| extend SplitAttributeValue = iff(AttributeLDAPDisplayName =~ "nTSecurityDescriptor", split(AttributeValue, "("), pack_array(AttributeValue))
| summarize
ValueAdded = take_anyif(SplitAttributeValue, OperationType == "%%14674"),
ValueDeleted = take_anyif(SplitAttributeValue, OperationType == "%%14675"),
AttributeValue_EventData = take_anyif(EventData, OperationType == "%%14674"),
take_any(Account, SourceComputerId, EventID, SubjectLogonId, TargetUserName, AttributeLDAPDisplayName)
by OpCorrelationID, ObjectGUID
| extend
ValueAdded = set_difference(ValueAdded, ValueDeleted),
ValueDeleted = set_difference(ValueDeleted, ValueAdded)
| mv-expand OperationTypeTranslated = pack_array("ValueAdded", "ValueDeleted") to typeof(string), ModifiedAttributeValue = pack_array(ValueAdded, ValueDeleted) to typeof(dynamic)
| mv-expand ModifiedAttributeValue to typeof(string)
| where isnotempty(ModifiedAttributeValue)
| project Account, SourceComputerId, EventID, SubjectLogonId, TargetUserName, OperationTypeTranslated, AttributeLDAPDisplayName, ModifiedAttributeValue, AttributeValue_EventData
) on Account, SourceComputerId, EventID, SubjectLogonId, TargetUserName
| extend
Activity = trim_end(@"\.", Activity),
ActorAccount = SubjectAccount,
GroupSid = TargetSid
// Remove expected activity
| join kind=leftanti _ExpectedActivityADGroups on Activity, ActorAccount, GroupSid
| extend
AttributeValue_MemberSid = extract(@"([^;]+)\)\s*$", 1, ModifiedAttributeValue),
AttributeValue_MemberName = extract(@"^((?i:CN\=.*))$", 1, ModifiedAttributeValue),
ModifiedAttributeValue = trim_end(@"\)\s*$", ModifiedAttributeValue)
| extend
MemberAccount = case(
MemberSid == SubjectUserSid, SubjectUserName,
isnotempty(MemberName), extract(@"(?i:CN\=)([^,]+)", 1, MemberName),
isnotempty(AttributeValue_MemberName), extract(@"(?i:CN\=)([^,]+)", 1, AttributeValue_MemberName),
""
),
MemberSid = case(
isnotempty(MemberSid), MemberSid,
isnotempty(AttributeValue_MemberSid), AttributeValue_MemberSid,
MemberSid
),
GroupName = SAMAccountName,
ActorSid = SubjectUserSid,
ActorAccountType = AccountType,
ActorDomainName = SubjectDomainName,
AlertSeverity = case(
IsWorkingTime(TimeGenerated) and ActorAccount in (_AccountOperators), "Informational",
Severity
)
| project
TimeGenerated,
Computer,
ActorAccount,
Activity,
GroupName,
MemberAccount,
ActorSid,
GroupSid,
MemberSid,
ActorAccountType,
ActorDomainName,
AlertSeverity,
Auditors,
EventData,
AttributeValue_EventData,
AttributeLDAPDisplayName,
OperationTypeTranslated,
ModifiedAttributeValue
The query is retrieving security events related to Active Directory (AD) groups and their members. It filters out events that are not relevant or expected. The query then joins the filtered events with a watchlist of AD groups to get additional information about the groups. It also performs some transformations and calculations to extract relevant data from the events. The final result includes various attributes of the events, such as the time generated, computer, actor account, activity, group name, member account, actor SID, group SID, member SID, actor account type, actor domain name, alert severity, auditors, event data, attribute value event data, attribute LDAP display name, operation type translated, and modified attribute value.

Jose Sebastián Canós
Released: December 29, 2022
Tables
Keywords
Operators