Detection rules › Kusto

Access Token Manipulation - Create Process with Token

Status
available
Severity
medium
Time window
1h
Group by
CleanElevatedAccountName, DeviceId
Source
github.com/Azure/Azure-Sentinel

This query detects the use of the 'runas' command and checks whether the account used to elevate privileges isn't the user's own admin account. Additionally, it will match this event to the logon events - to check whether it has been successful as well as augment the event with the new SID.

MITRE ATT&CK coverage

Telemetry coverage

Rule body

id: 8df80270-b4fa-4a7a-931e-8d17c0b321ae
name: Access Token Manipulation - Create Process with Token
description: |
  This query detects the use of the 'runas' command and checks whether the account used to elevate privileges isn't the user's own admin account. 
  Additionally, it will match this event to the logon events - to check whether it has been successful as well as augment the event with the new SID.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
      - DeviceLogonEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
  - DefenseEvasion
relevantTechniques:
  - T1134.002
query: |
  let RunAsProcess=DeviceProcessEvents
      | where FileName =~ "runas.exe" 
      // You can choose to filter out the local admin account. This is based on convention. Here, we assume that localadmin accounts
      // end with _ladmin (RID 500 / LAPS).
      | where not(AccountName has_any("_ladmin"))
      // De-obfuscate the commandline used. 
      | extend CleanProcessCommandLine=parse_command_line(tostring(ProcessCommandLine), "windows")
      // Exclude a user running something on their system through their admin account.
      | where CleanProcessCommandLine !contains strcat(AccountName, "_adm") // Replace this with your admin account naming convention.
      // Exclude local admin account activities by, for instance, the servicedesk that uses the LAPS provisioned account. This is optional. 
      // Disable the line below if the number of false positives is acceptable. 
      | where not(CleanProcessCommandLine has_any (":_ladmin")) // Replace this with your local RID500/LAPS account.
      // Extract the username for the elevation action.
      | extend ElevatedAccountName=extract("user:([a-zA-Z0-9\\\\]+)",1,tostring(CleanProcessCommandLine))
      // Strip the domain suffix.
      | extend CleanElevatedAccountName= trim("(.*\\\\)",ElevatedAccountName);
  RunAsProcess
  | join kind=leftouter ( 
      DeviceLogonEvents
      | project-rename CleanElevatedAccountName = AccountName
      ) on CleanElevatedAccountName,DeviceId
  | project-rename ElevatedActionType=ActionType1,ElevatedAccountSid=AccountSid1
  | project TimeGenerated,DeviceId,DeviceName,FileName,FolderPath,ProcessCommandLine,SHA256,ProcessIntegrityLevel,AccountDomain,AccountName,AccountSid, LogonId, InitiatingProcessFileName,InitiatingProcessFolderPath,InitiatingProcessCommandLine,CleanProcessCommandLine,ElevatedAccountName,CleanElevatedAccountName,ElevatedActionType,LogonType,ElevatedAccountSid
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
  - entityType: Account
    fieldMappings:
      - identifier: Sid
        columnName: AccountSid 
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountDomain
  - entityType: Process
    fieldMappings:
      - identifier: CommandLine
        columnName: ProcessCommandLine
version: 1.0.0
kind: Scheduled

Stages and Predicates

let RunAsProcess is inlined into the numbered stages below.

Stage 1: source

let RunAsProcess

Stage 2: source

DeviceProcessEvents

Stage 3: where

where FileName =~ "runas.exe"

Stage 4: where

where not (AccountName contains "_ladmin")

Stage 5: extend

extend CleanProcessCommandLine

Stage 6: where

where CleanProcessCommandLine !contains strcat(AccountName, "_adm")

Stage 7: where

where not (CleanProcessCommandLine contains ":_ladmin")

Stage 8: extend

extend ElevatedAccountName

Stage 9: extend

extend CleanElevatedAccountName

Stage 10: join

join kind=leftouter (DeviceLogonEvents) on CleanElevatedAccountName, DeviceId

Stage 11: project-rename

project-rename

Stage 12: project

project AccountDomain, AccountName, AccountSid, CleanElevatedAccountName, CleanProcessCommandLine, DeviceId, DeviceName, ElevatedAccountName, ElevatedAccountSid, ElevatedActionType, FileName, FolderPath, InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessFolderPath, LogonId, LogonType, ProcessCommandLine, ProcessIntegrityLevel, SHA256, TimeGenerated

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
AccountNamematch_ladminexcludes:AccountName field:"AccountName" value:"_ladmin"
CleanProcessCommandLinematch:_ladminexcludes:CleanProcessCommandLine field:"CleanProcessCommandLine" value:":_ladmin"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CleanProcessCommandLinecross_field_compare
  • AccountName transforms: op:not_contains, rhs:strcat_suffix:_adm
field:"CleanProcessCommandLine" kind:cross_field_compare value:"AccountName"
FileNameeq
  • runas.exe
field:"file_name" kind:eq value:"runas.exe"

Output fields

These fields are emitted when the rule matches.

FieldSource
AccountDomainproject
AccountNameproject
AccountSidproject
CleanElevatedAccountNameproject
CleanProcessCommandLineproject
DeviceIdproject
DeviceNameproject
ElevatedAccountNameproject
ElevatedAccountSidproject
ElevatedActionTypeproject
FileNameproject
FolderPathproject
InitiatingProcessCommandLineproject
InitiatingProcessFileNameproject
InitiatingProcessFolderPathproject
LogonIdproject
LogonTypeproject
ProcessCommandLineproject
ProcessIntegrityLevelproject
SHA256project
TimeGeneratedproject