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/09/18"
  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    "Noise: Medium",
111    "Performance: Normal",
112    "Profile: Recommended",
113    "Threat: Script-Based Execution",
114    "Rule Type: ES|QL",
115    "Platform: Windows",
116]
117timestamp_override = "event.ingested"
118type = "esql"
119
120query = '''
121from logs-windows.powershell_operational* metadata _id, _version, _index
122| where event.code == "4104" and powershell.file.script_block_text like "*{0}*"
123
124// Filter out smaller scripts that are unlikely to implement obfuscation using the patterns we are looking for
125| eval Esql.script_block_length = length(powershell.file.script_block_text)
126| where Esql.script_block_length > 500
127
128// replace the patterns we are looking for with the 🔥 emoji to enable counting them
129// The emoji is used because it's unlikely to appear in scripts and has a consistent character length of 1
130| eval Esql.script_block_tmp = replace(
131    powershell.file.script_block_text,
132    """((\{\d+\}){2,}["']\s?-f|::Format[^\{]+(\{\d+\}){2,})""",
133    "🔥"
134)
135
136// count how many patterns were detected by calculating the number of 🔥 characters inserted
137| eval Esql.script_block_pattern_count = length(Esql.script_block_tmp) - length(replace(Esql.script_block_tmp, "🔥", ""))
138
139// keep the fields relevant to the query, although this is not needed as the alert is populated using _id
140| keep
141    Esql.script_block_pattern_count,
142    Esql.script_block_length,
143    Esql.script_block_tmp,
144    powershell.file.*,
145    file.path,
146    file.directory,
147    powershell.sequence,
148    powershell.total,
149    _id,
150    _version,
151    _index,
152    host.name,
153    host.id,
154    agent.id,
155    user.id
156
157// Filter for scripts that match the pattern at least five times
158| where Esql.script_block_pattern_count >= 5
159
160// Exclude Noisy Patterns
161
162// Icinga Framework
163| where not file.directory == "C:\\Program Files\\WindowsPowerShell\\Modules\\icinga-powershell-framework\\cache"
164  // ESQL requires this condition, otherwise it only returns matches where file.directory exists.
165  or file.directory IS NULL
166
167| where not (powershell.file.script_block_text LIKE "*GitBranchStatus*" AND 
168    powershell.file.script_block_text LIKE "*$s.BranchBehindStatusSymbol.Text*")
169| where not
170    // https://wtfbins.wtf/17
171    (
172        (powershell.file.script_block_text like "*sentinelbreakpoints*" or
173         powershell.file.script_block_text like "*:::::\\\\windows\\\\sentinel*")
174        and
175        (powershell.file.script_block_text like "*$local:Bypassed*" or
176         powershell.file.script_block_text like "*origPSExecutionPolicyPreference*")
177    )
178'''
179
180
181[[rule.threat]]
182framework = "MITRE ATT&CK"
183
184[[rule.threat.technique]]
185id = "T1027"
186name = "Obfuscated Files or Information"
187reference = "https://attack.mitre.org/techniques/T1027/"
188
189[[rule.threat.technique.subtechnique]]
190id = "T1027.010"
191name = "Command Obfuscation"
192reference = "https://attack.mitre.org/techniques/T1027/010/"
193
194[[rule.threat.technique]]
195id = "T1140"
196name = "Deobfuscate/Decode Files or Information"
197reference = "https://attack.mitre.org/techniques/T1140/"
198
199[rule.threat.tactic]
200id = "TA0005"
201name = "Defense Evasion"
202reference = "https://attack.mitre.org/tactics/TA0005/"
203
204[[rule.threat]]
205framework = "MITRE ATT&CK"
206
207[[rule.threat.technique]]
208id = "T1059"
209name = "Command and Scripting Interpreter"
210reference = "https://attack.mitre.org/techniques/T1059/"
211
212[[rule.threat.technique.subtechnique]]
213id = "T1059.001"
214name = "PowerShell"
215reference = "https://attack.mitre.org/techniques/T1059/001/"
216
217[rule.threat.tactic]
218id = "TA0002"
219name = "Execution"
220reference = "https://attack.mitre.org/tactics/TA0002/"
221[rule.investigation_fields]
222field_names = [
223    "@timestamp",
224    "user.name",
225    "user.id",
226    "user.domain",
227    "powershell.file.script_block_text",
228    "powershell.file.script_block_id",
229    "powershell.sequence",
230    "powershell.total",
231    "file.path",
232    "file.directory",
233    "file.name",
234    "process.pid",
235    "host.name",
236    "host.id",
237    "powershell.file.script_block_length"
238]

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.name and host.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, and user.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.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.
  • Review the script block content and determine what is being reconstructed:

    • 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.
    • 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.
    • 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).
  • Use the available scoring and statistics fields to guide prioritization:

    • Review Esql.script_block_pattern_count to 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, and powershell.file.script_block_length to 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.
  • Reconstruct full script content when split across multiple events:

    • Pivot on powershell.file.script_block_id to gather all fragments for the same script block.
    • Order fragments using powershell.sequence and confirm completeness using powershell.total before drawing conclusions.
  • Correlate and validate impact using adjacent telemetry available in your environment:

    • Review other PowerShell script blocks from the same host.id and user.id to 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.
  • 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_count and the script block statistics fields to identify other high-similarity or high-complexity scripts that may represent the same technique.

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.id on the identified host.id is 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.id to 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).
  • 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).

Related rules

to-top