Skip to content

[Rule Tuning] PowerShell ES|QL Rules Tuning #4785

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 17, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion rules/windows/defense_evasion_posh_obfuscation_backtick.toml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
creation_date = "2025/04/15"
integration = ["windows"]
maturity = "production"
updated_date = "2025/04/15"
updated_date = "2025/06/10"

[rule]
author = ["Elastic"]
@@ -64,6 +64,10 @@ FROM logs-windows.powershell_operational* metadata _id, _version, _index
// Filter FPs, and due to the behavior of the LIKE operator, allow null values
| WHERE (file.name NOT LIKE "TSS_*.psm1" or file.name IS NULL)
| WHERE
// VSCode Shell integration
NOT powershell.file.script_block_text LIKE "*$([char]0x1b)]633*"
'''

[[rule.threat]]
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
creation_date = "2025/04/16"
integration = ["windows"]
maturity = "production"
updated_date = "2025/04/16"
updated_date = "2025/06/10"

[rule]
author = ["Elastic"]
@@ -65,8 +65,12 @@ FROM logs-windows.powershell_operational* metadata _id, _version, _index
// Keep the fields relevant to the query, although this is not needed as the alert is populated using _id
| KEEP special_count, script_len, proportion, replaced_with_fire, powershell.file.script_block_text, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id
// Filter for scripts with a 25%+ proportion of numbers
| WHERE proportion > 0.25
// Filter for scripts with a 30%+ proportion of numbers
| WHERE proportion > 0.30
// Exclude noisy patterns
| WHERE
NOT powershell.file.script_block_text RLIKE """.*\"[a-fA-F0-9]{64}\"\,.*"""
'''


Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
creation_date = "2025/04/14"
integration = ["windows"]
maturity = "production"
updated_date = "2025/04/14"
updated_date = "2025/06/10"

[rule]
author = ["Elastic"]
@@ -14,7 +14,7 @@ from = "now-9m"
language = "esql"
license = "Elastic License v2"
name = "Potential PowerShell Obfuscation via String Concatenation"
risk_score = 21
risk_score = 47
rule_id = "f6d8c743-0916-4483-8333-3c6f107e0caa"
setup = """## Setup
@@ -34,7 +34,7 @@ Steps to implement the logging policy via registry:
reg add "hklm\\SOFTWARE\\Policies\\Microsoft\\Windows\\PowerShell\\ScriptBlockLogging" /v EnableScriptBlockLogging /t REG_DWORD /d 1
```
"""
severity = "low"
severity = "medium"
tags = [
"Domain: Endpoint",
"OS: Windows",
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
creation_date = "2025/04/03"
integration = ["windows"]
maturity = "production"
updated_date = "2025/04/03"
updated_date = "2025/06/10"

[rule]
author = ["Elastic"]
@@ -65,6 +65,20 @@ FROM logs-windows.powershell_operational* metadata _id, _version, _index
// Keep the fields relevant to the query, although this is not needed as the alert is populated using _id
| KEEP count, replaced_with_fire, powershell.file.script_block_text, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id
| WHERE count > 3
// Exclude Noisy Patterns
// Icinga Framework
| WHERE (file.name NOT LIKE "framework_cache.psm1" or file.name IS NULL)
| WHERE NOT
// https://wtfbins.wtf/17
(
(powershell.file.script_block_text LIKE "*sentinelbreakpoints*" OR
powershell.file.script_block_text LIKE "*:::::\\\\windows\\\\sentinel*")
AND
(powershell.file.script_block_text LIKE "*$local:Bypassed*" OR
powershell.file.script_block_text LIKE "*origPSExecutionPolicyPreference*")
)
'''


Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
creation_date = "2025/04/16"
integration = ["windows"]
maturity = "production"
updated_date = "2025/04/16"
updated_date = "2025/06/10"

[rule]
author = ["Elastic"]
@@ -50,20 +50,23 @@ query = '''
FROM logs-windows.powershell_operational* metadata _id, _version, _index
| WHERE event.code == "4104"
// Replace repeated spaces used for formatting after a new line with a single space to reduce FPs
| EVAL dedup_space_script_block = REPLACE(powershell.file.script_block_text, """\n\s+""", "\n ")
// Look for scripts with more than 1000 chars that contain a related keyword
| EVAL script_len = LENGTH(powershell.file.script_block_text)
| EVAL script_len = LENGTH(dedup_space_script_block)
| WHERE script_len > 1000
// Replace string format expressions with 🔥 to enable counting the occurrence of the patterns we are looking for
// The emoji is used because it's unlikely to appear in scripts and has a consistent character length of 1
| EVAL replaced_with_fire = REPLACE(powershell.file.script_block_text, """[\s\$\{\}\+\@\=\(\)\^\\\"~\[\]\?\.]""", "🔥")
| EVAL replaced_with_fire = REPLACE(dedup_space_script_block, """[\s\$\{\}\+\@\=\(\)\^\\\"~\[\]\?\.]""", "🔥")
// Count the occurrence of numbers and their proportion to the total chars in the script
| EVAL special_count = script_len - LENGTH(REPLACE(replaced_with_fire, "🔥", ""))
| EVAL proportion = special_count::double / script_len::double
// Keep the fields relevant to the query, although this is not needed as the alert is populated using _id
| KEEP special_count, script_len, proportion, replaced_with_fire, powershell.file.script_block_text, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id
| KEEP special_count, script_len, proportion, dedup_space_script_block, replaced_with_fire, powershell.file.script_block_text, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id
// Filter for scripts with a 75%+ proportion of numbers
| WHERE proportion > 0.75