Detection rules › Kusto

Suspicious use of CPL file

Group by
SHA1
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

This query identifies .cpl files being loaded and verifies if the corresponding file is suspicious by looking at the signature and global prevalence.

Known false positives

  • Legitimate custom software might create a control panel item that is unsigned or has a low global prevalence.

MITRE ATT&CK coverage

References

Telemetry coverage

Rule body

let timeframe = 2*1h;
let default_global_prevalence = 0;
let suspiciousCPLs = DeviceImageLoadEvents
    | where ingestion_time() >= ago(timeframe)
    // Begin environment-specific filter.
    // End environment-specific filter.
    | where FileName endswith ".cpl"
    | summarize by SHA1
    // FileProfile is case-sensitive and works on lower-case hashes.
    | extend SHA1=tolower(SHA1)
    | invoke FileProfile(SHA1, 1000)
    | where not(ProfileAvailability =~ "Error")
    // Begin environment-specific filter.
    // End environment-specific filter.
    | where ((isempty(Signer) or not(IsCertificateValid==1)) and coalesce(GlobalPrevalence,default_global_prevalence) < 100) or coalesce(GlobalPrevalence,default_global_prevalence) < 50;
let loadedDlls=DeviceImageLoadEvents
    | where ingestion_time() >= ago(timeframe)
    // FileProfile is case-sensitive and works on lower-case hashes.
    | extend SHA1=tolower(SHA1)
    | where SHA1 in~ ((suspiciousCPLs|project SHA1)) and ActionType =~ "ImageLoaded"
    // Begin environment-specific filter.
    // End environment-specific filter.
    ;
loadedDlls
    | join kind=leftouter suspiciousCPLs on SHA1
    // Begin environment-specific filter.
    // End environment-specific filter.

Stages and Predicates

Parameters

let timeframe = 2*1h;
let default_global_prevalence = 0;

let loadedDlls is inlined into the numbered stages below.

Let binding: suspiciousCPLs used in Stages 1, 7

let suspiciousCPLs = DeviceImageLoadEvents
    | where ingestion_time() >= ago(timeframe)
    | where FileName endswith ".cpl"
    | summarize by SHA1
    | extend SHA1=tolower(SHA1)
    | invoke FileProfile(SHA1, 1000)
    | where not(ProfileAvailability =~ "Error")
    | where ((isempty(Signer) or not(IsCertificateValid==1)) and coalesce(GlobalPrevalence,default_global_prevalence) < 100) or coalesce(GlobalPrevalence,default_global_prevalence) < 50;

Stage 1: source

let suspiciousCPLs

Stage 2: source

let loadedDlls

Stage 3: source

DeviceImageLoadEvents

Stage 4: where

where ...

Stage 5: extend

extend SHA1

Stage 6: where

where ActionType =~ "ImageLoaded"

Stage 7: join

join kind=leftouter (suspiciousCPLs) on SHA1

Stage 8: summarize aggregation inside the join branch

summarize by SHA1

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ProfileAvailabilityeqErrorexcludes:ProfileAvailability field:"ProfileAvailability" value:"Error"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypeeq
  • ImageLoaded
field:"ActionType" kind:eq value:"ImageLoaded"
FileNameends_with
  • .cpl
field:"file_name" kind:ends_with value:".cpl"
GlobalPrevalencelt
  • 100 transforms: coalesce_default:0
  • 50 transforms: coalesce_default:0
field:"GlobalPrevalence" kind:lt
Signeris_null
  • (no value, null check)
field:"Signer" kind:is_null

Output fields

These fields are emitted when the rule matches.

FieldSource
SHA1summarize