Potential PowerShell Obfuscation via High Numeric Character Proportion
Detects long PowerShell script block content with unusually high numeric character density (high digit-to-length ratio), often produced by byte arrays, character-code reconstruction, or embedded encoded blobs. Attackers use numeric-heavy obfuscation to conceal payloads and rebuild them at runtime to avoid static inspection.
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 long PowerShell script block content with unusually high numeric character density (high digit-to-length ratio), often produced by byte arrays, character-code reconstruction, or embedded encoded blobs.
11Attackers use numeric-heavy obfuscation to conceal payloads and rebuild them at runtime to avoid static inspection.
12"""
13from = "now-9m"
14language = "esql"
15license = "Elastic License v2"
16name = "Potential PowerShell Obfuscation via High Numeric Character Proportion"
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 High Numeric Character Proportion
23
24This rule flags long PowerShell script blocks with unusually digit-dense content. Numeric-heavy script blocks are often used to conceal payloads as byte arrays or character codes that are decoded at runtime. Triage should focus on reconstructing the full script content, determining how it was initiated, and identifying any decoded or executed secondary content.
25
26#### Key alert fields to review
27
28- `user.name`, `user.domain`, `user.id`: Account execution context for correlation, prioritization, and scoping.
29- `host.name`, `host.id`: Host execution context for correlation, prioritization, and scoping.
30- `file.path`, `file.directory`, `file.name`: File-origin context when the script block is sourced from an on-disk file.
31- `powershell.file.script_block_text`: Script block content that matched the detection logic.
32- `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.
33- `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.
34- `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).
35- `Esql.script_block_pattern_count`: Count of matches for the detection pattern(s) observed in the script block content.
36- `powershell.file.script_block_entropy_bits`: Shannon entropy of the script block. Higher values may indicate obfuscation.
37- `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.
38- `powershell.file.script_block_unique_symbols`: Count of distinct characters present in the script block.
39- `powershell.file.script_block_length`: Script block length (size) context.
40
41#### Possible investigation steps
42
43- Review `powershell.file.script_block_text` to characterize the numeric content:
44 - Look for long comma-separated numbers, repeated digit sequences, or `0x`-prefixed values that may represent reconstructed bytes.
45 - Identify string reconstruction patterns (for example, casting numeric values to characters) and any subsequent decoding or decompression logic.
46 - Note any execution primitives that would run derived content (for example, invoking dynamically built commands or loading content into memory).
47- If the script is fragmented, use `powershell.sequence` and `powershell.total` to collect the related script block events on the same `host.name` and `user.id` and reconstruct the complete content in the correct order before drawing conclusions.
48- Establish execution context and scope using `host.name`, `host.id`, `agent.id`, and `user.id`:
49 - Determine whether the user context is expected to run PowerShell and whether similar script blocks have occurred recently on the same host or by the same user.
50 - Look for other alerts on the same host or user that could indicate staging, persistence, or lateral movement.
51- Assess script origin using `file.path` and `file.directory` when present:
52 - Determine whether the script is sourced from a location consistent with approved administration or automation workflows.
53 - If the script is file-backed, check for other security telemetry referencing the same path to identify file creation, modification, or repeated execution patterns.
54- Correlate with adjacent telemetry (as available in your environment) using the host and user pivots above:
55 - Process execution telemetry near the alert time to identify the PowerShell host process and its parent, and to understand how PowerShell was launched.
56 - Network telemetry for outbound connections or downloads that could support payload retrieval or command and control.
57 - File activity for dropped payloads or staging artifacts related to the script content or its on-disk source.
58
59### False positive analysis
60
61- Legitimate scripts that embed binary content as numeric arrays (for example, packaging resources into scripts or deploying configuration blobs) can appear digit-dense.
62- Administrative tooling that generates large reports, inventories, or exports may include extensive numeric identifiers and constants.
63- Some legitimate security or management products may produce numeric-heavy PowerShell content as part of automation; validate against known software, expected execution accounts, and change windows.
64
65### Response and remediation
66
67- If malicious behavior is suspected, contain the affected host to prevent further execution and reduce the risk of follow-on activity.
68- Preserve the script content from `powershell.file.script_block_text` (and any reconstructed multi-part content) for deeper analysis and to support incident response and retrospective hunting.
69- If `file.path` is present and the source is not authorized, remove or quarantine the script and investigate related host artifacts and execution mechanisms.
70- Investigate potential account compromise for the associated `user.id` by reviewing recent authentication and endpoint activity; take credential and session remediation actions in line with your procedures.
71- Hunt for related activity using `host.id`, `agent.id`, `user.id`, and distinctive script patterns identified during triage to find additional impacted systems.
72- Apply preventive controls based on findings, such as tightening PowerShell usage for affected accounts, improving script provenance controls, and enhancing monitoring for similar obfuscation patterns.
73"""
74risk_score = 21
75rule_id = "f9abcddc-a05d-4345-a81d-000b79aa5525"
76setup = """## Setup
77
78PowerShell Script Block Logging must be enabled to generate the events used by this rule (e.g., 4104).
79Setup instructions: https://ela.st/powershell-logging-setup
80"""
81severity = "low"
82tags = [
83 "Domain: Endpoint",
84 "OS: Windows",
85 "Use Case: Threat Detection",
86 "Tactic: Defense Evasion",
87 "Data Source: PowerShell Logs",
88 "Resources: Investigation Guide",
89]
90timestamp_override = "event.ingested"
91type = "esql"
92
93query = '''
94from logs-windows.powershell_operational* metadata _id, _version, _index
95| where event.code == "4104"
96
97// Filter out smaller scripts that are unlikely to implement obfuscation using the patterns we are looking for
98| eval Esql.script_block_length = length(powershell.file.script_block_text)
99| where Esql.script_block_length > 1000
100
101// replace the patterns we are looking for with the 🔥 emoji to enable counting them
102// The emoji is used because it's unlikely to appear in scripts and has a consistent character length of 1
103| eval Esql.script_block_tmp = replace(powershell.file.script_block_text, """[0-9]""", "🔥")
104
105// count how many patterns were detected by calculating the number of 🔥 characters inserted
106| eval Esql.script_block_pattern_count = Esql.script_block_length - length(replace(Esql.script_block_tmp, "🔥", ""))
107
108// Calculate the ratio of special characters to total length
109| eval Esql.script_block_ratio = Esql.script_block_pattern_count::double / Esql.script_block_length::double
110
111// keep the fields relevant to the query, although this is not needed as the alert is populated using _id
112| keep
113 Esql.script_block_pattern_count,
114 Esql.script_block_ratio,
115 Esql.script_block_length,
116 Esql.script_block_tmp,
117 powershell.file.*,
118 file.directory,
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 with high numeric character ratio
131| where Esql.script_block_ratio > 0.5
132
133// Exclude Windows Defender Noisy Patterns
134| where not (
135 file.directory == "C:\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\Downloads" or
136 file.directory like (
137 "C:\\\\ProgramData\\\\Microsoft\\\\Windows Defender Advanced Threat Protection\\\\DataCollection*",
138 "C:\\\\Program Files\\\\SentinelOne\\\\Sentinel Agent*"
139 )
140 )
141 // ESQL requires this condition, otherwise it only returns matches where file.directory exists.
142 or file.directory is null
143| where not powershell.file.script_block_text like "*[System.IO.File]::Open('C:\\\\ProgramData\\\\Microsoft\\\\Windows Defender Advanced Threat Protection\\\\DataCollection*"
144| where not powershell.file.script_block_text : "26a24ae4-039d-4ca4-87b4-2f64180311f0"
145'''
146
147
148[[rule.threat]]
149framework = "MITRE ATT&CK"
150[[rule.threat.technique]]
151id = "T1027"
152name = "Obfuscated Files or Information"
153reference = "https://attack.mitre.org/techniques/T1027/"
154
155[[rule.threat.technique]]
156id = "T1140"
157name = "Deobfuscate/Decode Files or Information"
158reference = "https://attack.mitre.org/techniques/T1140/"
159
160
161[rule.threat.tactic]
162id = "TA0005"
163name = "Defense Evasion"
164reference = "https://attack.mitre.org/tactics/TA0005/"
165[[rule.threat]]
166framework = "MITRE ATT&CK"
167[[rule.threat.technique]]
168id = "T1059"
169name = "Command and Scripting Interpreter"
170reference = "https://attack.mitre.org/techniques/T1059/"
171[[rule.threat.technique.subtechnique]]
172id = "T1059.001"
173name = "PowerShell"
174reference = "https://attack.mitre.org/techniques/T1059/001/"
175
176
177
178[rule.threat.tactic]
179id = "TA0002"
180name = "Execution"
181reference = "https://attack.mitre.org/tactics/TA0002/"
182
183[rule.investigation_fields]
184field_names = [
185 "@timestamp",
186 "user.name",
187 "user.id",
188 "user.domain",
189 "powershell.file.script_block_text",
190 "powershell.file.script_block_id",
191 "powershell.sequence",
192 "powershell.total",
193 "file.path",
194 "file.directory",
195 "file.name",
196 "process.pid",
197 "host.name",
198 "host.id",
199 "powershell.file.script_block_length"
200]
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 High Numeric Character Proportion
This rule flags long PowerShell script blocks with unusually digit-dense content. Numeric-heavy script blocks are often used to conceal payloads as byte arrays or character codes that are decoded at runtime. Triage should focus on reconstructing the full script content, determining how it was initiated, and identifying any decoded or executed secondary content.
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
powershell.file.script_block_textto characterize the numeric content:- Look for long comma-separated numbers, repeated digit sequences, or
0x-prefixed values that may represent reconstructed bytes. - Identify string reconstruction patterns (for example, casting numeric values to characters) and any subsequent decoding or decompression logic.
- Note any execution primitives that would run derived content (for example, invoking dynamically built commands or loading content into memory).
- Look for long comma-separated numbers, repeated digit sequences, or
- If the script is fragmented, use
powershell.sequenceandpowershell.totalto collect the related script block events on the samehost.nameanduser.idand reconstruct the complete content in the correct order before drawing conclusions. - Establish execution context and scope using
host.name,host.id,agent.id, anduser.id:- Determine whether the user context is expected to run PowerShell and whether similar script blocks have occurred recently on the same host or by the same user.
- Look for other alerts on the same host or user that could indicate staging, persistence, or lateral movement.
- Assess script origin using
file.pathandfile.directorywhen present:- Determine whether the script is sourced from a location consistent with approved administration or automation workflows.
- If the script is file-backed, check for other security telemetry referencing the same path to identify file creation, modification, or repeated execution patterns.
- Correlate with adjacent telemetry (as available in your environment) using the host and user pivots above:
- Process execution telemetry near the alert time to identify the PowerShell host process and its parent, and to understand how PowerShell was launched.
- Network telemetry for outbound connections or downloads that could support payload retrieval or command and control.
- File activity for dropped payloads or staging artifacts related to the script content or its on-disk source.
False positive analysis
- Legitimate scripts that embed binary content as numeric arrays (for example, packaging resources into scripts or deploying configuration blobs) can appear digit-dense.
- Administrative tooling that generates large reports, inventories, or exports may include extensive numeric identifiers and constants.
- Some legitimate security or management products may produce numeric-heavy PowerShell content as part of automation; validate against known software, expected execution accounts, and change windows.
Response and remediation
- If malicious behavior is suspected, contain the affected host to prevent further execution and reduce the risk of follow-on activity.
- Preserve the script content from
powershell.file.script_block_text(and any reconstructed multi-part content) for deeper analysis and to support incident response and retrospective hunting. - If
file.pathis present and the source is not authorized, remove or quarantine the script and investigate related host artifacts and execution mechanisms. - Investigate potential account compromise for the associated
user.idby reviewing recent authentication and endpoint activity; take credential and session remediation actions in line with your procedures. - Hunt for related activity using
host.id,agent.id,user.id, and distinctive script patterns identified during triage to find additional impacted systems. - Apply preventive controls based on findings, such as tightening PowerShell usage for affected accounts, improving script provenance controls, and enhancing monitoring for similar obfuscation patterns.
Related rules
- Potential PowerShell Obfuscation via Backtick-Escaped Variable Expansion
- Potential PowerShell Obfuscation via Character Array Reconstruction
- Potential PowerShell Obfuscation via Concatenated Dynamic Command Invocation
- Potential PowerShell Obfuscation via Invalid Escape Sequences
- PowerShell Script with Encryption/Decryption Capabilities