Detection rules › Kusto
Detect Unsigned executable launch from scheduled task
Persistence via Scheduled Tasks is a well-known technique used by adversaries to make sure their malware programs keep running an the compromised device. With this detection rule, you can search for unknown executables being launched from scheduled tasks. > [!WARNING] > This detection rule is the base for the detection. You will need to add environment specific finetuning in order to limit the BP detections on legitimate processes
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution | T1053.005 Scheduled Task/Job: Scheduled Task |
| Persistence | T1053.005 Scheduled Task/Job: Scheduled Task |
| Privilege Escalation | T1053.005 Scheduled Task/Job: Scheduled Task |
References
Event coverage
| Provider | Event/ActionType | Title |
|---|---|---|
| Sysmon | Event ID 1 | Process creation |
| Security-Auditing | Event ID 4688 | A new process has been created. |
| Defender-DeviceProcessEvents | any | Process activity (any) |
Rule body yaml
let scheduled_binaries = (
DeviceProcessEvents
| where ActionType !contains "aggregated"
| where Timestamp > ago(1h)
| where InitiatingProcessCommandLine == "svchost.exe -k netsvcs -p -s Schedule"
| distinct SHA1
);
let untrusted_binaries = (
scheduled_binaries
| join kind=leftanti (
DeviceFileCertificateInfo
| where Timestamp > ago(1h)
| summarize max_trusted=max(IsTrusted) by SHA1
| where max_trusted==1
) on SHA1
);
untrusted_binaries
| invoke FileProfile(SHA1,1000)
| where IsCertificateValid != 1 // Exclude signed binaries
| where (isnotempty(GlobalPrevalence) and GlobalPrevalence < 1000)
| join (
DeviceProcessEvents
| where ActionType !contains "aggregated"
| where InitiatingProcessCommandLine == "svchost.exe -k netsvcs -p -s Schedule"
) on SHA1
Stages and Predicates
Stage 0: let
let scheduled_binaries = ( <inlined as stages below>;
let untrusted_binaries = ( <inlined as stages below>;
The stages below define let untrusted_binaries (the rule's main pipeline source).
Stage 1: source
DeviceProcessEvents
Stage 2: where
| where ActionType !contains "aggregated"
Stage 3: where
| where Timestamp > ago(1h)
Stage 4: where
| where InitiatingProcessCommandLine == "svchost.exe -k netsvcs -p -s Schedule"
Stage 5: distinct
| distinct SHA1
Stage 6: join (negated)
| join kind=leftanti (
DeviceFileCertificateInfo
| where Timestamp > ago(1h)
| summarize max_trusted=max(IsTrusted) by SHA1
| where max_trusted==1
) on SHA1
The stages below run on untrusted_binaries (the outer pipeline).
Stage 7: invoke
untrusted_binaries
| invoke FileProfile(SHA1,1000)
Stage 8: where
| where IsCertificateValid != 1
Stage 9: where
| where (isnotempty(GlobalPrevalence) and GlobalPrevalence < 1000)
Stage 10: join
| join (
DeviceProcessEvents
| where ActionType !contains "aggregated"
| where InitiatingProcessCommandLine == "svchost.exe -k netsvcs -p -s Schedule"
) on SHA1
Stage 11: summarize
summarize by SHA1
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
ActionType | contains | aggregated |
max_trusted | eq | 1 |
ActionType | contains | aggregated |
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 |
|---|---|---|
GlobalPrevalence | is_not_null | |
GlobalPrevalence | lt |
|
InitiatingProcessCommandLine | eq |
|
IsCertificateValid | ne |
|
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 |