Loading

Potential PowerShell Obfuscation via Invalid Escape Sequences

Detects PowerShell scripts with repeated invalid backtick escapes between word characters (letters, digits, underscore, or dash), splitting tokens while preserving execution. Attackers use this obfuscation to fragment keywords and evade pattern-based detection 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: 9
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 flags PowerShell script block content that repeatedly inserts invalid backtick escape sequences within otherwise contiguous word characters. This can fragment tokens (cmdlets, parameters, variable names, strings) while preserving execution and readability to the interpreter, which can hinder content inspection and pattern-based detections.

Analyst goals:

  • Reconstruct complete script block content when split across multiple events.
  • Normalize the content (remove or correct invalid escapes) to reveal the underlying logic.
  • Determine execution context (host, user, script origin) and correlate with adjacent activity to assess intent and impact.
  • 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 timeline and ownership:

    • Anchor the activity using @timestamp, then record host.name, host.id, and agent.id.
    • Identify the execution context with user.name, user.domain, and user.id. Note whether the account is expected to run PowerShell on this host and whether the host is commonly used for scripting.
  • Assess the likelihood of intentional obfuscation:

    • Review Esql.script_block_pattern_count to understand how heavily the content is fragmented. Higher counts generally increase confidence that this is deliberate obfuscation rather than incidental escaping.
    • Use powershell.file.script_block_length as size context and review powershell.file.script_block_entropy_bits, powershell.file.script_block_unique_symbols, and powershell.file.script_block_surprisal_stdev to characterize the content (simple text vs. mixed/randomized payloads).
    • Compare Esql.script_block_tmp to powershell.file.script_block_text to understand where obfuscation is concentrated (localized string vs. widespread token fragmentation).
  • Reconstruct complete content when split across events:

    • If powershell.total is greater than 1, pivot on powershell.file.script_block_id and rebuild the script by ordering segments on powershell.sequence.
    • Validate the reconstructed set is complete (sequence 1 through powershell.total). Missing segments should be treated as an investigative gap and may require additional scoping.
  • Determine script origin and delivery:

    • If file.path, file.directory, and file.name are present, treat the script block as file-associated. Evaluate whether the location and naming are consistent with approved scripts or expected tooling for the endpoint.
    • If file fields are absent, treat the script as inline or dynamically generated content and prioritize correlation by host.id, user.id, and time.
  • Normalize and interpret the script content safely:

    • In a controlled analysis workflow, normalize the script by removing or correcting invalid backtick insertions so that split tokens become readable. Keep both the original and normalized versions for reporting.
    • Review the normalized text for behaviors that indicate malicious intent (secondary payload retrieval, dynamic execution, decoding/decompression, data collection, persistence logic, or remote interaction).
    • Extract and document indicators present in the content (network destinations, file paths/names, unique strings, or embedded encoded blobs) for scoping.
  • Correlate within PowerShell telemetry:

    • Pivot on host.id and user.id to identify additional powershell.file.script_block_text events shortly before and after the alert time to capture staging, follow-on commands, and potential cleanup.
    • Check for the same powershell.file.script_block_id appearing across hosts, or for repeated normalized strings, to identify automation reuse or broader activity.
  • Correlate with adjacent endpoint activity (if available in your environment):

    • Review process execution around @timestamp on host.name to identify the PowerShell host process and its parent, then assess whether the launch chain aligns with expected activity for the user and endpoint.
    • Review network activity around the alert time for connections that align with indicators extracted from the script content.
    • Review file and registry activity around the same time window for artifacts consistent with the script (new or modified scripts, dropped files, or persistence-related changes).
    • Review authentication activity associated with user.id around the alert time for suspicious logons or remote access that may align with script execution.
  • Scope impact:

    • Search for other alerts/events with similar obfuscation characteristics on the same host and for the same user to determine whether this is a one-off execution or a repeated pattern.
    • If multiple hosts are involved, prioritize investigation for critical assets and accounts with elevated privileges.
  • Legitimate scripts can contain backticks for formatting, string construction, or content generation; however, repeated invalid escape sequences embedded inside alphanumeric tokens are uncommon. Validate whether the execution context (host.id, user.id) aligns with known administrative or developer activity.
  • Some commercial or internal tools intentionally obfuscate PowerShell to protect intellectual property. Confirm whether the script origin (file.path when present), account context, and prevalence across the environment match an approved application or workflow.
  • Copy/paste artifacts and encoding transformations can introduce unexpected characters. When suspected, compare the normalized content to known-good scripts and assess whether the obfuscation is systematic (repeating across many tokens) versus localized.
  • If the activity is confirmed or strongly suspected malicious:

    • Contain affected host(s) to prevent further execution and lateral movement.
    • Preserve evidence: reconstructed powershell.file.script_block_text, powershell.file.script_block_id, powershell.sequence, powershell.total, and alert-derived fields (Esql.script_block_tmp, Esql.script_block_pattern_count, and the script block metrics).
    • If file.path is present, collect and quarantine the referenced script and review the surrounding directory for related artifacts.
    • Use indicators extracted from normalized content to scope related activity across endpoints (pivot on host.id, user.id, file.path, and unique strings from the script).
    • Coordinate credential remediation for affected accounts when remote execution, credential material, or post-exploitation behavior is suspected.
  • If the activity is benign but requires reduction:

    • Document the legitimate source (expected hosts/users and file.path when applicable).
    • Apply narrowly scoped tuning using stable attributes available in the alert (such as host.id, user.id, and file.path) and continue monitoring for deviations in script content and execution context.
from logs-windows.powershell_operational* metadata _id, _version, _index
| where event.code == "4104" and powershell.file.script_block_text like "*`*"

// 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_-]`(?![rntb]|\r|\n|\d)[A-Za-z0-9_-]""", "🔥")

// 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_tmp,
    powershell.file.*,
    file.name,
    file.directory,
    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 20 times
| where Esql.script_block_pattern_count >= 20

| where file.name not like "TSS_*.psm1"
  // ESQL requires this condition, otherwise it only returns matches where file.name exists.
  or file.name is null

// VSCode Shell integration
| where not powershell.file.script_block_text like "*$([char]0x1b)]633*"

| where not file.directory == "C:\\Program Files\\MVPSI\\JAMS\\Agent\\Temp"
  // ESQL requires this condition, otherwise it only returns matches where file.directory exists.
  or file.directory is null
		

Framework: MITRE ATT&CK

Framework: MITRE ATT&CK