Detection rules › Kusto

CertUtil Used for File Download (Living off the Land)

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

Detects certutil.exe being used to download files from remote URLs via the -urlcache or -verifyctl flags. CertUtil is a signed Windows binary (LOLBin) that attackers abuse to download payloads while bypassing application whitelisting and network controls. Tune AllowlistedDomains to match your PKI/CA infrastructure.

MITRE ATT&CK coverage

Telemetry coverage

Rule body

id: 4a9d3c2e-7f1b-4e58-9a0c-2d5b8e3f1a7c
name: CertUtil Used for File Download (Living off the Land)
description: |
  Detects certutil.exe being used to download files from remote URLs via the
  -urlcache or -verifyctl flags. CertUtil is a signed Windows binary (LOLBin)
  that attackers abuse to download payloads while bypassing application
  whitelisting and network controls.
  Tune AllowlistedDomains to match your PKI/CA infrastructure.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
  - DefenseEvasion
relevantTechniques:
  - T1105
  - T1218
  - T1140
query: |
  let Lookback = 1h;
  let AllowlistedDomains = dynamic([
    "windowsupdate.microsoft.com",
    "download.microsoft.com",
    "dl.delivery.mp.microsoft.com",
    "ctldl.windowsupdate.com"
  ]);
  let MDE_Results =
    DeviceProcessEvents
    | where Timestamp >= ago(Lookback)
    | where FileName =~ "certutil.exe"
    | where ProcessCommandLine has_any ("-urlcache", "-verifyctl", "-decode", "-decodehex")
    | where ProcessCommandLine has_any ("http://", "https://", "ftp://")
    | where not(ProcessCommandLine has_any (AllowlistedDomains))
    | extend
        AccountName   = InitiatingProcessAccountName,
        AccountDomain = InitiatingProcessAccountDomain,
        HostName      = DeviceName,
        EventTime     = Timestamp
    | project EventTime, HostName, AccountName, AccountDomain,
        ProcessCommandLine, InitiatingProcessFileName;
  let SecEvent_Results =
    SecurityEvent
    | where TimeGenerated >= ago(Lookback)
    | where EventID == 4688
    | where NewProcessName endswith "\\certutil.exe"
    | where CommandLine has_any ("-urlcache", "-verifyctl", "-decode", "-decodehex")
    | where CommandLine has_any ("http://", "https://", "ftp://")
    | where not(CommandLine has_any (AllowlistedDomains))
    | extend
        AccountName   = SubjectUserName,
        AccountDomain = SubjectDomainName,
        HostName      = Computer,
        EventTime     = TimeGenerated,
        ProcessCommandLine = CommandLine,
        InitiatingProcessFileName = ParentProcessName
    | project EventTime, HostName, AccountName, AccountDomain,
        ProcessCommandLine, InitiatingProcessFileName;
  union MDE_Results, SecEvent_Results
  | sort by 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.0
kind: Scheduled

Stages and Predicates

Stage 0: let

let Lookback = 1h;
let AllowlistedDomains = dynamic([
  "windowsupdate.microsoft.com",
  "download.microsoft.com",
  "dl.delivery.mp.microsoft.com",
  "ctldl.windowsupdate.com"
]);
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 =~ "certutil.exe"

Stage 7: where

where (ProcessCommandLine contains "-urlcache" or ProcessCommandLine contains "-verifyctl" or ProcessCommandLine contains "-decode" or ProcessCommandLine contains "-decodehex")

Stage 8: where

where (ProcessCommandLine contains "http://" or ProcessCommandLine contains "https://" or ProcessCommandLine contains "ftp://")

Stage 9: where

where not ((ProcessCommandLine contains "windowsupdate.microsoft.com" or ProcessCommandLine contains "download.microsoft.com" or ProcessCommandLine contains "dl.delivery.mp.microsoft.com" or ProcessCommandLine contains "ctldl.windowsupdate.com"))

Stage 10: extend

extend AccountDomain, AccountName, EventTime, HostName

Stage 11: project

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

Stage 12: source

SecurityEvent

Stage 13: where

where TimeGenerated >= ago(3600s)

Stage 14: where

where EventID == 4688

Stage 15: where

where NewProcessName endswith @"\certutil.exe"

Stage 16: where

where (CommandLine contains "-urlcache" or CommandLine contains "-verifyctl" or CommandLine contains "-decode" or CommandLine contains "-decodehex")

Stage 17: where

where (CommandLine contains "http://" or CommandLine contains "https://" or CommandLine contains "ftp://")

Stage 18: where

where not ((CommandLine contains "windowsupdate.microsoft.com" or CommandLine contains "download.microsoft.com" or CommandLine contains "dl.delivery.mp.microsoft.com" or CommandLine contains "ctldl.windowsupdate.com"))

Stage 19: extend

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

Stage 20: project

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

Stage 21: sort

sort by EventTime

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLinematch
  • -decode transforms: term corpus 2 (elastic 1, kusto 1)
  • -decodehex transforms: term
  • -urlcache transforms: term
  • -verifyctl transforms: term
  • ftp:// transforms: term corpus 13 (sigma 11, elastic 1, kusto 1)
  • http:// transforms: term corpus 23 (sigma 18, elastic 2, kusto 2, splunk 1)
  • https:// transforms: term corpus 23 (sigma 18, elastic 2, kusto 2, splunk 1)
field:"CommandLine" kind:match
EventIDeq
  • 4688 corpus 317 (splunk 283, kusto 33, elastic 1)
field:"EventID" kind:eq value:"4688"
FileNameeq
  • certutil.exe corpus 2 (kusto 2)
field:"file_name" kind:eq value:"certutil.exe"
NewProcessNameends_with
  • \certutil.exe corpus 44 (sigma 43, kusto 1)
field:"Image" kind:ends_with value:"\certutil.exe"
ProcessCommandLinematch
  • -decode transforms: term corpus 2 (elastic 1, kusto 1)
  • -decodehex transforms: term
  • -urlcache transforms: term
  • -verifyctl transforms: term
  • ftp:// transforms: term corpus 13 (sigma 11, elastic 1, kusto 1)
  • http:// transforms: term corpus 23 (sigma 18, elastic 2, kusto 2, splunk 1)
  • https:// transforms: term corpus 23 (sigma 18, elastic 2, kusto 2, splunk 1)
field:"CommandLine" kind:match

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ProcessCommandLinematchwindowsupdate.microsoft.com, download.microsoft.com, dl.delivery.mp.microsoft.com, ctldl.windowsupdate.comexcludes:ProcessCommandLine
CommandLinematchwindowsupdate.microsoft.com, download.microsoft.com, dl.delivery.mp.microsoft.com, ctldl.windowsupdate.comexcludes:CommandLine

Output fields

These fields are emitted when the rule matches.

FieldSource
AccountDomainproject
AccountNameproject
EventTimeproject
HostNameproject
InitiatingProcessFileNameproject
ProcessCommandLineproject