Query Details
let query_frequency = 1h;
let query_period = 14d;
let device_threshold = 15;
let _ExpectedAccounts = toscalar(
_GetWatchlist("Activity-ExpectedSignificantActivity")
| where Activity == "DeviceNTLMScan"
| summarize make_list(ActorPrincipalName)
);
let _ExpectedTargetDevices = toscalar(
_GetWatchlist("Service-PrivateCorporateServices")
| where Notes has "[NTLMAccess]"
| summarize make_list(HostName)
);
IdentityLogonEvents
| where TimeGenerated > ago(query_frequency)
| where LogonType == "Resource access" and not(Protocol == "Kerberos")// and Application == "Active Directory" and ActionType == "LogonSuccess"
| where not(tostring(AdditionalFields["ACTOR.DEVICE"]) == tostring(AdditionalFields["TARGET_OBJECT.DEVICE"]))
| where not(AccountUpn in (_ExpectedAccounts))
| where not(TargetDeviceName has_any (_ExpectedTargetDevices))
| extend SourceEntity = coalesce(AccountUpn, IPAddress, DeviceName)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
SourceDevices = make_set_if(DeviceName, isnotempty(DeviceName)),
SourceIPAddresses = make_set_if(IPAddress, isnotempty(IPAddress)),
take_any(AccountUpn, AccountName, AccountDomain, AccountSid, AccountObjectId, AccountDisplayName, ActionType)
by SourceEntity, TargetDeviceName, Application, Protocol
| join kind=leftanti (
IdentityLogonEvents
| where TimeGenerated between (ago(query_period) .. ago(query_frequency))
| where LogonType == "Resource access" and not(Protocol == "Kerberos")// and Application == "Active Directory" and ActionType == "LogonSuccess"
| where not(tostring(AdditionalFields["ACTOR.DEVICE"]) == tostring(AdditionalFields["TARGET_OBJECT.DEVICE"]))
| where not(AccountUpn in (_ExpectedAccounts))
| where not(TargetDeviceName has_any (_ExpectedTargetDevices))
| extend SourceEntity = coalesce(AccountUpn, IPAddress, DeviceName)
| where not(isempty(SourceEntity) or isempty(TargetDeviceName))
) on SourceEntity, TargetDeviceName
| summarize
StartTime = min(StartTime),
EndTime = max(EndTime),
SourceDevices = make_set(SourceDevices),
SourceIPAddresses = make_set(SourceIPAddresses),
TargetDevices = make_set_if(TargetDeviceName, isnotempty(TargetDeviceName), 250),
take_any(AccountUpn, AccountName, AccountDomain, AccountSid, AccountObjectId, AccountDisplayName, ActionType)
by SourceEntity, Application, Protocol
| extend TargetDevicesCount = array_length(TargetDevices)
| where TargetDevicesCount > device_threshold
| extend SourceDevice = iff(array_length(SourceDevices) == 1, tostring(SourceDevices[0]), "")
| sort by TargetDevicesCount desc
| project
StartTime,
EndTime,
Application,
ActionType,
Protocol,
SourceEntity,
SourceDevice,
SourceDevices,
SourceIPAddresses,
TargetDevicesCount,
TargetDevices,
AccountDisplayName,
AccountUpn,
AccountSid,
AccountObjectId
This query is looking for logon events in the IdentityLogonEvents table that meet certain criteria. It filters for logon events that occurred within the last hour, have a LogonType of "Resource access" and not a Protocol of "Kerberos". It also excludes logon events where the actor device is the same as the target device, and where the account UPN is in a list of expected accounts, and where the target device name is in a list of expected target devices.
The query then summarizes the results by grouping them based on the source entity, target device name, application, and protocol. It also calculates the start and end times of the logon events, and creates sets of source devices and source IP addresses. It takes any one of the account UPN, account name, account domain, account SID, account object ID, account display name, and action type for each group.
The query then performs a left anti join with another set of logon events from the IdentityLogonEvents table, this time looking for events that occurred between 14 days ago and 1 hour ago. It applies the same filters as before and creates the same sets and calculations.
After the join, the query summarizes the results again, this time grouping them by the source entity, application, and protocol. It calculates the start and end times, creates sets of source devices, source IP addresses, and target devices. It also counts the number of target devices and filters for groups where the count is greater than a threshold value.
Finally, the query extends the results by adding a source device column that takes the first value from the source devices set if it contains only one value. It then sorts the results by the count of target devices in descending order and projects a selected set of columns for the final output.

Jose Sebastián Canós
Released: June 29, 2023
Tables
Keywords
Operators