From d710c11889ca99773a0ef84fe592656be2a2697b Mon Sep 17 00:00:00 2001 From: Jonhnathan <26856693+w0rk3r@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:33:51 -0300 Subject: [PATCH 1/6] [New Rule] Potential PowerShell Obfuscation via String Reordering --- ...vasion_posh_obfuscation_string_format.toml | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 rules/windows/defense_evasion_posh_obfuscation_string_format.toml diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml new file mode 100644 index 00000000000..575a1539f01 --- /dev/null +++ b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml @@ -0,0 +1,106 @@ +[metadata] +creation_date = "2025/04/03" +integration = ["windows"] +maturity = "production" +updated_date = "2025/04/03" + +[rule] +author = ["Elastic"] +description = """ +Identifies PowerShell scripts that use string reordering and runtime reconstruction techniques as a form of obfuscation. +These methods are designed to evade static analysis and bypass security protections such as the Antimalware Scan +Interface (AMSI). +""" +from = "now-9m" +language = "esql" +license = "Elastic License v2" +name = "Potential PowerShell Obfuscation via String Reordering" +risk_score = 21 +rule_id = "e903ce9a-5ce6-4246-bb14-75ed3ec2edf5" +setup = """## Setup + +The 'PowerShell Script Block Logging' logging policy must be enabled. +Steps to implement the logging policy with Advanced Audit Configuration: + +``` +Computer Configuration > +Administrative Templates > +Windows PowerShell > +Turn on PowerShell Script Block Logging (Enable) +``` + +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" +tags = [ + "Domain: Endpoint", + "OS: Windows", + "Use Case: Threat Detection", + "Tactic: Defense Evasion", + "Data Source: PowerShell Logs", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "esql" + +query = ''' +FROM logs-windows.powershell_operational* metadata _id, _version, _index +| WHERE event.code == "4104" + +// Look for scripts with more than 500 chars that contain a related keyword +| EVAL script_len = LENGTH(powershell.file.script_block_text) +| WHERE script_len > 500 +| WHERE powershell.file.script_block_text LIKE "*{0}*" + +// 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, """((\{\d+\}){2,}["']\s?-f|::Format[^\{]+(\{\d+\}){2,})""", "🔥") + +// Count how many format-like patterns were detected by calculating the number of 🔥 characters inserted +| EVAL count = script_len - LENGTH(REPLACE(replaced_with_fire, "🔥", "")) + +// Keep the fields relevant to the query, although this is not needed as the alert is populated using _id +| KEEP count, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id +| WHERE count >= 3 +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1027" +name = "Obfuscated Files or Information" +reference = "https://attack.mitre.org/techniques/T1027/" + +[[rule.threat.technique]] +id = "T1140" +name = "Deobfuscate/Decode Files or Information" +reference = "https://attack.mitre.org/techniques/T1140/" + + +[rule.threat.tactic] +id = "TA0005" +name = "Defense Evasion" +reference = "https://attack.mitre.org/tactics/TA0005/" +[[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/" + From b41935c5429d7cc6a282e2bb56f5e5f11e2dfab5 Mon Sep 17 00:00:00 2001 From: Jonhnathan <26856693+w0rk3r@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:47:36 -0300 Subject: [PATCH 2/6] Update defense_evasion_posh_obfuscation_string_format.toml --- .../windows/defense_evasion_posh_obfuscation_string_format.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml index 575a1539f01..71483566080 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml @@ -60,7 +60,7 @@ FROM logs-windows.powershell_operational* metadata _id, _version, _index // 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, """((\{\d+\}){2,}["']\s?-f|::Format[^\{]+(\{\d+\}){2,})""", "🔥") -// Count how many format-like patterns were detected by calculating the number of 🔥 characters inserted +// Count how many patterns were detected by calculating the number of 🔥 characters inserted | EVAL count = script_len - LENGTH(REPLACE(replaced_with_fire, "🔥", "")) // Keep the fields relevant to the query, although this is not needed as the alert is populated using _id From 81e96a85fb48c663b10de37f14c7bacb5c0a6c1d Mon Sep 17 00:00:00 2001 From: Jonhnathan <26856693+w0rk3r@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:48:08 -0300 Subject: [PATCH 3/6] Update rules/windows/defense_evasion_posh_obfuscation_string_format.toml --- .../windows/defense_evasion_posh_obfuscation_string_format.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml index 71483566080..4bf13a9db1a 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml @@ -64,7 +64,7 @@ FROM logs-windows.powershell_operational* metadata _id, _version, _index | EVAL count = script_len - LENGTH(REPLACE(replaced_with_fire, "🔥", "")) // Keep the fields relevant to the query, although this is not needed as the alert is populated using _id -| KEEP count, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id +| KEEP count, powershell.file.script_block_id, powershell.file.script_block_text, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id | WHERE count >= 3 ''' From fd5ecd0d18e76232ac3f3c1f8a9855b401f2e966 Mon Sep 17 00:00:00 2001 From: Jonhnathan <26856693+w0rk3r@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:35:17 -0300 Subject: [PATCH 4/6] Update defense_evasion_posh_obfuscation_string_format.toml --- .../defense_evasion_posh_obfuscation_string_format.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml index 4bf13a9db1a..8ad633baef0 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml @@ -61,11 +61,11 @@ FROM logs-windows.powershell_operational* metadata _id, _version, _index | EVAL replaced_with_fire = REPLACE(powershell.file.script_block_text, """((\{\d+\}){2,}["']\s?-f|::Format[^\{]+(\{\d+\}){2,})""", "🔥") // Count how many patterns were detected by calculating the number of 🔥 characters inserted -| EVAL count = script_len - LENGTH(REPLACE(replaced_with_fire, "🔥", "")) +| EVAL count = LENGTH(replaced_with_fire) - LENGTH(REPLACE(replaced_with_fire, "🔥", "")) // Keep the fields relevant to the query, although this is not needed as the alert is populated using _id -| KEEP count, powershell.file.script_block_id, powershell.file.script_block_text, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id -| WHERE count >= 3 +| KEEP count, replaced_with_fire, powershell.file.script_block_text, replaced_with_fire, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.id +| WHERE count > 3 ''' From d409020d10ab53add1f3c4a55b32b7b90a3db086 Mon Sep 17 00:00:00 2001 From: Jonhnathan <26856693+w0rk3r@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:38:44 -0300 Subject: [PATCH 5/6] Update rules/windows/defense_evasion_posh_obfuscation_string_format.toml --- .../windows/defense_evasion_posh_obfuscation_string_format.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml index 8ad633baef0..54b5cabbafd 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml @@ -42,7 +42,6 @@ tags = [ "Use Case: Threat Detection", "Tactic: Defense Evasion", "Data Source: PowerShell Logs", - "Resources: Investigation Guide", ] timestamp_override = "event.ingested" type = "esql" From 2061b9bd3674d90951aa90c1ff341ae0a64beeff Mon Sep 17 00:00:00 2001 From: Jonhnathan <26856693+w0rk3r@users.noreply.github.com> Date: Tue, 15 Apr 2025 12:21:54 -0300 Subject: [PATCH 6/6] Update rules/windows/defense_evasion_posh_obfuscation_string_format.toml --- .../windows/defense_evasion_posh_obfuscation_string_format.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml index 54b5cabbafd..bd0fd0c6762 100644 --- a/rules/windows/defense_evasion_posh_obfuscation_string_format.toml +++ b/rules/windows/defense_evasion_posh_obfuscation_string_format.toml @@ -63,7 +63,7 @@ FROM logs-windows.powershell_operational* metadata _id, _version, _index | EVAL count = LENGTH(replaced_with_fire) - LENGTH(REPLACE(replaced_with_fire, "🔥", "")) // 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, replaced_with_fire, powershell.file.script_block_id, file.path, powershell.sequence, powershell.total, _id, _index, host.name, agent.id, user.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 '''