Loading

Potential Dynamic IEX Reconstruction via Environment Variables

Detects PowerShell scripts that reconstructs IEX (Invoke-Expression) by indexing environment variable strings (for example, $env:VAR[1,2,3]) or related .name[...] slices and joining characters at runtime. Attackers use environment-variable slicing to hide dynamic execution and evade keyword-based detections and AMSI.

Rule type: esql
Rule indices:

Rule Severity: medium
Risk Score: 47
Runs every:
Searches indices from: now-9m
Maximum alerts per execution: 100
References:

Tags:

  • Domain: Endpoint
  • OS: Windows
  • Use Case: Threat Detection
  • Tactic: Defense Evasion
  • Data Source: PowerShell Logs
  • Resources: Investigation Guide

Version: 8
Rule authors:

  • Elastic

Rule license: Elastic License v2

PowerShell Script Block Logging must be enabled to generate the events used by this rule (e.g., 4104). Setup instructions: https://ela.st/powershell-logging-setup

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.

This alert indicates PowerShell Script Block Logging captured a script that builds "IEX" (Invoke-Expression) at runtime by indexing characters from environment variable strings or related name properties and combining them. This technique is commonly used to obscure dynamic execution and can indicate an attempt to execute attacker-controlled content.

  • 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.
  • Confirm scope and execution context:

    • Review host.name and host.id to identify the impacted endpoint and determine whether it is a typical user workstation, server, or a special-purpose system in your environment.
    • Review user.name, user.domain, and user.id to understand who executed the script and whether the account is expected to run PowerShell on this host (interactive user, service account, or administrative context).
    • Use agent.id (if available) to identify the reporting agent and to support correlation with other telemetry collected from the same endpoint.
    • Use the alert timestamp as the anchor to correlate activity immediately before and after the script block ran.
  • Analyze the obfuscation and intended execution:

    • Examine powershell.file.script_block_text to locate environment-variable slicing patterns (for example, $env:<var>[<idx>], $env:<var>[<idx1>,<idx2>,<idx3>], or .name[<idx1>,<idx2>,<idx3>]) and identify the variable names and indices being used.
    • Use Esql.script_block_tmp to quickly find the match locations, then review the surrounding context in powershell.file.script_block_text to determine how the reconstructed string is used (assignment, concatenation/join, or immediate invocation).
    • Determine whether the reconstructed output is used as a dynamic execution primitive (for example, passed to Invoke-Expression / IEX, used with the call operator, or invoked via a method). Focus on what content is ultimately evaluated or executed.
  • Reconstruct full script content:

    • If the script appears incomplete or staged across multiple events, use powershell.file.script_block_id with powershell.sequence and powershell.total to collect all fragments and rebuild the full script in order.
    • After reconstruction, identify where string construction occurs versus where execution occurs to understand the end-to-end flow.
  • Assess obfuscation level and intent using available enrichments:

    • Review Esql.script_block_pattern_count to understand how frequently the reconstruction pattern appears within the script block; repeated occurrences can indicate systematic obfuscation rather than an isolated string operation.
    • Review powershell.file.script_block_length for size context and compare it with typical script sizes seen for the same host or user.
    • Review powershell.file.script_block_entropy_bits, powershell.file.script_block_surprisal_stdev, and powershell.file.script_block_unique_symbols to gauge whether the script contains encoded or highly obfuscated content (for example, large high-entropy blocks that may indicate packed or encoded data).
  • Identify script origin and potential spread:

    • If file.path is populated, review file.name and file.directory to determine where the script was sourced from and whether the location aligns with approved administrative tooling or software distribution paths.
    • If file.path is not populated, treat the activity as potentially inline or dynamically generated and prioritize identifying the initiating process or source using adjacent telemetry.
    • Scope for other alerts or script blocks on the same host.id or associated with the same user.id that show similar reconstruction patterns, especially within the same time window.
  • Correlate with adjacent telemetry (as available in your environment):

    • Using host.id / host.name, user.id, and the alert time, correlate with process execution data to identify the PowerShell host process and the initiating parent process or source (for example, interactive session, script runner, scheduled task, service, or another application).
    • Correlate with network, file, registry, and authentication telemetry on the same host around the alert time to identify follow-on activity that supports malicious intent (download or retrieval of content, creation or modification of files, persistence-related changes, or suspicious logons).
  • Legitimate automation or administration scripts may construct command strings dynamically, including deriving short tokens from environment variables for compatibility or to reduce hard-coded strings.
  • Security testing and purple-team or red-team activity may intentionally use environment-variable slicing to emulate evasive tradecraft.
  • Developer tooling, obfuscation research, or PowerShell training content may include this technique. Benign usage is typically tied to known owners, consistent hosts, predictable execution windows, and the absence of suspicious downstream activity.
  • If the activity is suspected or confirmed malicious:

    • Contain the affected host to prevent additional execution and reduce lateral movement risk.
    • Preserve evidence by collecting the complete script content using powershell.file.script_block_id, powershell.sequence, and powershell.total, and retain the original powershell.file.script_block_text for analysis.
    • Extract and track indicators from the script content (for example, URLs, domains, IP addresses, file names, or unique strings) and scope for additional occurrences across the environment using host.id, host.name, user.id, and file.path when present.
    • Identify and remediate the initial execution source (parent process or launching mechanism) and remove or quarantine any associated script files referenced by file.path.
    • If account compromise is suspected, reset affected credentials and review for additional suspicious PowerShell activity associated with the same user.id.
  • If the activity is determined to be benign:

    • Document the business justification, owning team, expected hosts, and expected script location (file.path when present).
    • Monitor for deviations in execution context (new hosts, new users, or materially different script content) and consider targeted tuning based on stable attributes such as file.path and known user.id values.
from logs-windows.powershell_operational* metadata _id, _version, _index
| where event.code == "4104"

// Filter out smaller scripts that are unlikely to implement obfuscation using the patterns we are looking for
| eval Esql.script_block_length = length(powershell.file.script_block_text)
| where Esql.script_block_length > 500

// replace the patterns we are looking for with the 🔥 emoji to enable counting them
// The emoji is used because it's unlikely to appear in scripts and has a consistent character length of 1
| eval Esql.script_block_tmp = replace(
    powershell.file.script_block_text,
    """(?i)(\$(?:\w+|\w+\:\w+)\[\d++\]\+\$(?:\w+|\w+\:\w+)\[\d++\]\+['"]x['"]|\$(?:\w+\:\w+)\[\d++,\d++,\d++\]|\.name\[\d++,\d++,\d++\])""",
    "🔥"
)

// count how many patterns were detected by calculating the number of 🔥 characters inserted
| eval Esql.script_block_pattern_count = length(Esql.script_block_tmp) - length(replace(Esql.script_block_tmp, "🔥", ""))

// keep the fields relevant to the query, although this is not needed as the alert is populated using _id
| keep
    Esql.script_block_pattern_count,
    Esql.script_block_length,
    Esql.script_block_tmp,
    powershell.file.*,
    file.path,
    powershell.sequence,
    powershell.total,
    _id,
    _version,
    _index,
    host.name,
    host.id,
    agent.id,
    user.id

// Filter for scripts that match the pattern at least once
| where Esql.script_block_pattern_count >= 1
		

Framework: MITRE ATT&CK

Framework: MITRE ATT&CK