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.5
kind: Scheduled

Stages and Predicates

Parameters

let Lookback = 1h;

Let binding: AllowlistedDomains

let AllowlistedDomains = dynamic([
  "windowsupdate.microsoft.com",
  "download.microsoft.com",
  "dl.delivery.mp.microsoft.com",
  "ctldl.windowsupdate.com"
]);

union (2 sources)

Each leg below queries one source; the rule matches if any leg does. Sources: MDE_Results, SecEvent_Results

Leg 1: MDE_Results

Leg 2: SecEvent_Results

Applied to the combined result

| sort by EventTime desc

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

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

Output fields

These fields are emitted when the rule matches.

FieldSource
AccountDomainproject
AccountNameproject
EventTimeproject
HostNameproject
InitiatingProcessFileNameproject
ProcessCommandLineproject