Loading

Potential PowerShell Obfuscation via String Concatenation

Detects PowerShell scripts that repeatedly concatenates multiple quoted string literals with + to assemble commands or tokens at runtime. Attackers use string concatenation to fragment keywords or URLs and evade static analysis and AMSI.

Rule type: esql
Rule indices:

Rule Severity: high
Risk Score: 73
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 rule identifies PowerShell script block content that uses repeated concatenation of quoted string literals. This technique is commonly used to fragment keywords, URLs, and command text so they are assembled only at runtime. Focus the investigation on reconstructing the final strings, understanding how they are used within the script, and scoping related activity from the same host and user.

  • 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.
  • Establish alert context and prioritize review:

    • Review host.name / host.id and user.name / user.domain / user.id to understand where and under which account the script executed.
    • Use Esql.script_block_pattern_count to gauge how heavily the script relies on concatenation. Higher counts can indicate more deliberate obfuscation.
    • Review powershell.file.script_block_length and the statistical fields (powershell.file.script_block_entropy_bits, powershell.file.script_block_surprisal_stdev, powershell.file.script_block_unique_symbols) to assess whether the script contains high-variance or high-randomness content that may indicate encoding, packing, or layered obfuscation.
  • Identify the most likely script origin:

    • Review file.path, file.directory, and file.name (when present) to determine whether the script block content was sourced from an on-disk script. Note whether the location is expected for administrative tooling or is user-writable/unusual for the host role.
    • If file origin fields are not populated, treat the activity as potentially inline/interactive execution and prioritize identifying what initiated it through correlation with surrounding telemetry.
  • Recover the assembled strings and intent:

    • Start with Esql.script_block_tmp to find where concatenation patterns occur, then inspect the surrounding text in powershell.file.script_block_text for the full logic.
    • Reconstruct concatenated literals by joining the quoted fragments (preserving ordering and separators) to reveal the final keywords, paths, URLs, arguments, or identifiers the script is attempting to hide.
    • Look for how reconstructed strings are used, especially:
      • Dynamic invocation of reconstructed commands or expressions.
      • Download or retrieval of remote content.
      • Decoding/decryption routines and second-stage content handling.
      • Construction of file paths, scheduled execution artifacts, or configuration changes.
    • Capture any reconstructed indicators (domains, URLs, file names, directories, command fragments) that can be used to scope other activity.
  • Reconstruct full script content when split across events:

    • Pivot on powershell.file.script_block_id to collect related script block events from the same execution context.
    • If powershell.total is greater than 1, gather all fragments and order them by powershell.sequence to rebuild the complete script content before making a final determination.
    • Review whether additional script blocks were executed in close succession by the same user.id on the same host.id, which may indicate staging or multi-step execution.
  • Scope the activity across hosts and accounts:

    • On the same host.id, look for other script blocks that share the same file.path or distinctive reconstructed strings to determine if this is a single run or repeated behavior.
    • Pivot on user.id to identify the same technique on other endpoints, which can indicate credential misuse or automated distribution.
  • Correlate with adjacent telemetry (as available in your environment):

    • Process execution: identify the PowerShell host process and its parent process near the alert time to determine whether execution was interactive or spawned by another program.
    • Network activity: if reconstructed strings include URLs/domains, check for outbound connections, downloads, or callbacks that align with the alert timeframe.
    • Host changes: review nearby file and registry activity for payload drops, persistence creation, or configuration changes that match reconstructed paths or names.
    • Authentication: review recent logon activity for user.id and host.id for anomalies that may explain the execution (new logon source, unusual logon type, or unexpected access patterns).
  • Legitimate administrative or operational scripts may concatenate many string literals to generate dynamic content (for example, configuration files, templated output, or complex argument strings). Validate whether the reconstructed strings align with expected internal tooling and whether the execution context (host.id, user.id, and file.path) matches normal operations.
  • If malicious or suspicious activity is confirmed:

    • Contain the affected host to prevent follow-on execution or lateral movement.
    • Preserve evidence from the alert, including powershell.file.script_block_text, powershell.file.script_block_id, powershell.sequence / powershell.total, and any file.path details that indicate script origin.
    • Use reconstructed strings to scope across the environment for related script blocks and any referenced artifacts (additional scripts, payload files, or remote destinations).
    • Identify and remediate the initial execution vector by tracing the execution chain (user context and initiating process) and removing any persistence mechanisms discovered during investigation.
    • If account compromise is suspected, reset credentials for the affected user.id, review recent access patterns, and apply least-privilege controls to limit further abuse.
  • If the activity is determined to be benign:

    • Document the script purpose, expected execution context, and the specific concatenated strings that explain the detection.
    • Monitor for deviations from the established baseline (new hosts, new accounts, unexpected file paths, or substantially different reconstructed content).
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,
    """['"][A-Za-z0-9.]+['"](\s?\+\s?['"][A-Za-z0-9.,\-\s]+['"]){2,}""",
    "🔥"
)

// 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 twice
| where Esql.script_block_pattern_count >= 2
		

Framework: MITRE ATT&CK

Framework: MITRE ATT&CK