Potential PowerShell Obfuscation via Character Array Reconstruction
Detects PowerShell scripts that reconstructs strings from char[] arrays, index lookups, or repeated ([char]NN)+ concatenation/join logic. Attackers use character-array reconstruction to hide commands, URLs, or payloads and evade static analysis and AMSI.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/04/14"
3integration = ["windows"]
4maturity = "production"
5updated_date = "2026/02/09"
6
7[rule]
8author = ["Elastic"]
9description = """
10Detects PowerShell scripts that reconstructs strings from char[] arrays, index lookups, or repeated ([char]NN)+
11concatenation/join logic. Attackers use character-array reconstruction to hide commands, URLs, or payloads and evade
12static analysis and AMSI.
13"""
14from = "now-9m"
15language = "esql"
16license = "Elastic License v2"
17name = "Potential PowerShell Obfuscation via Character Array Reconstruction"
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 Character Array Reconstruction
24
25This rule identifies PowerShell Script Block Logging content that reconstructs strings at runtime from character codes or character arrays. This technique is commonly used to conceal intent (for example, commands, URLs, or file paths) and can indicate attempts to evade static inspection.
26
27Focus triage on what strings are being rebuilt, who ran the script, where it ran, and whether the decoded content leads to follow-on activity.
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- Review `powershell.file.script_block_text` and use `Esql.script_block_tmp` to quickly spot the segments that triggered the match. Identify the reconstruction method (for example, `[char[]](...)`, repeated `([char]NN)+` concatenation, joins, or index-based lookups).
46- If the script block is split, reconstruct the full content using `powershell.file.script_block_id` together with `powershell.sequence` and `powershell.total`. Verify that all expected sequences are present and in order before drawing conclusions.
47- If `powershell.total` indicates multiple fragments but one or more `powershell.sequence` values are missing, treat the script as incomplete context. Attempt to retrieve the missing fragments and consider the possibility of log gaps or ingestion delays.
48- Deobfuscate the reconstructed strings by translating numeric character codes and arrays into their resulting text. Capture any decoded values that look like commands, script content, URLs, file paths, registry paths, or encoded blobs.
49- After decoding, re-review the full script block content for additional obfuscation layers or execution logic that is not covered by the character reconstruction pattern (for example, secondary decoding steps or dynamically-invoked script).
50- Use `Esql.script_block_pattern_count` as a quick measure of how heavily the script relies on this obfuscation pattern. Higher counts typically indicate more deliberate concealment and can help prioritize review.
51- Use the script statistics to support prioritization and comparison against expected baselines: `powershell.file.script_block_entropy_bits`, `powershell.file.script_block_surprisal_stdev`, `powershell.file.script_block_unique_symbols`, and `powershell.file.script_block_length`. Look for unusually long, high-entropy, or highly variable content relative to known-good PowerShell activity in your environment.
52- Review the execution context: `user.name`, `user.domain`, and `user.id` to understand who executed the script and whether the account matches expected administrative or automation usage for the `host.name` and `host.id` involved.
53- Review file origin indicators when present: `file.path`, `file.directory`, and `file.name`. Assess whether the script appears to originate from an expected location for your environment versus a user-writable, temporary, or unusual directory.
54- Pivot to the source event using `_id` and `_index` to review the full event payload and confirm whether additional relevant `powershell.file.*` context is available for analysis.
55- Scope for related activity by searching for additional script blocks that share the same `powershell.file.script_block_id`, similar `powershell.file.script_block_text` content, the same `file.path`, the same `user.id`, or the same `host.id` around the alert time window.
56- Correlate the alert timestamp with adjacent telemetry from the same host and user (process activity, network connections, file writes, registry modifications, and authentication events) to identify how PowerShell was launched (including the initiating/parent process) and what occurred immediately before and after the obfuscated content executed.
57- If decoded content indicates external communication or secondary payload retrieval, identify potential indicators (domains, IPs, URLs, file names) and check for additional occurrences across other hosts and users.
58- If decoded content indicates credential access, lateral movement, or persistence, expand the investigation to related accounts and hosts within the same timeframe and document the full execution chain.
59
60### False positive analysis
61
62- Some benign scripts reconstruct strings from character codes for formatting, localization, or to safely represent special characters. These cases are often limited in scope and decode to human-readable, expected values.
63- Software deployment, management, or monitoring tooling may generate PowerShell dynamically and use string reconstruction as an implementation detail. Validate whether the script source (`file.path`) and execution context (`user.id`, `host.id`) align with known tooling behavior.
64- Internal scripts may use light obfuscation to reduce casual tampering or to embed configuration values. Treat unknown sources, unexpected accounts, or unusually high `Esql.script_block_pattern_count` / `powershell.file.script_block_entropy_bits` as higher risk until validated.
65- If determined benign, document the script source and expected execution context (account and host) and retain the decoded strings for faster triage of future alerts.
66
67### Response and remediation
68
69- If malicious or suspicious activity is confirmed, contain the affected host to prevent additional execution and lateral movement. Consider restricting the involved account based on investigation results.
70- Preserve evidence: retain the full `powershell.file.script_block_text` and any reconstructed/decoded strings, along with related events for the same `powershell.file.script_block_id` (all `powershell.sequence` values).
71- If the script originates from disk (`file.path` present), collect and quarantine the referenced file and review for additional related artifacts created or modified around the alert time window.
72- Identify and remediate follow-on actions indicated by the decoded content (for example, downloaded payloads, persistence mechanisms, or changes to system configuration).
73- Hunt for spread: search for the same decoded indicators, similar reconstruction patterns, and elevated `Esql.script_block_pattern_count` across other hosts and users.
74- If account misuse is suspected, perform appropriate credential hygiene and review recent authentication activity for the affected `user.id` and related accounts.
75- Review and strengthen PowerShell controls and monitoring based on findings (for example, ensure Script Block Logging is consistently enabled and that anti-malware scanning integration is functioning as expected).
76"""
77risk_score = 73
78rule_id = "85e2d45e-a3df-4acf-83d3-21805f564ff4"
79setup = """## Setup
80
81PowerShell Script Block Logging must be enabled to generate the events used by this rule (e.g., 4104).
82Setup instructions: https://ela.st/powershell-logging-setup
83"""
84severity = "high"
85tags = [
86 "Domain: Endpoint",
87 "OS: Windows",
88 "Use Case: Threat Detection",
89 "Tactic: Defense Evasion",
90 "Data Source: PowerShell Logs",
91 "Resources: Investigation Guide",
92]
93timestamp_override = "event.ingested"
94type = "esql"
95
96query = '''
97from logs-windows.powershell_operational* metadata _id, _version, _index
98| where event.code == "4104"
99
100// Filter for scripts that contain the "char" keyword using MATCH, boosts the query performance
101| where powershell.file.script_block_text : "char"
102
103// replace the patterns we are looking for with the 🔥 emoji to enable counting them
104// The emoji is used because it's unlikely to appear in scripts and has a consistent character length of 1
105| eval Esql.script_block_tmp = replace(
106 powershell.file.script_block_text,
107 """(char\[\]\]\(\d+,\d+[^)]+|(\s?\(\[char\]\d+\s?\)\+){2,})""",
108 "🔥"
109)
110
111// count how many patterns were detected by calculating the number of 🔥 characters inserted
112| eval Esql.script_block_pattern_count = length(Esql.script_block_tmp) - length(replace(Esql.script_block_tmp, "🔥", ""))
113
114// keep the fields relevant to the query, although this is not needed as the alert is populated using _id
115| keep
116 Esql.script_block_pattern_count,
117 Esql.script_block_tmp,
118 powershell.file.*,
119 file.path,
120 powershell.sequence,
121 powershell.total,
122 _id,
123 _version,
124 _index,
125 host.name,
126 host.id,
127 agent.id,
128 user.id
129
130// Filter for scripts that match the pattern at least once
131| where Esql.script_block_pattern_count >= 1
132'''
133
134
135[[rule.threat]]
136framework = "MITRE ATT&CK"
137[[rule.threat.technique]]
138id = "T1027"
139name = "Obfuscated Files or Information"
140reference = "https://attack.mitre.org/techniques/T1027/"
141
142[[rule.threat.technique]]
143id = "T1140"
144name = "Deobfuscate/Decode Files or Information"
145reference = "https://attack.mitre.org/techniques/T1140/"
146
147
148[rule.threat.tactic]
149id = "TA0005"
150name = "Defense Evasion"
151reference = "https://attack.mitre.org/tactics/TA0005/"
152[[rule.threat]]
153framework = "MITRE ATT&CK"
154[[rule.threat.technique]]
155id = "T1059"
156name = "Command and Scripting Interpreter"
157reference = "https://attack.mitre.org/techniques/T1059/"
158[[rule.threat.technique.subtechnique]]
159id = "T1059.001"
160name = "PowerShell"
161reference = "https://attack.mitre.org/techniques/T1059/001/"
162
163
164
165[rule.threat.tactic]
166id = "TA0002"
167name = "Execution"
168reference = "https://attack.mitre.org/tactics/TA0002/"
169
170[rule.investigation_fields]
171field_names = [
172 "@timestamp",
173 "user.name",
174 "user.id",
175 "user.domain",
176 "powershell.file.script_block_text",
177 "powershell.file.script_block_id",
178 "powershell.sequence",
179 "powershell.total",
180 "file.path",
181 "file.directory",
182 "file.name",
183 "process.pid",
184 "host.name",
185 "host.id",
186 "powershell.file.script_block_length"
187]
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 Character Array Reconstruction
This rule identifies PowerShell Script Block Logging content that reconstructs strings at runtime from character codes or character arrays. This technique is commonly used to conceal intent (for example, commands, URLs, or file paths) and can indicate attempts to evade static inspection.
Focus triage on what strings are being rebuilt, who ran the script, where it ran, and whether the decoded content leads to follow-on 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_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
powershell.file.script_block_textand useEsql.script_block_tmpto quickly spot the segments that triggered the match. Identify the reconstruction method (for example,[char[]](...), repeated([char]NN)+concatenation, joins, or index-based lookups). - If the script block is split, reconstruct the full content using
powershell.file.script_block_idtogether withpowershell.sequenceandpowershell.total. Verify that all expected sequences are present and in order before drawing conclusions. - If
powershell.totalindicates multiple fragments but one or morepowershell.sequencevalues are missing, treat the script as incomplete context. Attempt to retrieve the missing fragments and consider the possibility of log gaps or ingestion delays. - Deobfuscate the reconstructed strings by translating numeric character codes and arrays into their resulting text. Capture any decoded values that look like commands, script content, URLs, file paths, registry paths, or encoded blobs.
- After decoding, re-review the full script block content for additional obfuscation layers or execution logic that is not covered by the character reconstruction pattern (for example, secondary decoding steps or dynamically-invoked script).
- Use
Esql.script_block_pattern_countas a quick measure of how heavily the script relies on this obfuscation pattern. Higher counts typically indicate more deliberate concealment and can help prioritize review. - Use the script statistics to support prioritization and comparison against expected baselines:
powershell.file.script_block_entropy_bits,powershell.file.script_block_surprisal_stdev,powershell.file.script_block_unique_symbols, andpowershell.file.script_block_length. Look for unusually long, high-entropy, or highly variable content relative to known-good PowerShell activity in your environment. - Review the execution context:
user.name,user.domain, anduser.idto understand who executed the script and whether the account matches expected administrative or automation usage for thehost.nameandhost.idinvolved. - Review file origin indicators when present:
file.path,file.directory, andfile.name. Assess whether the script appears to originate from an expected location for your environment versus a user-writable, temporary, or unusual directory. - Pivot to the source event using
_idand_indexto review the full event payload and confirm whether additional relevantpowershell.file.*context is available for analysis. - Scope for related activity by searching for additional script blocks that share the same
powershell.file.script_block_id, similarpowershell.file.script_block_textcontent, the samefile.path, the sameuser.id, or the samehost.idaround the alert time window. - Correlate the alert timestamp with adjacent telemetry from the same host and user (process activity, network connections, file writes, registry modifications, and authentication events) to identify how PowerShell was launched (including the initiating/parent process) and what occurred immediately before and after the obfuscated content executed.
- If decoded content indicates external communication or secondary payload retrieval, identify potential indicators (domains, IPs, URLs, file names) and check for additional occurrences across other hosts and users.
- If decoded content indicates credential access, lateral movement, or persistence, expand the investigation to related accounts and hosts within the same timeframe and document the full execution chain.
False positive analysis
- Some benign scripts reconstruct strings from character codes for formatting, localization, or to safely represent special characters. These cases are often limited in scope and decode to human-readable, expected values.
- Software deployment, management, or monitoring tooling may generate PowerShell dynamically and use string reconstruction as an implementation detail. Validate whether the script source (
file.path) and execution context (user.id,host.id) align with known tooling behavior. - Internal scripts may use light obfuscation to reduce casual tampering or to embed configuration values. Treat unknown sources, unexpected accounts, or unusually high
Esql.script_block_pattern_count/powershell.file.script_block_entropy_bitsas higher risk until validated. - If determined benign, document the script source and expected execution context (account and host) and retain the decoded strings for faster triage of future alerts.
Response and remediation
- If malicious or suspicious activity is confirmed, contain the affected host to prevent additional execution and lateral movement. Consider restricting the involved account based on investigation results.
- Preserve evidence: retain the full
powershell.file.script_block_textand any reconstructed/decoded strings, along with related events for the samepowershell.file.script_block_id(allpowershell.sequencevalues). - If the script originates from disk (
file.pathpresent), collect and quarantine the referenced file and review for additional related artifacts created or modified around the alert time window. - Identify and remediate follow-on actions indicated by the decoded content (for example, downloaded payloads, persistence mechanisms, or changes to system configuration).
- Hunt for spread: search for the same decoded indicators, similar reconstruction patterns, and elevated
Esql.script_block_pattern_countacross other hosts and users. - If account misuse is suspected, perform appropriate credential hygiene and review recent authentication activity for the affected
user.idand related accounts. - Review and strengthen PowerShell controls and monitoring based on findings (for example, ensure Script Block Logging is consistently enabled and that anti-malware scanning integration is functioning as expected).
Related rules
- Potential PowerShell Obfuscation via Backtick-Escaped Variable Expansion
- 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