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.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Stealth | T1218.002 System Binary Proxy Execution: Control Panel |
References
Event coverage
| Provider | Event/ActionType | Title |
|---|---|---|
| Sysmon | Event ID 7 | Image loaded |
| Defender-DeviceImageLoadEvents | ImageLoaded | Image loaded |
Rule body kusto
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 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;
Derived from timeframe, default_global_prevalence.
The stages below define let loadedDlls (the rule's main pipeline source).
Stage 1: source
DeviceImageLoadEvents
Stage 2: where
| where ingestion_time() >= ago(timeframe)
Stage 3: extend
| extend SHA1=tolower(SHA1)
Stage 4: where
| where SHA1 in~ ((suspiciousCPLs|project SHA1)) and ActionType =~ "ImageLoaded"
The stages below run on loadedDlls (the outer pipeline).
Stage 5: join
loadedDlls
| join kind=leftouter suspiciousCPLs on SHA1
Stage 6: summarize
summarize by SHA1
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
ProfileAvailability | eq | Error |
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
ActionType | eq |
|
FileName | ends_with |
|
GlobalPrevalence | lt |
|
Signer | is_null |
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
SHA1 | summarize |