mirror of
https://github.com/elastic/detection-rules
synced 2026-06-08 14:00:08 +00:00
67c7d1d1a9
* [Rule Tuning] Windows High-Severity Rules Revamp - 10
* ++
* Update defense_evasion_untrusted_driver_loaded.toml
* Update defense_evasion_untrusted_driver_loaded.toml
* Apply suggestion from @w0rk3r
(cherry picked from commit 2df45b2a6b)
167 lines
11 KiB
TOML
167 lines
11 KiB
TOML
[metadata]
|
|
creation_date = "2025/04/16"
|
|
maturity = "production"
|
|
updated_date = "2026/05/01"
|
|
|
|
[rule]
|
|
author = ["Elastic"]
|
|
description = """
|
|
Identifies PowerShell script blocks linked to multiple distinct PowerShell detections via the same ScriptBlock ID,
|
|
indicating compound suspicious behavior. Attackers often chain obfuscation, decoding, and execution within a single
|
|
script block.
|
|
"""
|
|
from = "now-9m"
|
|
language = "esql"
|
|
license = "Elastic License v2"
|
|
name = "Potential Malicious PowerShell Based on Alert Correlation"
|
|
risk_score = 73
|
|
rule_id = "f770ce79-05fd-4d74-9866-1c5d66c9b34b"
|
|
severity = "high"
|
|
tags = [
|
|
"Domain: Endpoint",
|
|
"OS: Windows",
|
|
"Use Case: Threat Detection",
|
|
"Tactic: Execution",
|
|
"Rule Type: Higher-Order Rule",
|
|
"Resources: Investigation Guide",
|
|
]
|
|
timestamp_override = "event.ingested"
|
|
type = "esql"
|
|
|
|
query = '''
|
|
from .alerts-security.* metadata _id
|
|
|
|
// Filter for PowerShell related alerts
|
|
| where kibana.alert.rule.name like "*PowerShell*"
|
|
|
|
// as alerts don't have non-ECS fields, parse the script block ID using grok
|
|
| grok message "ScriptBlock ID: (?<Esql.script_block_id>.+)"
|
|
| where Esql.script_block_id is not null
|
|
|
|
// keep relevant fields for further processing
|
|
| keep kibana.alert.rule.name, Esql.script_block_id, _id, user.id, process.pid, host.id
|
|
|
|
// count distinct alerts and filter for matches above the threshold
|
|
| stats
|
|
Esql.kibana_alert_rule_name_count_distinct = count_distinct(kibana.alert.rule.name),
|
|
Esql.kibana_alert_rule_name_values = values(kibana.alert.rule.name),
|
|
Esql._id_values = values(_id),
|
|
Esql.user_id_values = values(user.id),
|
|
Esql.process_pid_values = values(process.pid),
|
|
Esql.host_id_values = values(host.id)
|
|
by Esql.script_block_id
|
|
|
|
// Apply detection threshold
|
|
| where Esql.kibana_alert_rule_name_count_distinct >= 5
|
|
| eval user.id = MV_MIN(Esql.user_id_values),
|
|
process.pid = MV_MIN(Esql.process_pid_values),
|
|
host.id = MV_MIN(Esql.host_id_values)
|
|
| keep host.id, user.id, process.pid, Esql.*
|
|
'''
|
|
|
|
note = """## Triage and analysis
|
|
|
|
### Investigating Potential Malicious PowerShell Based on Alert Correlation
|
|
|
|
#### Possible investigation steps
|
|
|
|
- What does the ES|QL grouped alert preserve about the suspicious PowerShell mix?
|
|
- Focus: treat `Esql.script_block_id`, `Esql.kibana_alert_rule_name_values`, `Esql._id_values`, preserved `host.id` / `user.id`, and `Esql.kibana_alert_rule_name_count_distinct` as search clues, not evidence.
|
|
- Implication: escalate if rule names span obfuscation, download, execution, persistence, credential access, or defense evasion for one host/user; lower suspicion only when recovered source alerts/events show one recognized detection-validation script or controlled encoded-content automation pattern.
|
|
|
|
- Do the contributing alerts bind the summary to one script execution?
|
|
- Focus: recover contributing alerts around grouped-alert time using preserved host/user, `Esql.kibana_alert_rule_name_values`, and script-block ID; use `Esql._id_values` only when alert search supports those IDs.
|
|
- Hint: recover contributing alerts before interpreting grouped behavior; ES|QL grouped alerts lack member-event Timeline pivots and reliable source-event time, PID, or entity anchors.
|
|
- Implication: treat as one execution chain only when source alerts and events align to one host, one user, one source-event window, and one script-block ID; keep unresolved if timestamps, script evidence, PID reuse risk, or entity scope conflict.
|
|
|
|
- Can you reconstruct and interpret the source PowerShell script block?
|
|
- Focus: using recovered source-event host, process, and time, query PowerShell 4104 or source events; match `powershell.file.script_block_id`, order `powershell.sequence` / `powershell.total`, and read script-block text.
|
|
- Implication: escalate when reconstructed text shows encoded/decoded stages, download cradles, reflection, hosted System.Management.Automation execution, credential access, persistence, or defense evasion; missing fragments or source PowerShell telemetry are unresolved, not benign.
|
|
|
|
- Which process and launch chain executed the script block?
|
|
- Focus: use recovered time, `host.id`, and process identifiers to find the process start; collect `process.entity_id`, `process.command_line`, `process.parent.command_line`, and `process.Ext.authentication_id`.
|
|
- Hint: if no process start appears, expand time first; if still missing, scope later file, registry, and network review to recovered source-event host/user/process/time.
|
|
- Implication: escalate when the launcher is a document, browser, remote-management tool, scheduled task, unexpected script or .NET host, or command line with encoded, hidden, bypass, download, or dynamic evaluation; lower suspicion only when the same parent, command, user, and host bind to the exact recovered benign workflow.
|
|
|
|
- Did the process stage payloads, persistence, or security-impacting changes?
|
|
- Focus: scope file and registry events to recovered `process.entity_id` or fallback source-event host/PID/time context; review `file.path`, `file.origin_url`, `registry.path`, `registry.value`, and `registry.data.strings`.
|
|
- Implication: escalate when the script writes executable or scriptable content to user-writable or startup paths, leaves internet provenance, modifies persistence or security keys, or later executes staged content; lower suspicion only when artifacts stay inside the exact recovered benign workflow. Missing file or registry telemetry does not clear the alert.
|
|
|
|
- Did network or session evidence fit offensive PowerShell use?
|
|
- Focus: scope DNS/connections to recovered `process.entity_id` or source-event host/PID/time context; read `dns.question.name`, `destination.ip`, and `destination.port`; when origin matters, bridge `process.Ext.authentication_id` to `winlog.event_data.TargetLogonId`.
|
|
- Implication: escalate when the script reaches rare or public destinations, pulls content, contacts infrastructure unrelated to the recovered workflow, or runs from an unexpected remote session; missing network or Windows Security telemetry is unresolved, not benign.
|
|
|
|
- If local evidence is suspicious or unresolved, does the same pattern recur elsewhere enough to change scope?
|
|
- Focus: related alerts for preserved `user.id` over 48 hours, looking for the same `Esql.kibana_alert_rule_name_values`, reconstructed script fragment, launch context, or extracted indicators. $investigate_0
|
|
- Hint: if user-scoped alerts are quiet or ambiguous, compare related alerts for preserved `host.id` over 48 hours. $investigate_1
|
|
- Implication: broaden response when the same script body, rule-name mix, or indicators appear on unrelated hosts or users; keep scope local when recurrence stays on the host under review, but do not close from recurrence alone.
|
|
|
|
- Weigh contributing-alert alignment, reconstructed script, launch chain, artifacts, destinations, and host/user scope; escalate for offensive tooling, unauthorized execution, staged payloads, persistence, or suspicious destinations; close only when source evidence binds to one exact benign workflow, using outside confirmation only for facts telemetry cannot prove; preserve artifacts and escalate on conflicts or missing telemetry.
|
|
|
|
### False positive analysis
|
|
|
|
- Authorized red-team, lab, or detection-validation can trigger several PowerShell rules on one script. Confirm alert mix, reconstructed script, launch chain, `user.id`, and `host.id` match exact test scope; if records are unavailable, recurrence can support scoping but cannot close the alert.
|
|
- Endpoint management or deployment automation can trigger multiple detections through encoded content, dynamic script generation, or controlled downloads. Confirm `powershell.file.script_block_text`, parent/child command lines, artifacts, and destinations align with one managed workflow. If script body, destinations, or follow-on artifacts diverge, do not close as benign.
|
|
- Before an exception, validate stable benign anchors: `user.id`, `host.id`, parent/child command lines, script fragment, and destination or artifact pattern. Avoid exceptions on ES|QL summary fields or `Esql.script_block_id`; they are alert-local or execution-specific.
|
|
|
|
### Response and remediation
|
|
|
|
- If confirmed benign, document the alert mix, reconstructed script, launch chain, and recovered host/user context before reversing containment. Build exceptions from stable recovered workflow anchors, not ES|QL summary fields alone.
|
|
- If suspicious but unconfirmed, preserve contributing alerts, `Esql._id_values`, `Esql.script_block_id`, rule-name values, reconstructed script text, recovered parent/child command lines, file/registry paths, and DNS/destination indicators before cleanup. Apply reversible containment first, such as temporary destination restrictions or heightened monitoring on recovered `host.id` and `user.id`; isolate or strengthen account action only if the script is still executing, staging payloads, or reaching suspicious destinations and host role allows.
|
|
- If confirmed malicious, record entity IDs, command lines, and indicators, then isolate the host when lateral-movement or active-payload risk is present. Stop the PowerShell process or descendants only after preserving evidence; if direct response is unavailable, escalate with the evidence set. Block confirmed malicious destinations, URLs, hashes, and script paths; eradicate only tied files, registry changes, tasks, services, and payloads; remediate the launch path; and review related hosts/users before destructive cleanup.
|
|
- Post-incident hardening: preserve or expand PowerShell 4104 logging, source-alert retention, endpoint process telemetry, and Windows Security logs when gaps limited recovery; where operations allow, restrict high-risk PowerShell through signed scripts, Constrained Language Mode, JEA, or WinRM controls. Document the rule-name mix, observables, and adjacent variants such as indirect System.Management.Automation execution.
|
|
"""
|
|
|
|
[rule.investigation_fields]
|
|
field_names = [
|
|
"host.id",
|
|
"user.id",
|
|
"process.pid",
|
|
"Esql.script_block_id",
|
|
"Esql._id_values",
|
|
"Esql.kibana_alert_rule_name_values",
|
|
"Esql.kibana_alert_rule_name_count_distinct",
|
|
]
|
|
|
|
[transform]
|
|
|
|
[[transform.investigate]]
|
|
label = "Alerts associated with the user"
|
|
description = ""
|
|
providers = [
|
|
[
|
|
{ excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
|
|
{ excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
|
|
]
|
|
]
|
|
relativeFrom = "now-48h/h"
|
|
relativeTo = "now"
|
|
|
|
[[transform.investigate]]
|
|
label = "Alerts associated with the host"
|
|
description = ""
|
|
providers = [
|
|
[
|
|
{ excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
|
|
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" }
|
|
]
|
|
]
|
|
relativeFrom = "now-48h/h"
|
|
relativeTo = "now"
|
|
|
|
[[rule.threat]]
|
|
framework = "MITRE ATT&CK"
|
|
[[rule.threat.technique]]
|
|
id = "T1059"
|
|
name = "Command and Scripting Interpreter"
|
|
reference = "https://attack.mitre.org/techniques/T1059/"
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1059.001"
|
|
name = "PowerShell"
|
|
reference = "https://attack.mitre.org/techniques/T1059/001/"
|
|
|
|
[rule.threat.tactic]
|
|
id = "TA0002"
|
|
name = "Execution"
|
|
reference = "https://attack.mitre.org/tactics/TA0002/"
|