Detection rules › Kusto

PowerShell Encoded Command Execution (Living off the Land)

Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

Detects PowerShell or pwsh.exe launched with encoded command flags (-EncodedCommand, -enc, -ec, -enco). Attackers encode commands in Base64 to obfuscate malicious payloads and bypass script-based controls. Risk is elevated when a download cradle pattern is also detected. Tune AllowlistedParents for known-safe management tools such as SCCM.

MITRE ATT&CK coverage

Telemetry coverage

Rule body

id: 7b2f4d1a-9c3e-4f72-8b1d-3e6a9f2c4b8d
name: PowerShell Encoded Command Execution (Living off the Land)
description: |
  Detects PowerShell or pwsh.exe launched with encoded command flags
  (-EncodedCommand, -enc, -ec, -enco). Attackers encode commands in Base64
  to obfuscate malicious payloads and bypass script-based controls.
  Risk is elevated when a download cradle pattern is also detected.
  Tune AllowlistedParents for known-safe management tools such as SCCM.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - DefenseEvasion
relevantTechniques:
  - T1059.001
  - T1027
query: |
  let Lookback = 1h;
  let EncodedFlagRegex = @'(?i)(^|\s)-(encodedcommand|enc|ec|enco)\b';
  let AllowlistedParents = dynamic([
    "SqlServer.exe", "ManagementStudio.exe", "devenv.exe",
    "ccmexec.exe", "SMSAgent.exe"
  ]);
  let DownloadCradlePatterns = dynamic([
    "Net.WebClient", "WebRequest", "DownloadString", "DownloadFile",
    "IEX", "Invoke-Expression", "Invoke-RestMethod", "Start-BitsTransfer"
  ]);
  let MDE_Results =
    DeviceProcessEvents
    | where Timestamp >= ago(Lookback)
    | where FileName in~ ("powershell.exe", "pwsh.exe")
    | where ProcessCommandLine matches regex EncodedFlagRegex
    | where not(InitiatingProcessFileName has_any (AllowlistedParents))
    | extend
        HasDownloadCradle = ProcessCommandLine has_any (DownloadCradlePatterns),
        AccountName       = InitiatingProcessAccountName,
        AccountDomain     = InitiatingProcessAccountDomain,
        HostName          = DeviceName,
        EventTime         = Timestamp
    | project EventTime, HostName, AccountName, AccountDomain,
        ProcessCommandLine, InitiatingProcessFileName, HasDownloadCradle;
  let SecEvent_Results =
    SecurityEvent
    | where TimeGenerated >= ago(Lookback)
    | where EventID == 4688
    | where NewProcessName endswith "\\powershell.exe"
        or NewProcessName endswith "\\pwsh.exe"
    | where CommandLine matches regex EncodedFlagRegex
    | where not(ParentProcessName has_any (AllowlistedParents))
    | extend
        HasDownloadCradle     = CommandLine has_any (DownloadCradlePatterns),
        AccountName           = SubjectUserName,
        AccountDomain         = SubjectDomainName,
        HostName              = Computer,
        EventTime             = TimeGenerated,
        ProcessCommandLine    = CommandLine,
        InitiatingProcessFileName = ParentProcessName
    | project EventTime, HostName, AccountName, AccountDomain,
        ProcessCommandLine, InitiatingProcessFileName, HasDownloadCradle;
  union MDE_Results, SecEvent_Results
  | extend RiskScore = iif(HasDownloadCradle, 2, 1)
  | sort by RiskScore desc, EventTime desc
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: HostName
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountDomain
  - entityType: Process
    fieldMappings:
      - identifier: CommandLine
        columnName: ProcessCommandLine
version: 1.0.4
kind: Scheduled

Stages and Predicates

Stage 0: let

let Lookback = 1h;
let EncodedFlagRegex = @'(?i)(^|\s)-(encodedcommand|enc|ec|enco)\b';
let AllowlistedParents = dynamic([
  "SqlServer.exe", "ManagementStudio.exe", "devenv.exe",
  "ccmexec.exe", "SMSAgent.exe"
]);
let DownloadCradlePatterns = dynamic([
  "Net.WebClient", "WebRequest", "DownloadString", "DownloadFile",
  "IEX", "Invoke-Expression", "Invoke-RestMethod", "Start-BitsTransfer"
]);
let MDE_Results = DeviceProcessEvents <inlined as stages below>;
let SecEvent_Results = SecurityEvent <inlined as stages below>;

Stage 1: source

let MDE_Results

Stage 2: source

let SecEvent_Results

Stage 3: union

union of 2 branches

Stage 4: source

DeviceProcessEvents

Stage 5: where

where Timestamp >= ago(3600s)

Stage 6: where

where FileName in~ ("powershell.exe", "pwsh.exe")

Stage 7: where

where ProcessCommandLine matches regex @"(?i)(^|\s)-(encodedcommand|enc|ec|enco)\b"

Stage 8: where

where not ((InitiatingProcessFileName contains "SqlServer.exe" or InitiatingProcessFileName contains "ManagementStudio.exe" or InitiatingProcessFileName contains "devenv.exe" or InitiatingProcessFileName contains "ccmexec.exe" or InitiatingProcessFileName contains "SMSAgent.exe"))

Stage 9: extend

extend AccountDomain, AccountName, EventTime, HasDownloadCradle, HostName

Stage 10: project

project AccountDomain, AccountName, EventTime, HasDownloadCradle, HostName, InitiatingProcessFileName, ProcessCommandLine

Stage 11: source

SecurityEvent

Stage 12: where

where TimeGenerated >= ago(3600s)

Stage 13: where

where EventID == 4688

Stage 14: where

where (NewProcessName endswith @"\powershell.exe" or NewProcessName endswith @"\pwsh.exe")

Stage 15: where

where CommandLine matches regex @"(?i)(^|\s)-(encodedcommand|enc|ec|enco)\b"

Stage 16: where

where not ((ParentProcessName contains "SqlServer.exe" or ParentProcessName contains "ManagementStudio.exe" or ParentProcessName contains "devenv.exe" or ParentProcessName contains "ccmexec.exe" or ParentProcessName contains "SMSAgent.exe"))

Stage 17: extend

extend AccountDomain, AccountName, EventTime, HasDownloadCradle, HostName, InitiatingProcessFileName, ProcessCommandLine

Stage 18: project

project AccountDomain, AccountName, EventTime, HasDownloadCradle, HostName, InitiatingProcessFileName, ProcessCommandLine

Stage 19: extend

extend RiskScore

Stage 20: sort

sort by EventTime, RiskScore

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLineregex_match
  • (?i)(^|\s)-(encodedcommand|enc|ec|enco)\b
field:"CommandLine" kind:regex_match value:"(?i)(^|\s)-(encodedcommand|enc|ec|enco)\b"
EventIDeq
  • 4688 corpus 317 (splunk 283, kusto 33, elastic 1)
field:"EventID" kind:eq value:"4688"
FileNamein
  • powershell.exe corpus 3 (kusto 3)
  • pwsh.exe
field:"file_name" kind:in
NewProcessNameends_with
  • \powershell.exe corpus 181 (sigma 180, kusto 1)
  • \pwsh.exe corpus 167 (sigma 166, kusto 1)
field:"Image" kind:ends_with
ProcessCommandLineregex_match
  • (?i)(^|\s)-(encodedcommand|enc|ec|enco)\b
field:"CommandLine" kind:regex_match value:"(?i)(^|\s)-(encodedcommand|enc|ec|enco)\b"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
InitiatingProcessFileNamematchSqlServer.exe, ManagementStudio.exe, devenv.exe, ccmexec.exe, SMSAgent.exeexcludes:InitiatingProcessFileName
ParentProcessNamematchSqlServer.exe, ManagementStudio.exe, devenv.exe, ccmexec.exe, SMSAgent.exeexcludes:ParentProcessName

Output fields

These fields are emitted when the rule matches.

FieldSource
AccountDomainproject
AccountNameproject
EventTimeproject
HasDownloadCradleproject
HostNameproject
InitiatingProcessFileNameproject
ProcessCommandLineproject
RiskScoreextend