diff --git a/rules/windows/defense_evasion_posh_obfuscation_backtick.toml b/rules/windows/defense_evasion_posh_obfuscation_backtick.toml index 02ed5c699cd..e72a6c79d30 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_backtick.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_backtick.toml @@ -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]] diff --git a/rules/windows/defense_evasion_posh_obfuscation_high_number_proportion.toml b/rules/windows/defense_evasion_posh_obfuscation_high_number_proportion.toml index 7e6ad3e35bf..5a0801619c0 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_high_number_proportion.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_high_number_proportion.toml @@ -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}\"\,.*""" ''' diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_concat.toml b/rules/windows/defense_evasion_posh_obfuscation_string_concat.toml index 583c5f8a508..cc5dc737e02 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_string_concat.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_string_concat.toml @@ -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", diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml index bd0fd0c6762..bf73561031a 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml @@ -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*") + ) ''' diff --git a/rules/windows/defense_evasion_posh_obfuscation_whitespace_special_proportion.toml b/rules/windows/defense_evasion_posh_obfuscation_whitespace_special_proportion.toml index 3dfcca8a918..13d5b42623d 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_whitespace_special_proportion.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_whitespace_special_proportion.toml @@ -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