Forum Discussion
Nick165
May 01, 2025Copper Contributor
How to exclude IPs & accounts from Analytic Rule, with Watchlist?
We are trying to filter out some false positives from a Analytic rule called "Service accounts performing RemotePS". Using automation rules still gives a lot of false mail notifications we don't want so we would like to try using a watchlist with the serviceaccounts and IP combination we want to exclude. Anyone knows where and what syntax we would need to exlude the items on the specific Watchlist?
Query:
let InteractiveTypes = pack_array( // Declare Interactive logon type names
'Interactive',
'CachedInteractive',
'Unlock',
'RemoteInteractive',
'CachedRemoteInteractive',
'CachedUnlock'
);
let WhitelistedCmdlets = pack_array( // List of whitelisted commands that don't provide a lot of value
'prompt',
'Out-Default',
'out-lineoutput',
'format-default',
'Set-StrictMode',
'TabExpansion2'
);
let WhitelistedAccounts = pack_array('FakeWhitelistedAccount'); // List of accounts that are known to perform this activity in the environment and can be ignored
DeviceLogonEvents // Get all logon events...
| where AccountName !in~ (WhitelistedAccounts) // ...where it is not a whitelisted account...
| where ActionType == "LogonSuccess" // ...and the logon was successful...
| where AccountName !contains "$" // ...and not a machine logon.
| where AccountName !has "winrm va_" // WinRM will have pseudo account names that match this if there is an explicit permission for an admin to run the cmdlet, so assume it is good.
| extend IsInteractive=(LogonType in (InteractiveTypes)) // Determine if the logon is interactive (True=1,False=0)...
| summarize HasInteractiveLogon=max(IsInteractive) // ...then bucket and get the maximum interactive value (0 or 1)...
by AccountName // ... by the AccountNames
| where HasInteractiveLogon == 0 // ...and filter out all accounts that had an interactive logon.
// At this point, we have a list of accounts that we believe to be service accounts
// Now we need to find RemotePS sessions that were spawned by those accounts
// Note that we look at all powershell cmdlets executed to form a 29-day baseline to evaluate the data on today
| join kind=rightsemi ( // Start by dropping the account name and only tracking the...
DeviceEvents // ...
| where ActionType == 'PowerShellCommand' // ...PowerShell commands seen...
| where InitiatingProcessFileName =~ 'wsmprovhost.exe' // ...whose parent was wsmprovhost.exe (RemotePS Server)...
| extend AccountName = InitiatingProcessAccountName // ...and add an AccountName field so the join is easier
) on AccountName
// At this point, we have all of the commands that were ran by service accounts
| extend Command = tostring(extractjson('$.Command', tostring(AdditionalFields))) // Extract the actual PowerShell command that was executed
| where Command !in (WhitelistedCmdlets) // Remove any values that match the whitelisted cmdlets
| summarize (Timestamp, ReportId)=arg_max(TimeGenerated, ReportId), // Then group all of the cmdlets and calculate the min/max times of execution...
make_set(Command, 100000), count(), min(TimeGenerated) by // ...as well as creating a list of cmdlets ran and the count..
AccountName, AccountDomain, DeviceName, DeviceId // ...and have the commonality be the account, DeviceName and DeviceId
// At this point, we have machine-account pairs along with the list of commands run as well as the first/last time the commands were ran
| order by AccountName asc // Order the final list by AccountName just to make it easier to go through
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
No RepliesBe the first to reply