Potential PowerShell Obfuscation via String Reordering
Detects PowerShell scripts that uses format placeholders like "{0}{1}" with the -f operator or ::Format to reorder strings at runtime. Attackers use format-based reconstruction to hide commands or payload strings and evade static analysis and AMSI.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/04/03"
3integration = ["windows"]
4maturity = "production"
5updated_date = "2026/02/09"
6
7[rule]
8author = ["Elastic"]
9description = """
10Detects PowerShell scripts that uses format placeholders like "{0}{1}" with the -f operator or ::Format to reorder strings at runtime.
11Attackers use format-based reconstruction to hide commands or payload strings and evade static analysis and AMSI.
12"""
13from = "now-9m"
14language = "esql"
15license = "Elastic License v2"
16name = "Potential PowerShell Obfuscation via String Reordering"
17note = """## Triage and analysis
18
19> **Disclaimer**:
20> 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.
21
22### Investigating Potential PowerShell Obfuscation via String Reordering
23
24This alert indicates a PowerShell script block used indexed format placeholders (for example, "{0}{1}") with runtime formatting (for example, the `-f` operator or `::Format`) to reorder and reconstruct strings. This technique can hide meaningful strings (commands, URLs, artifact names) from straightforward text inspection and can be used as part of staged execution.
25
26Because the detection requires repeated occurrences of these patterns in a larger script block, triage should focus on (1) execution context (host and user), (2) script origin (file-backed vs. in-memory/interactive), and (3) what strings are being reconstructed and what actions they enable.
27
28#### Key alert fields to review
29
30- `user.name`, `user.domain`, `user.id`: Account execution context for correlation, prioritization, and scoping.
31- `host.name`, `host.id`: Host execution context for correlation, prioritization, and scoping.
32- `file.path`, `file.directory`, `file.name`: File-origin context when the script block is sourced from an on-disk file.
33- `powershell.file.script_block_text`: Script block content that matched the detection logic.
34- `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.
35- `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.
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- Confirm the execution context and initial scope:
45 - Identify the affected endpoint using `host.name` and `host.id`. Check for other security alerts or suspicious activity on the same host around the alert time.
46 - Identify the account context using `user.name`, `user.domain`, and `user.id`. Prioritize alerts where the user is unexpected for the host role or where the user does not typically run PowerShell.
47 - If `file.path` / `file.directory` / `file.name` are present, capture the script location and assess whether the path and filename are expected for that host and user. If file fields are missing, treat the activity as potentially interactive or in-memory and increase emphasis on correlated telemetry.
48
49- Review the script block content and determine what is being reconstructed:
50 - Start with `Esql.script_block_tmp` to quickly locate where formatting patterns occur, then use `powershell.file.script_block_text` as the authoritative source for analysis and evidence.
51 - Identify how formatting is used:
52 - Placeholder-only or placeholder-heavy format strings that primarily consist of `{n}` tokens.
53 - Out-of-order placeholder indexes (for example, `{3}{0}{2}{1}`) and repeated reordering blocks.
54 - Multiple reconstruction stages where formatted output is subsequently reformatted or concatenated.
55 - Reconstruct key strings by mapping placeholder indexes to the arguments/fragments used in each formatting operation. Record any reconstructed strings that indicate follow-on behavior (remote addresses, filenames, persistence identifiers, or execution flow).
56
57- Use the available scoring and statistics fields to guide prioritization:
58 - Review `Esql.script_block_pattern_count` to understand how heavily the script relies on formatting-based reconstruction. Higher counts generally increase suspicion.
59 - Review `powershell.file.script_block_entropy_bits`, `powershell.file.script_block_unique_symbols`, `powershell.file.script_block_surprisal_stdev`, and `powershell.file.script_block_length` to differentiate structured, readable scripts from highly variable or packed content.
60 - Treat these values as supporting signals; base the decision primarily on reconstructed strings and correlated activity.
61
62- Reconstruct full script content when split across multiple events:
63 - Pivot on `powershell.file.script_block_id` to gather all fragments for the same script block.
64 - Order fragments using `powershell.sequence` and confirm completeness using `powershell.total` before drawing conclusions.
65
66- Correlate and validate impact using adjacent telemetry available in your environment:
67 - Review other PowerShell script blocks from the same `host.id` and `user.id` to identify staging, deobfuscation, or follow-on execution.
68 - Correlate with endpoint telemetry for the same host and timeframe to understand how PowerShell was started and what occurred next (process ancestry, network activity, file/registry changes, and authentication activity). Use reconstructed strings to focus this correlation.
69
70- Expand the hunt to assess prevalence:
71 - Search for the same or similar content in `powershell.file.script_block_text` (shared fragments, repeated placeholder patterns) across other hosts.
72 - Use `Esql.script_block_pattern_count` and the script block statistics fields to identify other high-similarity or high-complexity scripts that may represent the same technique.
73
74### False positive analysis
75
76- Legitimate automation can use indexed placeholders to build dynamic output, reports, or templated configuration content. Benign usage is more likely when the resulting strings are human-readable and the script has an expected on-disk origin (`file.path` / `file.name`) and consistent execution over time.
77- Some internally developed frameworks generate PowerShell dynamically and may include repeated formatting patterns. Validate ownership of the script source and whether execution by the identified `user.id` on the identified `host.id` is expected.
78- Localization or templating logic may use indexed placeholders. This is typically associated with readable templates and stable execution patterns rather than multi-stage reconstruction of operational strings.
79
80### Response and remediation
81
82- If malicious or suspicious activity is confirmed:
83 - Contain the affected endpoint identified by `host.name` / `host.id` to prevent further execution and limit lateral movement.
84 - Preserve evidence, including `powershell.file.script_block_text`, `powershell.file.script_block_id`, `powershell.sequence`, `powershell.total`, and the alert enrichment fields (`Esql.script_block_tmp`, `Esql.script_block_pattern_count`, and script block statistics).
85 - Use reconstructed strings to drive scoping and impact assessment (look for related activity on the same host, and search for the same indicators across other hosts and users).
86
87- Eradication and recovery:
88 - Identify the execution mechanism and remove it (for example, an unexpected script file, a startup trigger, or other persistence identified during correlation).
89 - Remove or quarantine related artifacts discovered during analysis and validate that similar activity is not occurring on other endpoints.
90
91- If the activity is determined to be benign:
92 - Document the expected script source (`file.path` / `file.name`), the responsible team, and the expected execution context (`host.id`, `user.id`) to support faster triage of future alerts.
93 - Monitor for deviations from the established baseline (new hosts, new users, or materially different reconstructed strings).
94"""
95risk_score = 47
96rule_id = "e903ce9a-5ce6-4246-bb14-75ed3ec2edf5"
97setup = """## Setup
98
99PowerShell Script Block Logging must be enabled to generate the events used by this rule (e.g., 4104).
100Setup instructions: https://ela.st/powershell-logging-setup
101"""
102severity = "medium"
103tags = [
104 "Domain: Endpoint",
105 "OS: Windows",
106 "Use Case: Threat Detection",
107 "Tactic: Defense Evasion",
108 "Data Source: PowerShell Logs",
109 "Resources: Investigation Guide",
110]
111timestamp_override = "event.ingested"
112type = "esql"
113
114query = '''
115from logs-windows.powershell_operational* metadata _id, _version, _index
116| where event.code == "4104" and powershell.file.script_block_text like "*{0}*"
117
118// Filter out smaller scripts that are unlikely to implement obfuscation using the patterns we are looking for
119| eval Esql.script_block_length = length(powershell.file.script_block_text)
120| where Esql.script_block_length > 500
121
122// replace the patterns we are looking for with the 🔥 emoji to enable counting them
123// The emoji is used because it's unlikely to appear in scripts and has a consistent character length of 1
124| eval Esql.script_block_tmp = replace(
125 powershell.file.script_block_text,
126 """((\{\d+\}){2,}["']\s?-f|::Format[^\{]+(\{\d+\}){2,})""",
127 "🔥"
128)
129
130// count how many patterns were detected by calculating the number of 🔥 characters inserted
131| eval Esql.script_block_pattern_count = length(Esql.script_block_tmp) - length(replace(Esql.script_block_tmp, "🔥", ""))
132
133// keep the fields relevant to the query, although this is not needed as the alert is populated using _id
134| keep
135 Esql.script_block_pattern_count,
136 Esql.script_block_length,
137 Esql.script_block_tmp,
138 powershell.file.*,
139 file.path,
140 file.directory,
141 powershell.sequence,
142 powershell.total,
143 _id,
144 _version,
145 _index,
146 host.name,
147 host.id,
148 agent.id,
149 user.id
150
151// Filter for scripts that match the pattern at least five times
152| where Esql.script_block_pattern_count >= 5
153
154// Exclude Noisy Patterns
155
156// Icinga Framework
157| where not file.directory == "C:\\Program Files\\WindowsPowerShell\\Modules\\icinga-powershell-framework\\cache"
158 // ESQL requires this condition, otherwise it only returns matches where file.directory exists.
159 or file.directory IS NULL
160
161| where not (powershell.file.script_block_text LIKE "*GitBranchStatus*" AND
162 powershell.file.script_block_text LIKE "*$s.BranchBehindStatusSymbol.Text*")
163| where not
164 // https://wtfbins.wtf/17
165 (
166 (powershell.file.script_block_text like "*sentinelbreakpoints*" or
167 powershell.file.script_block_text like "*:::::\\\\windows\\\\sentinel*")
168 and
169 (powershell.file.script_block_text like "*$local:Bypassed*" or
170 powershell.file.script_block_text like "*origPSExecutionPolicyPreference*")
171 )
172'''
173
174
175[[rule.threat]]
176framework = "MITRE ATT&CK"
177[[rule.threat.technique]]
178id = "T1027"
179name = "Obfuscated Files or Information"
180reference = "https://attack.mitre.org/techniques/T1027/"
181
182[[rule.threat.technique]]
183id = "T1140"
184name = "Deobfuscate/Decode Files or Information"
185reference = "https://attack.mitre.org/techniques/T1140/"
186
187
188[rule.threat.tactic]
189id = "TA0005"
190name = "Defense Evasion"
191reference = "https://attack.mitre.org/tactics/TA0005/"
192[[rule.threat]]
193framework = "MITRE ATT&CK"
194[[rule.threat.technique]]
195id = "T1059"
196name = "Command and Scripting Interpreter"
197reference = "https://attack.mitre.org/techniques/T1059/"
198[[rule.threat.technique.subtechnique]]
199id = "T1059.001"
200name = "PowerShell"
201reference = "https://attack.mitre.org/techniques/T1059/001/"
202
203
204
205[rule.threat.tactic]
206id = "TA0002"
207name = "Execution"
208reference = "https://attack.mitre.org/tactics/TA0002/"
209
210[rule.investigation_fields]
211field_names = [
212 "@timestamp",
213 "user.name",
214 "user.id",
215 "user.domain",
216 "powershell.file.script_block_text",
217 "powershell.file.script_block_id",
218 "powershell.sequence",
219 "powershell.total",
220 "file.path",
221 "file.directory",
222 "file.name",
223 "process.pid",
224 "host.name",
225 "host.id",
226 "powershell.file.script_block_length"
227]
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 String Reordering
This alert indicates a PowerShell script block used indexed format placeholders (for example, "{0}{1}") with runtime formatting (for example, the -f operator or ::Format) to reorder and reconstruct strings. This technique can hide meaningful strings (commands, URLs, artifact names) from straightforward text inspection and can be used as part of staged execution.
Because the detection requires repeated occurrences of these patterns in a larger script block, triage should focus on (1) execution context (host and user), (2) script origin (file-backed vs. in-memory/interactive), and (3) what strings are being reconstructed and what actions they enable.
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_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
-
Confirm the execution context and initial scope:
- Identify the affected endpoint using
host.nameandhost.id. Check for other security alerts or suspicious activity on the same host around the alert time. - Identify the account context using
user.name,user.domain, anduser.id. Prioritize alerts where the user is unexpected for the host role or where the user does not typically run PowerShell. - If
file.path/file.directory/file.nameare present, capture the script location and assess whether the path and filename are expected for that host and user. If file fields are missing, treat the activity as potentially interactive or in-memory and increase emphasis on correlated telemetry.
- Identify the affected endpoint using
-
Review the script block content and determine what is being reconstructed:
- Start with
Esql.script_block_tmpto quickly locate where formatting patterns occur, then usepowershell.file.script_block_textas the authoritative source for analysis and evidence. - Identify how formatting is used:
- Placeholder-only or placeholder-heavy format strings that primarily consist of
{n}tokens. - Out-of-order placeholder indexes (for example,
{3}{0}{2}{1}) and repeated reordering blocks. - Multiple reconstruction stages where formatted output is subsequently reformatted or concatenated.
- Placeholder-only or placeholder-heavy format strings that primarily consist of
- Reconstruct key strings by mapping placeholder indexes to the arguments/fragments used in each formatting operation. Record any reconstructed strings that indicate follow-on behavior (remote addresses, filenames, persistence identifiers, or execution flow).
- Start with
-
Use the available scoring and statistics fields to guide prioritization:
- Review
Esql.script_block_pattern_countto understand how heavily the script relies on formatting-based reconstruction. Higher counts generally increase suspicion. - Review
powershell.file.script_block_entropy_bits,powershell.file.script_block_unique_symbols,powershell.file.script_block_surprisal_stdev, andpowershell.file.script_block_lengthto differentiate structured, readable scripts from highly variable or packed content. - Treat these values as supporting signals; base the decision primarily on reconstructed strings and correlated activity.
- Review
-
Reconstruct full script content when split across multiple events:
- Pivot on
powershell.file.script_block_idto gather all fragments for the same script block. - Order fragments using
powershell.sequenceand confirm completeness usingpowershell.totalbefore drawing conclusions.
- Pivot on
-
Correlate and validate impact using adjacent telemetry available in your environment:
- Review other PowerShell script blocks from the same
host.idanduser.idto identify staging, deobfuscation, or follow-on execution. - Correlate with endpoint telemetry for the same host and timeframe to understand how PowerShell was started and what occurred next (process ancestry, network activity, file/registry changes, and authentication activity). Use reconstructed strings to focus this correlation.
- Review other PowerShell script blocks from the same
-
Expand the hunt to assess prevalence:
- Search for the same or similar content in
powershell.file.script_block_text(shared fragments, repeated placeholder patterns) across other hosts. - Use
Esql.script_block_pattern_countand the script block statistics fields to identify other high-similarity or high-complexity scripts that may represent the same technique.
- Search for the same or similar content in
False positive analysis
- Legitimate automation can use indexed placeholders to build dynamic output, reports, or templated configuration content. Benign usage is more likely when the resulting strings are human-readable and the script has an expected on-disk origin (
file.path/file.name) and consistent execution over time. - Some internally developed frameworks generate PowerShell dynamically and may include repeated formatting patterns. Validate ownership of the script source and whether execution by the identified
user.idon the identifiedhost.idis expected. - Localization or templating logic may use indexed placeholders. This is typically associated with readable templates and stable execution patterns rather than multi-stage reconstruction of operational strings.
Response and remediation
-
If malicious or suspicious activity is confirmed:
- Contain the affected endpoint identified by
host.name/host.idto prevent further execution and limit lateral movement. - Preserve evidence, including
powershell.file.script_block_text,powershell.file.script_block_id,powershell.sequence,powershell.total, and the alert enrichment fields (Esql.script_block_tmp,Esql.script_block_pattern_count, and script block statistics). - Use reconstructed strings to drive scoping and impact assessment (look for related activity on the same host, and search for the same indicators across other hosts and users).
- Contain the affected endpoint identified by
-
Eradication and recovery:
- Identify the execution mechanism and remove it (for example, an unexpected script file, a startup trigger, or other persistence identified during correlation).
- Remove or quarantine related artifacts discovered during analysis and validate that similar activity is not occurring on other endpoints.
-
If the activity is determined to be benign:
- Document the expected script source (
file.path/file.name), the responsible team, and the expected execution context (host.id,user.id) to support faster triage of future alerts. - Monitor for deviations from the established baseline (new hosts, new users, or materially different reconstructed strings).
- Document the expected script source (
Related rules
- 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
- Potential PowerShell Obfuscation via Backtick-Escaped Variable Expansion