Detection rules › Kusto
Suspicious use of CPL file
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
| Tactic | Techniques |
|---|---|
| Stealth |
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
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.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ProfileAvailability | eq | Error | excludes:ProfileAvailability field:"ProfileAvailability" value:"Error" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"ImageLoaded" |
FileName | ends_with |
| field:"file_name" kind:ends_with value:".cpl" |
GlobalPrevalence | lt |
| field:"GlobalPrevalence" kind:lt |
Signer | is_null | field:"Signer" kind:is_null |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SHA1 | summarize |