Potential PowerShell Obfuscation via Backtick-Escaped Variable Expansion
Detects PowerShell scripts that uses backtick-escaped characters inside ${} variable expansion (multiple backticks
between word characters) to reconstruct strings at runtime. Attackers use variable-expansion obfuscation to split
keywords, hide commands, 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 that uses backtick-escaped characters inside `${}` variable expansion (multiple backticks
11between word characters) to reconstruct strings at runtime. Attackers use variable-expansion obfuscation to split
12keywords, hide commands, and evade static analysis and AMSI.
13"""
14from = "now-9m"
15language = "esql"
16license = "Elastic License v2"
17name = "Potential PowerShell Obfuscation via Backtick-Escaped Variable Expansion"
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 Backtick-Escaped Variable Expansion
24
25This rule identifies Windows PowerShell Script Block Logging events where backtick-escaped characters are embedded within `${}` variable expansion. This technique can be used to split tokens and reconstruct variable names or keywords at runtime, reducing the effectiveness of simple string-based detections and content scanning.
26
27Focus analysis on (1) who executed the script, (2) where it executed, (3) how much of the script is obfuscated, and (4) what the script ultimately does after deobfuscation. Higher `Esql.script_block_pattern_count`, elevated `powershell.file.script_block_entropy_bits`, and unexpected script origin (for example, unusual `file.path`) increase the likelihood of malicious intent.
28
29#### Key alert fields to review
30
31- `user.name`, `user.domain`, `user.id`: Account execution context for correlation, prioritization, and scoping.
32- `host.name`, `host.id`: Host execution context for correlation, prioritization, and scoping.
33- `file.path`, `file.directory`, `file.name`: File-origin context when the script block is sourced from an on-disk file.
34- `powershell.file.script_block_text`: Script block content that matched the detection logic.
35- `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.
36- `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.
37- `Esql.script_block_pattern_count`: Count of matches for the detection pattern(s) observed in the script block content.
38- `powershell.file.script_block_entropy_bits`: Shannon entropy of the script block. Higher values may indicate obfuscation.
39- `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.
40- `powershell.file.script_block_unique_symbols`: Count of distinct characters present in the script block.
41- `powershell.file.script_block_length`: Script block length (size) context.
42
43#### Possible investigation steps
44
45- Validate execution scope and context:
46 - Review `host.name` / `host.id` to understand where the script ran and whether the endpoint role makes this activity unexpected.
47 - Review `user.name`, `user.domain`, and `user.id` to determine whether the account is expected to run PowerShell on this host and to identify any unusual account usage patterns in your environment.
48 - If `file.path` / `file.name` / `file.directory` are present, determine whether the script appears to originate from an expected location (for example, a managed scripts directory) versus an unusual or user-writable location.
49
50- Reconstruct the complete script before interpreting intent:
51 - Pivot on `powershell.file.script_block_id` and collect all related fragments.
52 - Use `powershell.sequence` and `powershell.total` to verify you have the full set of fragments and to order them correctly.
53 - Review the reconstructed `powershell.file.script_block_text` for staging behavior (for example, an initial deobfuscation routine followed by a second stage that performs the primary action).
54
55- Locate the obfuscated variable expansions and normalize the content:
56 - Use `Esql.script_block_tmp` to quickly identify the positions of suspicious `${}` expansions, then review the corresponding sections in `powershell.file.script_block_text`.
57 - Use `Esql.script_block_pattern_count` to estimate how pervasive the obfuscation is. A higher count is more consistent with deliberate evasion than isolated escaping.
58 - In the matched `${}` segments, assess what the obfuscated expansion is intended to represent by mentally removing backtick escapes and looking for recognizable tokens (cmdlet/function names, variable names, or string literals) that the script is trying to hide.
59
60- Assess behavior indicated by the script content:
61 - Identify whether the script uses dynamic invocation patterns, such as building an invocation target at runtime or executing reconstructed strings.
62 - Look for decoding and deobfuscation constructs (string concatenation, character-by-character reconstruction, data transformation, decompression) that may reveal embedded or second-stage content.
63 - Extract and document any clear indicators contained in the script text (remote endpoints, file paths, registry paths, service/task names, or additional scripts referenced). Use these indicators to scope impact across hosts.
64
65- Use alert-side obfuscation metrics to prioritize and focus review:
66 - Compare `powershell.file.script_block_entropy_bits`, `powershell.file.script_block_unique_symbols`, and `powershell.file.script_block_surprisal_stdev` against what is typical in your environment for administrative scripts.
67 - Treat scripts with high entropy and many unique symbols as higher risk, especially when combined with multiple obfuscated `${}` expansions.
68
69- Scope prevalence and recurrence:
70 - Search for other Script Block Logging events on the same `host.id` and `user.id` that include similar backtick-escaped `${}` patterns.
71 - Look for repeated occurrences of the same `file.name` / `file.path` across multiple hosts, which may indicate a shared script or distributed execution.
72 - If the script contains unique strings or indicators, use them to identify additional affected endpoints.
73
74### False positive analysis
75
76- Legitimate scripts that implement complex string building or variable-name handling and happen to use backticks within `${}` expansion (more common in developer tooling, templating, or edge-case input handling).
77- Auto-generated PowerShell produced by administrative automation that uses nonstandard escaping or runtime string construction.
78
79When evaluating potential false positives, weigh consistency (same `user.id`, `host.id`, and `file.path` over time) against indicators of compromise (unexpected user/host pairing, high obfuscation density, high entropy, or evidence of follow-on actions).
80
81### Response and remediation
82
83- If the activity is suspicious or confirmed malicious:
84 - Contain the affected endpoint to prevent additional execution and limit potential lateral movement.
85 - Preserve evidence from the alert, including the full reconstructed `powershell.file.script_block_text`, `powershell.file.script_block_id`, `powershell.sequence` / `powershell.total`, and any `file.path` / `file.name` values.
86 - If an on-disk source is indicated by `file.path`, collect the referenced script and related files for review and remove or quarantine malicious artifacts according to your procedures.
87 - Investigate for follow-on effects suggested by the script content (persistence, payload delivery, configuration changes) and remediate any identified artifacts.
88 - Review the impacted `user.id` for compromise, revoke active sessions as appropriate, and reset credentials based on your incident response policy.
89 - Use extracted indicators and distinctive script fragments to hunt for additional affected hosts and users.
90
91- If the activity is verified benign:
92 - Document the legitimate script source, expected `file.path` / `file.name`, and the normal execution context (`user.id`, `host.id`) to speed up future triage.
93 - Monitor for deviations in execution context or significant changes in obfuscation metrics (for example, increased `Esql.script_block_pattern_count` or higher entropy) that could indicate abuse of an otherwise legitimate script.
94"""
95risk_score = 73
96rule_id = "d43f2b43-02a1-4219-8ce9-10929a32a618"
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 = "high"
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"
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(powershell.file.script_block_text, """\$\{(\w++`){2,}\w++\}""", "🔥")
125
126// count how many patterns were detected by calculating the number of 🔥 characters inserted
127| eval Esql.script_block_pattern_count = length(Esql.script_block_tmp) - length(replace(Esql.script_block_tmp, "🔥", ""))
128
129// keep the fields relevant to the query, although this is not needed as the alert is populated using _id
130| keep
131 Esql.script_block_pattern_count,
132 Esql.script_block_length,
133 Esql.script_block_tmp,
134 powershell.file.*,
135 file.path,
136 file.name,
137 powershell.sequence,
138 powershell.total,
139 _id,
140 _version,
141 _index,
142 host.name,
143 host.id,
144 agent.id,
145 user.id
146
147// Filter for scripts that match the pattern at least once
148| where Esql.script_block_pattern_count >= 1
149'''
150
151
152[[rule.threat]]
153framework = "MITRE ATT&CK"
154[[rule.threat.technique]]
155id = "T1027"
156name = "Obfuscated Files or Information"
157reference = "https://attack.mitre.org/techniques/T1027/"
158
159[[rule.threat.technique]]
160id = "T1140"
161name = "Deobfuscate/Decode Files or Information"
162reference = "https://attack.mitre.org/techniques/T1140/"
163
164
165[rule.threat.tactic]
166id = "TA0005"
167name = "Defense Evasion"
168reference = "https://attack.mitre.org/tactics/TA0005/"
169[[rule.threat]]
170framework = "MITRE ATT&CK"
171[[rule.threat.technique]]
172id = "T1059"
173name = "Command and Scripting Interpreter"
174reference = "https://attack.mitre.org/techniques/T1059/"
175[[rule.threat.technique.subtechnique]]
176id = "T1059.001"
177name = "PowerShell"
178reference = "https://attack.mitre.org/techniques/T1059/001/"
179
180
181
182[rule.threat.tactic]
183id = "TA0002"
184name = "Execution"
185reference = "https://attack.mitre.org/tactics/TA0002/"
186
187[rule.investigation_fields]
188field_names = [
189 "@timestamp",
190 "user.name",
191 "user.id",
192 "user.domain",
193 "powershell.file.script_block_text",
194 "powershell.file.script_block_id",
195 "powershell.sequence",
196 "powershell.total",
197 "file.path",
198 "file.directory",
199 "file.name",
200 "process.pid",
201 "host.name",
202 "host.id",
203 "powershell.file.script_block_length"
204]
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 Backtick-Escaped Variable Expansion
This rule identifies Windows PowerShell Script Block Logging events where backtick-escaped characters are embedded within ${} variable expansion. This technique can be used to split tokens and reconstruct variable names or keywords at runtime, reducing the effectiveness of simple string-based detections and content scanning.
Focus analysis on (1) who executed the script, (2) where it executed, (3) how much of the script is obfuscated, and (4) what the script ultimately does after deobfuscation. Higher Esql.script_block_pattern_count, elevated powershell.file.script_block_entropy_bits, and unexpected script origin (for example, unusual file.path) increase the likelihood of malicious intent.
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
-
Validate execution scope and context:
- Review
host.name/host.idto understand where the script ran and whether the endpoint role makes this activity unexpected. - Review
user.name,user.domain, anduser.idto determine whether the account is expected to run PowerShell on this host and to identify any unusual account usage patterns in your environment. - If
file.path/file.name/file.directoryare present, determine whether the script appears to originate from an expected location (for example, a managed scripts directory) versus an unusual or user-writable location.
- Review
-
Reconstruct the complete script before interpreting intent:
- Pivot on
powershell.file.script_block_idand collect all related fragments. - Use
powershell.sequenceandpowershell.totalto verify you have the full set of fragments and to order them correctly. - Review the reconstructed
powershell.file.script_block_textfor staging behavior (for example, an initial deobfuscation routine followed by a second stage that performs the primary action).
- Pivot on
-
Locate the obfuscated variable expansions and normalize the content:
- Use
Esql.script_block_tmpto quickly identify the positions of suspicious${}expansions, then review the corresponding sections inpowershell.file.script_block_text. - Use
Esql.script_block_pattern_countto estimate how pervasive the obfuscation is. A higher count is more consistent with deliberate evasion than isolated escaping. - In the matched
${}segments, assess what the obfuscated expansion is intended to represent by mentally removing backtick escapes and looking for recognizable tokens (cmdlet/function names, variable names, or string literals) that the script is trying to hide.
- Use
-
Assess behavior indicated by the script content:
- Identify whether the script uses dynamic invocation patterns, such as building an invocation target at runtime or executing reconstructed strings.
- Look for decoding and deobfuscation constructs (string concatenation, character-by-character reconstruction, data transformation, decompression) that may reveal embedded or second-stage content.
- Extract and document any clear indicators contained in the script text (remote endpoints, file paths, registry paths, service/task names, or additional scripts referenced). Use these indicators to scope impact across hosts.
-
Use alert-side obfuscation metrics to prioritize and focus review:
- Compare
powershell.file.script_block_entropy_bits,powershell.file.script_block_unique_symbols, andpowershell.file.script_block_surprisal_stdevagainst what is typical in your environment for administrative scripts. - Treat scripts with high entropy and many unique symbols as higher risk, especially when combined with multiple obfuscated
${}expansions.
- Compare
-
Scope prevalence and recurrence:
- Search for other Script Block Logging events on the same
host.idanduser.idthat include similar backtick-escaped${}patterns. - Look for repeated occurrences of the same
file.name/file.pathacross multiple hosts, which may indicate a shared script or distributed execution. - If the script contains unique strings or indicators, use them to identify additional affected endpoints.
- Search for other Script Block Logging events on the same
False positive analysis
- Legitimate scripts that implement complex string building or variable-name handling and happen to use backticks within
${}expansion (more common in developer tooling, templating, or edge-case input handling). - Auto-generated PowerShell produced by administrative automation that uses nonstandard escaping or runtime string construction.
When evaluating potential false positives, weigh consistency (same user.id, host.id, and file.path over time) against indicators of compromise (unexpected user/host pairing, high obfuscation density, high entropy, or evidence of follow-on actions).
Response and remediation
-
If the activity is suspicious or confirmed malicious:
- Contain the affected endpoint to prevent additional execution and limit potential lateral movement.
- Preserve evidence from the alert, including the full reconstructed
powershell.file.script_block_text,powershell.file.script_block_id,powershell.sequence/powershell.total, and anyfile.path/file.namevalues. - If an on-disk source is indicated by
file.path, collect the referenced script and related files for review and remove or quarantine malicious artifacts according to your procedures. - Investigate for follow-on effects suggested by the script content (persistence, payload delivery, configuration changes) and remediate any identified artifacts.
- Review the impacted
user.idfor compromise, revoke active sessions as appropriate, and reset credentials based on your incident response policy. - Use extracted indicators and distinctive script fragments to hunt for additional affected hosts and users.
-
If the activity is verified benign:
- Document the legitimate script source, expected
file.path/file.name, and the normal execution context (user.id,host.id) to speed up future triage. - Monitor for deviations in execution context or significant changes in obfuscation metrics (for example, increased
Esql.script_block_pattern_countor higher entropy) that could indicate abuse of an otherwise legitimate script.
- Document the legitimate script source, expected
Related rules
- Potential PowerShell Obfuscation via Character Array Reconstruction
- Potential PowerShell Obfuscation via Concatenated Dynamic Command Invocation
- Potential PowerShell Obfuscation via High Numeric Character Proportion
- Potential PowerShell Obfuscation via Invalid Escape Sequences
- PowerShell Script with Encryption/Decryption Capabilities