Home
BlogContact Us
Back to Blog
Red Team

PSRansom Readiness Assessment: Bypassing MDE on a Hardened Windows 11 Endpoint

Netlore Security Red Team
20 min read

Executive Summary

This post documents a ransomware readiness assessment conducted by the Netlore Security Red Team for a mid-sized financial services client. The engagement objective was simple: determine whether a commodity PowerShell-based ransomware tool could successfully encrypt workstation data in a fully-patched, enterprise-hardened environment — and if so, identify every control gap along the way.

The client's endpoint posture at the time of engagement:

ComponentVersion / Config
OSWindows 11 22H2 (fully patched, September 2025)
EDRMicrosoft Defender for Endpoint (E5 license)
CloudMicrosoft 365 (Entra ID joined, Intune managed)
PowerShellVersion 5.1 and 7.4 both present
Script Block LoggingEnabled via GPO
AMSIActive, cloud-delivered protection ON
Network egressPalo Alto next-gen firewall, SSL inspection enabled

Target tool: PSRansom — an open-source, PowerShell-native ransomware simulator available on GitHub, widely used in readiness exercises.

Bottom line: After iterating through four distinct bypass layers, PSRansom successfully encrypted 847 files (4.2 GB) across the test workstation. MDE generated 11 alerts across the engagement, but only 3 fired before encryption completed. The remaining 8 fired post-execution — too late for automated remediation.


Engagement Scope & Rules of Engagement

The client provided a single, domain-joined Windows 11 workstation that mirrored the standard employee build. No credentials were pre-staged. The engagement simulated a scenario in which an attacker had already achieved code execution via a phishing payload — our starting point was an interactive PowerShell session running as the domain user.

What was explicitly out of scope:

  • Lateral movement beyond the single workstation
  • Exfiltration of actual business data
  • Any persistence mechanism surviving reboot
  • Production systems of any kind

All activity was performed in an isolated VLAN with full packet capture for post-engagement review.


Phase 1 — Endpoint Reconnaissance

Before touching any payload, we spent time understanding the defensive surface. Standard enumeration:

Get-MpPreferenceGet-MpComputerStatusGet-ExecutionPolicy -ListGet-AppLockerPolicyGet-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\

Key findings from this phase:

  • Constrained Language Mode (CLM): Active via Intune policy. This is the most impactful single control on the endpoint — it restricts which .NET types PowerShell can directly instantiate, severely limiting reflection-based attacks.
  • AMSI: Active with cloud lookups enabled. MpCmdRun.exe -SignatureUpdate confirmed definitions were current to the day.
  • Script Block Logging: Enabled. Every script block executed in PowerShell 5.1 gets logged to Windows Event Log (Event ID 4104) and forwarded to MDE's telemetry pipeline.
  • Attack Surface Reduction (ASR) rules: 12 of 16 recommended rules in Enforce mode. Notable exceptions: the rule blocking Office macros from spawning child processes was in Audit mode, and the rule blocking credential stealing from LSASS was also in Audit mode.
  • Tamper Protection: Enabled. This prevents direct modification of Defender settings via standard registry keys or PowerShell cmdlets.

With CLM and AMSI both active, PSRansom cannot be run directly. The tool relies on [System.Reflection.Assembly]::Load() and other .NET calls that CLM blocks. The AMSI layer would terminate the session before those calls even ran.


Phase 2 — The AMSI Problem

AMSI (Antimalware Scan Interface) is a Windows API that allows script engines — PowerShell, VBScript, JScript, WSH — to pass content to the registered security provider (in this case, MDE) before execution. The critical point: AMSI scans content, not just file hashes. Even if a script is obfuscated or loaded from memory, AMSI sees the deobfuscated content at the moment of execution.

The scanning pipeline for PowerShell 5.1 looks roughly like this:

User types command
    ↓
PowerShell tokenizes & builds AST
    ↓
AmsiScanBuffer() called with script content
    ↓
[MDE cloud lookup + local signatures]
    ↓
Detected? → AMSI_RESULT_DETECTED → session terminated
Clean? → execution proceeds

AMSI operates in two modes simultaneously: signature-based (matching known bad strings and byte patterns) and behavioral (heuristic scoring based on the structure and intent of the script). PSRansom triggers both: the file name PSRansom.ps1, function names like Invoke-PSRansom, and string literals like AES-256 encryption all match signatures. Even with those renamed, the behavioral model flags the file enumeration + encryption loop pattern with high confidence.

Simply renaming variables is not enough.


Phase 3 — AMSI Bypass

Two bypass techniques were evaluated. Both exploit the fact that amsi.dll is loaded into the PowerShell process address space, and its scanning function must reside in writable memory to be patchable.

Technique A: AmsiScanBuffer Memory Patch

AmsiScanBuffer is the primary AMSI function. It accepts a buffer of content, scans it, and returns a result code. If we can overwrite the first few bytes of this function with a ret instruction that returns AMSI_RESULT_CLEAN (value 1), all subsequent calls from that process will return clean.

The technique:

  1. Resolve amsi.dll base address in the current process
  2. Walk the export table to find AmsiScanBuffer's offset
  3. Change memory page permissions to PAGE_EXECUTE_READWRITE via VirtualProtect
  4. Write patch bytes at the function entry point
  5. Restore original permissions

Operational implementation (critical bytes masked):

$amsiDll = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
    (****),  # resolved function pointer — masked
    (****)
)

$patchBytes = [Byte[]] (****) # patch payload — masked

$****  # VirtualProtect call — masked
[System.Runtime.InteropServices.Marshal]::Copy($patchBytes, 0, $funcPtr, $patchBytes.Length)
$****  # permission restore — masked

This patch survives for the lifetime of the process. A new PowerShell session would re-load amsi.dll clean.

MDE response: No alert. The memory write itself is not flagged. MDE only monitors AMSI output, not the act of patching — a well-known visibility gap.

Technique B: Reflection via amsiInitFailed

An alternative approach sets the private amsiInitFailed field of the System.Management.Automation.AmsiUtils class to true via reflection. This causes PowerShell to believe AMSI initialization failed and skips all subsequent scans.

$amsiUtils = [Ref].Assembly.GetType('System.Management.Automation.****')  # class name masked
$field = $amsiUtils.GetField('****', [Reflection.BindingFlags]'NonPublic,Static')  # field name masked
$field.SetValue($null, $true)

This is cleaner and requires no memory manipulation, but Constrained Language Mode blocks it entirely[Ref].Assembly.GetType() with non-public binding flags is a restricted operation under CLM.

Result: Technique A was selected. CLM does not block the P/Invoke-style pointer operations used by Technique A when executed through a pre-compiled helper that was already loaded.


Phase 4 — ETW Bypass

Even with AMSI patched, PowerShell Script Block Logging remained active. Every deobfuscated script block executed in the session is written to ETW (Event Tracing for Windows) and forwarded to MDE. This would expose the attack chain in near-real-time to the SOC.

ETW for PowerShell flows through EtwEventWrite in ntdll.dll. The approach mirrors the AMSI patch:

  1. Resolve EtwEventWrite in the current process
  2. Overwrite the first bytes with a ret 14h instruction, causing the function to return immediately without writing any event

Operational patch (masked):

$etwFunc = (****) # ntdll!EtwEventWrite pointer resolution — masked
$patch   = [Byte[]](****) # ret instruction bytes — masked
$****    # VirtualProtect + Copy + restore — masked

Effect: From this point forward, no PowerShell script block events (Event ID 4104) are written. MDE's PowerShell telemetry goes dark for this session.

MDE response: Still no alert. The ETW patch is not directly observable by Defender — it operates below the layer Defender monitors.

Defender Visibility Note: MDE can detect the absence of expected telemetry from a previously active session as an anomaly, but this requires a tuned custom detection rule that the client had not implemented. This gap was documented as Finding #2 in the engagement report.


Phase 5 — PowerShell Obfuscation Chain

With AMSI and ETW both neutralized, PSRansom still could not be loaded directly — its plaintext source would trigger Defender's real-time file scan if written to disk. Loading from memory required the content to pass through AMSI at the moment of invocation (before our patch was applied), so we needed to deliver the bypass and payload as a single obfuscated unit.

The obfuscation chain used three layers:

Layer 1 — String Reversal + Char Array Substitution

Function names and string literals are reversed and reconstructed at runtime:

$r = 'rebmaNnoitcnuF'[-1..-14] -join ''
# Reconstructs: 'FunctionNombre' — masked actual target

Layer 2 — Base64 Encoding

The reassembled script is Base64-encoded and decoded at runtime:

$b = '****'  # base64 blob — masked
$d = [System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($b))

Layer 3 — IEX via Variable Indirection

Direct use of Invoke-Expression or its alias iex triggers AMSI signatures even in the string. Indirection avoids this:

$exec = (Get-Item 'Variable:****').Value  # retrieves IEX reference — masked
& $exec $d

The final obfuscated loader — approximately 4KB — passes AMSI clean (post-patch) and delivers the PSRansom payload into memory without touching disk.


Phase 6 — The Firewall / C2 Key Exchange Problem

This was the most technically interesting phase of the engagement. PSRansom's default design generates a random AES-256 key locally, encrypts files, and transmits the key in cleartext to a C2 server over HTTP for later recovery (simulating "pay the ransom, get the key").

The problem: The client's Palo Alto firewall performs full SSL inspection on outbound traffic. Any HTTP/HTTPS connection to an unknown external IP would be:

  1. Intercepted and decrypted
  2. Inspected by the URL filtering engine
  3. Blocked if the destination wasn't in an approved category

Our test C2 server's IP had no URL category and was immediately blocked. The AES key never reached us. Without the key, the simulation would produce permanently unrecoverable files — an unacceptable outcome in an authorized engagement.

The solution: RSA-2048 asymmetric key wrapping — no C2 required.

Instead of transmitting the AES key, we:

  1. Embedded our RSA-2048 public key in the payload
  2. Modified PSRansom to encrypt the AES session key with the RSA public key
  3. Wrote the RSA-encrypted key blob to a local file (RECOVERY_KEY.bin) on the workstation
  4. Only our RSA private key (held offline) could decrypt this blob to recover the AES key
# Key wrapping — conceptual (implementation masked)
$aesKey      = (****) # random 256-bit key
$rsaPubKey   = (****) # embedded RSA-2048 public key — masked
$wrappedKey  = $rsaPubKey.Encrypt($aesKey, [System.Security.Cryptography.RSAEncryptionPadding]::****)
[IO.File]::WriteAllBytes('C:\Users\Public\RECOVERY_KEY.bin', $wrappedKey)

This accurately models modern ransomware behavior (e.g., LockBit 3.0, BlackCat/ALPHV) where the master key never traverses the network in plaintext. The firewall was completely bypassed — not by tunneling, but by eliminating the network dependency entirely.


Phase 7 — PSRansom Customization & Execution

PSRansom was forked and modified in the following ways before use:

ModificationPurpose
Variable renaming (all variables)Defeat signature-based detection
Function aliasingAvoid function name matching
Dead code injection (~200 lines)Inflate entropy, confuse static analysis
Removed C2 transmissionReplaced with RSA key wrapping (Phase 6)
Encrypted string constantsPrevent static string matching by Defender
Randomized file extension.[random 5 chars] instead of default .psransom

Execution was initiated from the obfuscated loader chain (Phase 5). PSRansom targeted:

  • C:\Users\[victim]\Documents
  • C:\Users\[victim]\Desktop
  • C:\Users\[victim]\Downloads
  • Mapped network drives (none were present on the test workstation)

MDE Alerts generated during execution:

AlertFired WhenSeverity
Suspicious PowerShell script execution4 seconds after IEXMedium
Potential ransomware behavior detected47 seconds into encryptionHigh
Mass file modification activity2 minutes 11 secondsHigh
Script block logging gap detectedPost-execution (manual review)Medium
Defender tamper attempt (process memory)Post-execution (forensic)High
... 6 additional post-execution alertsAfter encryption completedVarious

Encryption completed in 3 minutes 42 seconds. The first High-severity alert (ransomware behavior) fired at 47 seconds — 2 minutes 55 seconds before encryption finished. However, automated isolation (the client's configured response action) requires two High alerts before triggering. The second High alert (mass file modification) fired at 2:11, triggering automated isolation at approximately 2:20.

Net result: 22 seconds of encryption proceeded after the isolation trigger. Approximately 180 files were encrypted in that window.


Phase 8 — Simulation Results

MetricValue
Total files encrypted847
Total data encrypted4.2 GB
Encryption duration3 min 42 sec
Time to first MDE alert4 seconds (Medium)
Time to first High alert47 seconds
Time to automated isolation~2 min 20 sec
Files encrypted after isolation trigger~180
Total MDE alerts generated11
Alerts fired before encryption completed3
Alerts fired after encryption completed8
AMSI detections0
ETW/Script Block Log entries captured0

MITRE ATT&CK Mapping

TacticTechniqueSub-TechniquePhase
Defense EvasionT1562T1562.001 — Disable or Modify ToolsPhase 3 (AMSI)
Defense EvasionT1562T1562.006 — Indicator BlockingPhase 4 (ETW)
Defense EvasionT1027T1027.010 — Command ObfuscationPhase 5
Defense EvasionT1140— Deobfuscate/Decode FilesPhase 5
ExecutionT1059T1059.001 — PowerShellPhase 5, 7
Defense EvasionT1620— Reflective Code LoadingPhase 3
ImpactT1486— Data Encrypted for ImpactPhase 7
Command & ControlT1573T1573.002 — Asymmetric CryptographyPhase 6
CollectionT1005— Data from Local SystemPhase 7

Critical Findings & Risk Assessment

Finding #1 — AMSI Bypass via In-Process Memory Patching

Risk: Critical | CVSS 3.1: 8.8 (AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)

Microsoft Defender for Endpoint does not alert on the act of patching amsi.dll within a running process. Once patched, the AMSI scan layer is entirely neutralized for the lifetime of that process. Tamper Protection only prevents Defender's own settings from being changed — it does not protect the in-memory AMSI DLL.

Fix: Enable MDE's kernel-mode process injection detection. Consider deploying Credential Guard equivalent protections for AMSI DLL integrity.

Finding #2 — ETW Blind Spot Creates SOC Telemetry Gap

Risk: High | CVSS 3.1: 7.5 (AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:H)

Patching EtwEventWrite in a PowerShell process eliminates all Script Block Logging for that session. MDE's PowerShell visibility relies heavily on ETW. The client had no custom detection rule for "expected telemetry absence" from an active session.

Fix: Implement a custom MDE detection rule alerting when a known-active PowerShell process stops generating 4104 events. Enforce audit log integrity monitoring.

Finding #3 — Automated Isolation Response Window

Risk: High | CVSS 3.1: 7.3

The client's automated isolation policy required two High-severity alerts before isolating the endpoint. The gap between the first and second High alerts (~84 seconds) allowed encryption to continue significantly.

Fix: Configure automated isolation to trigger on a single High-severity ransomware alert. Reduce the threshold or create a dedicated rule for mass file modification events.

Finding #4 — ASR Rules Not Fully Enforced

Risk: Medium | CVSS 3.1: 6.1

Two critical ASR rules — Office macro child process spawning and LSASS credential access — were in Audit mode, not Enforce mode. While not directly exploited in this engagement, they represent significant risk for initial access vectors.

Fix: Move all recommended ASR rules to Enforce mode. Conduct regression testing on legitimate business applications to identify any conflicts before enforcement.


Remediation Roadmap

Immediate (0–30 days)

  • Enable automated endpoint isolation on first High-severity ransomware alert
  • Move remaining ASR rules from Audit to Enforce mode
  • Deploy custom MDE detection rule for ETW telemetry gaps
  • Review and restrict PowerShell Constrained Language Mode exceptions

Short-Term (1–3 months)

  • Evaluate MDE Kernel-mode protection settings for in-process DLL integrity
  • Implement application allowlisting for PowerShell scripts via AppLocker or WDAC
  • Deploy canary files (honeypot documents) in user directories to trigger early ransomware detection
  • Conduct tabletop exercise with SOC team using this engagement as the scenario

Medium-Term (3–6 months)

  • Implement immutable backup solution (3-2-1 rule with offline/airgapped copy)
  • Deploy network-level deception technology (honeypots) on internal segments
  • Evaluate privileged access workstation (PAW) architecture for administrative users
  • Commission a follow-up assessment after controls are hardened

Conclusion

This engagement demonstrates that commodity ransomware tooling, augmented with well-documented bypass techniques, can successfully operate on a modern enterprise Windows 11 endpoint even with MDE E5 deployed. The controls work — but they require correct configuration, appropriate response thresholds, and compensating controls where gaps exist.

The most impactful finding is not technical: it is operational. MDE generated a High-severity ransomware alert 47 seconds into execution. If that alert had triggered automated isolation immediately, encryption would have been limited to a fraction of the final scope. Configuration, not capability, was the decisive factor.

Key takeaways:

  • AMSI bypass via memory patching remains undetected by MDE — this is a known gap Microsoft has not closed at the EDR layer
  • ETW patching eliminates PowerShell telemetry — SOC visibility depends on controls the attacker can turn off
  • Automated response thresholds matter enormously — a single configuration change (isolate on first High alert) would have contained this simulation to under 100 files
  • Firewall SSL inspection is not sufficient — modern ransomware eliminates C2 dependency entirely through asymmetric key wrapping

Full technical appendix including IOCs, raw MDE alert timeline, and registry/artifact artifacts was delivered to the client under NDA.

Tags:PSRansomRansomware ReadinessAMSI BypassETW BypassPowerShell EvasionMicrosoft Defender for EndpointWindows 11Red TeamMITRE ATT&CKO365

Need Cybersecurity Consulting?

Our expert team provides comprehensive cybersecurity services to secure your corporate infrastructure. Contact us for detailed information about penetration testing, security audits, and consulting services.

Contact Us

Cookie Usage

We use cookies to improve your experience on our website. By continuing, you accept the use of cookies.

Cookie Policy