1
0
Форкнуть 0
Microsoft-365-Defender-Hunt.../Lateral Movement/ServiceAccountsPerformingRe...

55 строки
4.5 KiB
Plaintext

// Service Accounts Performing Remote PowerShell
// Author: miflower
// The purpose behind this detection is for finding service accounts that are performing remote powershell sessions
// There are two phases to the detection: Identify service accounts, Find remote PS cmdlets being ran by these accounts
// To accomplish this, we utilize DeviceLogonEvents and DeviceEvents to find cmdlets ran that meet the criteria
// One of the main advantages of this method is that only requires server telemetry, and not the attacking client
// The first phase relies on the DeviceLogonEvents to determine whether an account is a service account or not, consider the following accounts with logons:
// random_user has DeviceLogonEvents with type 2, 3, 7, 10, 11 & 13
// random_service_account 'should' only have DeviceLogonEvents with type 3,4 or 5
//
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', AdditionalFields)) // Extract the actual PowerShell command that was executed
| where Command !in (WhitelistedCmdlets) // Remove any values that match the whitelisted cmdlets
| summarize (Timestamp, ReportId)=argmax(Timestamp, ReportId), // Then group all of the cmdlets and calculate the min/max times of execution...
makeset(Command), count(), min(Timestamp) by // ...as well as creating a list of cmdlets ran and the count..
AccountName, 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
| where min_Timestamp > ago(1d) // Included to restrict the scope for the custom detection page