Potential PowerShell Obfuscation via Special Character Overuse
Detects PowerShell scripts dominated by whitespace and special characters with low symbol diversity, a profile often produced by formatting or encoding obfuscation. Attackers use symbol-heavy encoding or formatting (for example, SecureString-style blobs or character-level transforms) to hide payloads and evade static analysis and AMSI.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/04/16"
3integration = ["windows"]
4maturity = "production"
5updated_date = "2026/02/09"
6
7[rule]
8author = ["Elastic"]
9description = """
10Detects PowerShell scripts dominated by whitespace and special characters with low symbol diversity, a profile often
11produced by formatting or encoding obfuscation. Attackers use symbol-heavy encoding or formatting (for example,
12SecureString-style blobs or character-level transforms) to hide payloads and evade static analysis and AMSI.
13"""
14from = "now-9m"
15language = "esql"
16license = "Elastic License v2"
17name = "Potential PowerShell Obfuscation via Special Character Overuse"
18note = """## Triage and analysis
19
20> **Disclaimer**:
21> This guide was created by humans with the assistance of generative AI. While its contents have been manually curated to include the most valuable information, always validate assumptions and adjust procedures to match your internal runbooks and incident triage and response policies.
22
23### Investigating Potential PowerShell Obfuscation via Special Character Overuse
24
25This rule flags PowerShell script block content that is unusually long and dominated by whitespace and a narrow set of special characters. This profile is often associated with formatting or encoding obfuscation where payload logic is transformed into symbol-heavy strings and reconstructed at runtime. Use the steps below to validate execution context, reconstruct full content, determine likely intent, and scope related activity.
26
27#### Key alert fields to review
28
29- `user.name`, `user.domain`, `user.id`: Account execution context for correlation, prioritization, and scoping.
30- `host.name`, `host.id`: Host execution context for correlation, prioritization, and scoping.
31- `file.path`, `file.directory`, `file.name`: File-origin context when the script block is sourced from an on-disk file.
32- `powershell.file.script_block_text`: Script block content that matched the detection logic.
33- `powershell.file.script_block_id`, `powershell.sequence`, `powershell.total`: Script block metadata to pivot to other fragments or reconstruct full script content when split across multiple events.
34- `Esql.script_block_tmp`: Transformed script block where detection patterns replace original content with a marker to support scoring/counting and quickly spot match locations.
35- `Esql.script_block_ratio`: Proportion of the script block's characters that match the alert's target character set, divided by total script length (0-1).
36- `Esql.script_block_pattern_count`: Count of matches for the detection pattern(s) observed in the script block content.
37- `powershell.file.script_block_entropy_bits`: Shannon entropy of the script block. Higher values may indicate obfuscation.
38- `powershell.file.script_block_surprisal_stdev`: Standard deviation of surprisal across the script block. Low values indicate uniform randomness. High values indicate mixed patterns and variability.
39- `powershell.file.script_block_unique_symbols`: Count of distinct characters present in the script block.
40- `powershell.file.script_block_length`: Script block length (size) context.
41
42#### Possible investigation steps
43
44- Review alert context and scope:
45 - Use `@timestamp` to identify when the activity occurred and to bound the timeline for correlation.
46 - Review `host.name` and `host.id` to understand which endpoint produced the script block.
47 - Review `user.name`, `user.domain`, and `user.id` to determine whether the account is expected to run PowerShell on this host.
48 - If multiple alerts are present, group by `host.id` and `user.id` to identify concentrated or repeat activity.
49
50- Analyze the script block content for obfuscation and intent:
51 - Inspect `powershell.file.script_block_text` to understand what is being executed. Obfuscation commonly presents as large blocks of escaped characters, excessive punctuation, and character-level reassembly.
52 - Use `Esql.script_block_tmp` to quickly locate symbol-dense regions, then interpret the corresponding content in `powershell.file.script_block_text`.
53 - Use `Esql.script_block_ratio`, `powershell.file.script_block_unique_symbols`, `powershell.file.script_block_entropy_bits`, and `powershell.file.script_block_surprisal_stdev` to gauge how atypical the content is compared to known-good scripts in your environment.
54 - Identify deobfuscation and runtime execution patterns such as repeated string replacement, concatenation, `[char]` casting, `-join`, formatting operators, reflection, and dynamic invocation (for example, `Invoke-Expression` or executing decoded strings).
55 - Capture any embedded indicators from `powershell.file.script_block_text`, including URLs, hostnames, IP addresses, file paths, registry paths, or scheduled task/service names.
56
57- Reconstruct full script content when logged in chunks:
58 - If `powershell.total` indicates multiple fragments, pivot on `powershell.file.script_block_id` and reassemble the script in `powershell.sequence` order.
59 - Confirm completeness by comparing observed fragments to `powershell.total`. Missing segments can hide key decode or execution stages.
60
61- Validate script origin and expected usage:
62 - Review `file.path`, `file.directory`, and `file.name` (when present) to determine whether the script originated from disk, a module path, or an unusual location.
63 - If the script is file-backed, assess whether the file location and naming are consistent with approved administration and automation practices for the host and user.
64
65- Scope for related PowerShell activity:
66 - Pivot on `powershell.file.script_block_hash` (when available) to identify repeated executions of the same content across hosts and users.
67 - Review additional script blocks on the same `host.id` and `user.id` around the alert time for staging behavior (variable setup, decoding routines, or creation of additional script blocks).
68 - Use stable substrings from `powershell.file.script_block_text` (unique function names or strings) to find related executions that may not match this specific obfuscation profile.
69
70- Correlate with adjacent telemetry to confirm execution chain and impact (if available):
71 - Use `host.id`, `user.id`, and `@timestamp` to pivot into process telemetry and determine which process initiated PowerShell and whether the parent process is expected.
72 - Review activity on the same host around the alert time for signs of follow-on behavior such as outbound connections, file creation/modification, registry changes, or persistence mechanisms consistent with the recovered script logic.
73
74### False positive analysis
75
76- Legitimate automation can embed large protected or serialized values (for example, encrypted configuration blobs or SecureString exports) that appear symbol-heavy.
77- Deployment and configuration tooling may generate templated PowerShell with extensive escaping or large here-strings, especially when embedding JSON/XML or code as data.
78- Authorized security testing may use obfuscation techniques that resemble this behavior.
79- To validate a benign source, confirm the script's provenance and repeatability:
80 - Check whether `file.path` (when present) and `powershell.file.script_block_hash` consistently map to an approved script, owner, and expected execution pattern.
81 - Compare the alerting `user.id` and `host.id` against known automation accounts and managed endpoints; unexpected combinations warrant escalation.
82
83### Response and remediation
84
85- If malicious or suspicious activity is confirmed:
86 - Contain the affected host according to your incident response procedures to prevent additional execution and lateral movement.
87 - Preserve evidence for triage and forensics, including `powershell.file.script_block_text` (and any reconstructed content), `powershell.file.script_block_id`, `powershell.sequence`, `powershell.total`, `powershell.file.script_block_hash` (if available), `file.path` (if present), and the execution context (`host.name`, `host.id`, `user.name`, `user.domain`, `user.id`, `agent.id`, `@timestamp`).
88 - Scope the activity by searching for the same `powershell.file.script_block_hash` and any extracted indicators across the environment.
89 - Identify and remediate follow-on actions associated with the script (downloaded payloads, dropped files, persistence changes, or credential access). Apply blocking controls for confirmed indicators where feasible.
90 - If the script content indicates credential material handling or unauthorized automation, rotate affected credentials and review account activity for misuse.
91
92- If the activity is determined to be benign:
93 - Document the script owner, purpose, and expected execution context (hosts, users, and schedule), using `file.path` and `powershell.file.script_block_hash` (when available) as stable identifiers.
94 - Monitor for drift, such as execution by different users/hosts, unexpected file paths, or material changes in the script block content.
95"""
96risk_score = 47
97rule_id = "6ddb6c33-00ce-4acd-832a-24b251512023"
98setup = """## Setup
99
100PowerShell Script Block Logging must be enabled to generate the events used by this rule (e.g., 4104).
101Setup instructions: https://ela.st/powershell-logging-setup
102
103This rule uses the following fields that require the Windows Integration v3.3.0 and up: `powershell.file.script_block_unique_symbols`.
104"""
105severity = "medium"
106tags = [
107 "Domain: Endpoint",
108 "OS: Windows",
109 "Use Case: Threat Detection",
110 "Tactic: Defense Evasion",
111 "Data Source: PowerShell Logs",
112 "Resources: Investigation Guide",
113]
114timestamp_override = "event.ingested"
115type = "esql"
116
117query = '''
118from logs-windows.powershell_operational* metadata _id, _version, _index
119| where event.code == "4104"
120
121// filter for scripts with low unique symbol counts, which can indicate special character obfuscation
122| where powershell.file.script_block_unique_symbols < 50
123
124// replace repeated spaces used for formatting after a new line with a single space to reduce FPs
125| eval Esql.script_block_tmp = replace(powershell.file.script_block_text, """\n\s+""", "\n ")
126
127// Look for scripts with more than 1000 chars
128| eval Esql.script_block_length = length(Esql.script_block_tmp)
129| where Esql.script_block_length > 1000
130
131// replace the patterns we are looking for with the 🔥 emoji to enable counting them
132// The emoji is used because it's unlikely to appear in scripts and has a consistent character length of 1
133| eval Esql.script_block_tmp = replace(
134 Esql.script_block_tmp,
135 """[\s\$\{\}\+\@\=\(\)\^\\\"~\[\]\?\./%#\`\'\;\-\!\*]""",
136 "🔥"
137)
138
139// count how many patterns were detected by calculating the number of 🔥 characters inserted
140| eval Esql.script_block_count = Esql.script_block_length - length(replace(Esql.script_block_tmp, "🔥", ""))
141
142// Calculate the ratio of special characters to total length
143| eval Esql.script_block_ratio = Esql.script_block_count::double / Esql.script_block_length::double
144
145// keep the fields relevant to the query, although this is not needed as the alert is populated using _id
146| keep
147 Esql.script_block_count,
148 Esql.script_block_length,
149 Esql.script_block_ratio,
150 Esql.script_block_tmp,
151 powershell.file.*,
152 file.path,
153 powershell.sequence,
154 powershell.total,
155 _id,
156 _version,
157 _index,
158 host.name,
159 host.id,
160 agent.id,
161 user.id
162
163// Filter for scripts with high whitespace and special character ratio
164| where Esql.script_block_ratio >= 0.75
165'''
166
167
168[[rule.threat]]
169framework = "MITRE ATT&CK"
170[[rule.threat.technique]]
171id = "T1027"
172name = "Obfuscated Files or Information"
173reference = "https://attack.mitre.org/techniques/T1027/"
174
175[[rule.threat.technique]]
176id = "T1140"
177name = "Deobfuscate/Decode Files or Information"
178reference = "https://attack.mitre.org/techniques/T1140/"
179
180
181[rule.threat.tactic]
182id = "TA0005"
183name = "Defense Evasion"
184reference = "https://attack.mitre.org/tactics/TA0005/"
185[[rule.threat]]
186framework = "MITRE ATT&CK"
187[[rule.threat.technique]]
188id = "T1059"
189name = "Command and Scripting Interpreter"
190reference = "https://attack.mitre.org/techniques/T1059/"
191[[rule.threat.technique.subtechnique]]
192id = "T1059.001"
193name = "PowerShell"
194reference = "https://attack.mitre.org/techniques/T1059/001/"
195
196
197
198[rule.threat.tactic]
199id = "TA0002"
200name = "Execution"
201reference = "https://attack.mitre.org/tactics/TA0002/"
202
203[rule.investigation_fields]
204field_names = [
205 "@timestamp",
206 "user.name",
207 "user.id",
208 "user.domain",
209 "powershell.file.script_block_text",
210 "powershell.file.script_block_id",
211 "powershell.sequence",
212 "powershell.total",
213 "file.path",
214 "file.directory",
215 "file.name",
216 "process.pid",
217 "host.name",
218 "host.id",
219 "powershell.file.script_block_length"
220]
Triage and analysis
Disclaimer: This guide was created by humans with the assistance of generative AI. While its contents have been manually curated to include the most valuable information, always validate assumptions and adjust procedures to match your internal runbooks and incident triage and response policies.
Investigating Potential PowerShell Obfuscation via Special Character Overuse
This rule flags PowerShell script block content that is unusually long and dominated by whitespace and a narrow set of special characters. This profile is often associated with formatting or encoding obfuscation where payload logic is transformed into symbol-heavy strings and reconstructed at runtime. Use the steps below to validate execution context, reconstruct full content, determine likely intent, and scope related activity.
Key alert fields to review
user.name,user.domain,user.id: Account execution context for correlation, prioritization, and scoping.host.name,host.id: Host execution context for correlation, prioritization, and scoping.file.path,file.directory,file.name: File-origin context when the script block is sourced from an on-disk file.powershell.file.script_block_text: Script block content that matched the detection logic.powershell.file.script_block_id,powershell.sequence,powershell.total: Script block metadata to pivot to other fragments or reconstruct full script content when split across multiple events.Esql.script_block_tmp: Transformed script block where detection patterns replace original content with a marker to support scoring/counting and quickly spot match locations.Esql.script_block_ratio: Proportion of the script block's characters that match the alert's target character set, divided by total script length (0-1).Esql.script_block_pattern_count: Count of matches for the detection pattern(s) observed in the script block content.powershell.file.script_block_entropy_bits: Shannon entropy of the script block. Higher values may indicate obfuscation.powershell.file.script_block_surprisal_stdev: Standard deviation of surprisal across the script block. Low values indicate uniform randomness. High values indicate mixed patterns and variability.powershell.file.script_block_unique_symbols: Count of distinct characters present in the script block.powershell.file.script_block_length: Script block length (size) context.
Possible investigation steps
-
Review alert context and scope:
- Use
@timestampto identify when the activity occurred and to bound the timeline for correlation. - Review
host.nameandhost.idto understand which endpoint produced the script block. - Review
user.name,user.domain, anduser.idto determine whether the account is expected to run PowerShell on this host. - If multiple alerts are present, group by
host.idanduser.idto identify concentrated or repeat activity.
- Use
-
Analyze the script block content for obfuscation and intent:
- Inspect
powershell.file.script_block_textto understand what is being executed. Obfuscation commonly presents as large blocks of escaped characters, excessive punctuation, and character-level reassembly. - Use
Esql.script_block_tmpto quickly locate symbol-dense regions, then interpret the corresponding content inpowershell.file.script_block_text. - Use
Esql.script_block_ratio,powershell.file.script_block_unique_symbols,powershell.file.script_block_entropy_bits, andpowershell.file.script_block_surprisal_stdevto gauge how atypical the content is compared to known-good scripts in your environment. - Identify deobfuscation and runtime execution patterns such as repeated string replacement, concatenation,
[char]casting,-join, formatting operators, reflection, and dynamic invocation (for example,Invoke-Expressionor executing decoded strings). - Capture any embedded indicators from
powershell.file.script_block_text, including URLs, hostnames, IP addresses, file paths, registry paths, or scheduled task/service names.
- Inspect
-
Reconstruct full script content when logged in chunks:
- If
powershell.totalindicates multiple fragments, pivot onpowershell.file.script_block_idand reassemble the script inpowershell.sequenceorder. - Confirm completeness by comparing observed fragments to
powershell.total. Missing segments can hide key decode or execution stages.
- If
-
Validate script origin and expected usage:
- Review
file.path,file.directory, andfile.name(when present) to determine whether the script originated from disk, a module path, or an unusual location. - If the script is file-backed, assess whether the file location and naming are consistent with approved administration and automation practices for the host and user.
- Review
-
Scope for related PowerShell activity:
- Pivot on
powershell.file.script_block_hash(when available) to identify repeated executions of the same content across hosts and users. - Review additional script blocks on the same
host.idanduser.idaround the alert time for staging behavior (variable setup, decoding routines, or creation of additional script blocks). - Use stable substrings from
powershell.file.script_block_text(unique function names or strings) to find related executions that may not match this specific obfuscation profile.
- Pivot on
-
Correlate with adjacent telemetry to confirm execution chain and impact (if available):
- Use
host.id,user.id, and@timestampto pivot into process telemetry and determine which process initiated PowerShell and whether the parent process is expected. - Review activity on the same host around the alert time for signs of follow-on behavior such as outbound connections, file creation/modification, registry changes, or persistence mechanisms consistent with the recovered script logic.
- Use
False positive analysis
- Legitimate automation can embed large protected or serialized values (for example, encrypted configuration blobs or SecureString exports) that appear symbol-heavy.
- Deployment and configuration tooling may generate templated PowerShell with extensive escaping or large here-strings, especially when embedding JSON/XML or code as data.
- Authorized security testing may use obfuscation techniques that resemble this behavior.
- To validate a benign source, confirm the script's provenance and repeatability:
- Check whether
file.path(when present) andpowershell.file.script_block_hashconsistently map to an approved script, owner, and expected execution pattern. - Compare the alerting
user.idandhost.idagainst known automation accounts and managed endpoints; unexpected combinations warrant escalation.
- Check whether
Response and remediation
-
If malicious or suspicious activity is confirmed:
- Contain the affected host according to your incident response procedures to prevent additional execution and lateral movement.
- Preserve evidence for triage and forensics, including
powershell.file.script_block_text(and any reconstructed content),powershell.file.script_block_id,powershell.sequence,powershell.total,powershell.file.script_block_hash(if available),file.path(if present), and the execution context (host.name,host.id,user.name,user.domain,user.id,agent.id,@timestamp). - Scope the activity by searching for the same
powershell.file.script_block_hashand any extracted indicators across the environment. - Identify and remediate follow-on actions associated with the script (downloaded payloads, dropped files, persistence changes, or credential access). Apply blocking controls for confirmed indicators where feasible.
- If the script content indicates credential material handling or unauthorized automation, rotate affected credentials and review account activity for misuse.
-
If the activity is determined to be benign:
- Document the script owner, purpose, and expected execution context (hosts, users, and schedule), using
file.pathandpowershell.file.script_block_hash(when available) as stable identifiers. - Monitor for drift, such as execution by different users/hosts, unexpected file paths, or material changes in the script block content.
- Document the script owner, purpose, and expected execution context (hosts, users, and schedule), using
Related rules
- Potential Process Injection via PowerShell
- Dynamic IEX Reconstruction via Method String Access
- Potential Dynamic IEX Reconstruction via Environment Variables
- Potential PowerShell Obfuscation via Reverse Keywords
- Potential PowerShell Obfuscation via String Concatenation