Forum Discussion

Nick165's avatar
Nick165
Copper Contributor
May 01, 2025

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

Resources