Skip to content

LSASS Dump Bypassing Defender: WerFaultSecure PPL Bypass (2026)

9 methods tested against Defender with PPL active. Why the original WSASS loader fails and the exact bit that fixes it. ADscan's custom build, Defender sig VirTool:Win32/LsassDump.B, and catch intelligence.

Yeray Martín
Yeray Martín · 19 min read

There are nine modern techniques for dumping lsass.exe on a Windows machine with RunAsPPL=1 and Defender active. I tested all nine in lab. Eight failed for one of three reasons: Microsoft patched the primitive, Defender has a signature on the pattern, or PPL blocks the process handle before the technique can execute. The ninth — wsass, abuse of WerFaultSecure.exe — looked like the solution. I compiled the original from GitHub. I tested it. Error 5. ACCESS DENIED. The original was also caught by Defender.

The reason was a single bit in one argument of the loader. This post documents the full matrix of all nine methods, the specific failure codes of the ones that fall, why the original wsass also failed against Defender, the exact signature that blocked it (VirTool:Win32/LsassDump.B), the one-bit modification that fixes it, and how ADscan orchestrates the full process with AV fingerprinting and persistent catch intelligence.

MITRE ATT&CK: T1003.001 — OS Credential Dumping: LSASS Memory. Highest-priority post-exploitation technique because lsass.exe holds NT hashes, Kerberos tickets, DPAPI master keys, and interactive logon session keys in memory. A successful dump is typically the pivot to Domain Admin.

TL;DR for pentesters in a hurry:

  • The problem: PPL (RunAsPPL=1) blocks most methods; Defender signatures block the rest.
  • Why the original WSASS fails: the loader passes /type 268310 (0x41816) which has bit 0x40000 set — Defender blocks it via sig VirTool:Win32/LsassDump.B (sigs 1.449.367.0+) before WerFaultSecure even starts.
  • The fix: change to /type 0x2 (MiniDumpWithFullMemory). No 0x40000 bit, no signature hit, no block.
  • ADscan: orchestrates AV fingerprinting + method selection + automatic dump + persistent catch intelligence across engagements.

Why is modern LSASS dump so hard?

LSASS dump in 2014 was a single call to MiniDumpWriteDump on the PID of lsass.exe. It worked against any machine, any Windows version, any AV of the era. In 2026 that same call fails for three cumulative reasons, and a modern attacker has to defeat all three simultaneously.

The first is PPL (ProtectedProcessLight), a kernel primitive introduced in Windows 8.1 that marks processes as protected in a hierarchy. When a process runs with RunAsPPL=1, another process can only open a handle with PROCESS_VM_READ or PROCESS_QUERY_INFORMATION if it's running at an equal or higher PPL level. An unprotected process — even SYSTEM with all elevated privileges — cannot open lsass.exe when PPL is active. Techniques like comsvcs, procdump, nanodump by handle duplication, or direct MiniDumpWriteDump all fail on the first OpenProcess call with STATUS_ACCESS_DENIED (0xc0000022). The Microsoft baseline GPO for Windows 11 and Server 2022 enables it by default, and most Defender for Endpoint deployments with baseline applied enforce it.

The second is AV/EDR signature-based detection on known access patterns. Defender, MDE, CrowdStrike, and SentinelOne don't need to analyze the resulting dump to block the operation: they hook MiniDumpWriteDump, NtReadVirtualMemory, and NtDuplicateObject via user-mode hooks or syscall instrumentation, compare arguments against known pattern signatures, and block the call before it returns. Procdump has had signatures since 2018. Nanodump since late 2022. Any technique that ends up calling MiniDumpWriteDump on a handle to lsass.exe triggers the detection chain even when the handle came from a valid PPL bypass.

The third is ASR (Attack Surface Reduction) rules in Defender, specifically the "Block credential stealing from the Windows local security authority subsystem" rule (9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2). When this rule is in Block mode (not Audit), Defender blocks any process that attempts to access lsass.exe with flags that include VM_READ or memory dump primitives, regardless of PPL and regardless of whether the specific technique has a signature. This rule has been part of the Microsoft security configuration baseline since 2018 and is active by default in any MDE deployment with baseline applied.

A modern dump has to clear all three barriers. Most public techniques solve one. Some solve two. Almost none solve all three at once.

What nine methods are on the table in 2026?

ADscan implements nine LSASS dump backends and ranks them based on the target host fingerprint. The exact declaration is in the orchestrator in the public repo:

# adscan_internal/services/exploitation/lsass_orchestrator.py

_ALL_METHODS: tuple[LsassMethod, ...] = (
    LsassMethod(name="comsvcs",            ppl_safe=False, needs_upload=False, opsec_score=2),
    LsassMethod(name="task",               ppl_safe=False, needs_upload=False, opsec_score=2),
    LsassMethod(name="nanodump",           ppl_safe=False, needs_upload=True,  opsec_score=4),
    LsassMethod(name="procdump",           ppl_safe=False, needs_upload=True,  opsec_score=2),
    LsassMethod(name="silentprocessexit",  ppl_safe=True,  needs_upload=False, opsec_score=3),
    LsassMethod(name="ppldump",            ppl_safe=True,  needs_upload=True,  opsec_score=2),
    LsassMethod(name="wsass",              ppl_safe=True,  needs_upload=True,  opsec_score=5),
    LsassMethod(name="pss",                ppl_safe=False, needs_upload=True,  opsec_score=4),
    LsassMethod(name="rtlcp",              ppl_safe=False, needs_upload=True,  opsec_score=4),
)

The nine methods fall into three families by bypass approach.

LOTL (Living off the Land)comsvcs, task, and silentprocessexit. They use signed Windows binaries or native services to trigger the dump. No payload upload required. The weakness is that the invocation pattern is known: rundll32 comsvcs.dll MiniDump <pid> has had a Defender signature since 2019, and silentprocessexit leaves registry entries under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit that any blue team with decent monitoring flags immediately.

Memory primitive bypassnanodump --dup, pss (PssCaptureSnapshot), and rtlcp (RtlCreateProcessReflection). These attack Defender's signature detection by ensuring MiniDumpWriteDump never receives a direct handle to lsass.exe. Nanodump duplicates existing handles to lsass from other processes. PssCaptureSnapshot clones lsass into a virtual snapshot and dumps the snapshot. RtlCreateProcessReflection creates a clone via the Process Reflection API. All three bypass AV signatures because the technical dump target is not the lsass PID. None of the three are PPL-safe: the initial handle open still goes through OpenProcess with privileged flags, which PPL rejects.

Real PPL bypassppldump, wsass, and silentprocessexit. These solve the PPL level problem. Ppldump (itm4n, PPLMedic technique) uses the KnownDlls + DefineDosDevice trick to promote a userland process to PPL via DLL loading hijacking. Silentprocessexit registers lsass.exe for crash trigger with a configured debugger, causing the OS to spawn WerFault with sufficient privileges. Wsass directly invokes WerFaultSecure.exe, a Microsoft-signed binary that runs at PPL level WinTcb — the highest in the system — and passes it the lsass PID to dump the process as if it were a legitimate crash report.

How does each one behave against Defender with PPL active?

I re-ran the matrix against Defender on 2026-05-21 on a Windows Server 2022 with RunAsPPL=1, the credential-stealing ASR rule in Block mode, and Defender signatures updated on the day.

The table below shows all 9 LSASS dump methods tested against Defender with PPL active on Windows Server 2022:

MethodPPL-safeAV-evasionResult vs Defender + PPLFailure reason
comsvcsFailedOpenProcess rejected by PPL before dump
taskFailedSame chain: PPL + ASR rule
nanodump --dupFailedMicrosoft patched --dup on W10 22H2+
procdumpFailedPPL blocks + Defender signature
silentprocessexit⚠️FailedASR rule detects the registry trigger
ppldump (PPLMedic)FailedPatched Win10 19044.1826+ (Jul 2022) + AV sig
pss (snapshot)FailedPPL blocks initial open
rtlcp (reflection)FailedPPL blocks initial open
wsass (WerFaultSecure)Live¹

¹ With ADscan's custom build. The original TwoSevenOneT/WSASS is caught by Defender with error 5 (ACCESS DENIED) before WerFaultSecure starts. See next section.

The wsass method description in the orchestrator catalog is direct: "Launches WerFaultSecure.exe (WinTcb PPL level) to dump lsass. Confirmed bypass of PPL and Defender in testing. Best option when PPL + AV/EDR are both active."

The selector reflects this in scoring: wsass gets a base bonus of +30 points by default, and another +30 points when any AV/EDR product is detected. The two rules combined guarantee it's the chosen method whenever the host fingerprint detects active protection.

# adscan_internal/services/exploitation/lsass_orchestrator.py
# (LsassMethodSelector.rank)

if method.name == "wsass":
    score += 30

if method.name == "wsass" and has_any_protection_detected:
    score += 30

Why can wsass bypass PPL?

wsass is not a vulnerability. It's documented Windows behavior that Microsoft cannot patch without breaking compatibility with signed Windows 8.1 binaries.

WerFaultSecure.exe launches with PROTECTION_LEVEL_WINTCB_LIGHT, the highest PPL level in the system. The Windows PPL hierarchy has several levels: Lsa, Windows, Antimalware, Authenticode, and WinTcb, in ascending order. A PPL process can open privileged handles against other PPL processes of equal or lower level. lsass.exe runs with RunAsPPL=1 at the Lsa level, several levels below WinTcb. When WerFaultSecure.exe requests a handle to lsass.exe, the kernel grants access because the requester's level is strictly higher than the target's.

WerFaultSecure.exe has been signed and in circulation since Windows 8.1 (October 2013). Revoking its certificate or changing its behavior would break error reporting on any system with that heritage. The PPL bypass has no patch ahead of it.

The problem nobody mentions: the original from GitHub was also caught by Defender

That WerFaultSecure.exe bypasses PPL is only half the problem. The other half is Defender.

I compiled the original loader from TwoSevenOneT/WSASS, uploaded it to the target, and ran it with Defender RTP active. Result: error 5. ACCESS DENIED. WerFaultSecure never started.

The original loader builds the command like this:

// TwoSevenOneT/WSASS — original
cmd << werPath
    << L" /h"
    << L" /pid "    << targetPID
    << L" /tid "    << targetTID
    << L" /file "   << HandleToDecimal(hDump)
    << L" /encfile "<< HandleToDecimal(hEncDump)
    << L" /cancel " << HandleToDecimal(hCancel)
    << L" /type 268310";

The argument /type 268310 is 0x41816 in hex. That value has bit 0x40000 (MiniDumpWithFullAuxiliaryState) set.

Defender has a specific signature — VirTool:Win32/LsassDump.B, active from signatures 1.449.367.0+ — that blocks any invocation of WerFaultSecure.exe with a /type that contains that bit. It doesn't matter that the binary is signed by Microsoft. It doesn't matter what PPL level it runs at. Defender blocks the CreateProcessW before WerFaultSecure starts.

268310 in hex = 0x41816
0x41816 & 0x40000 = 0x40000  → bit present → Defender blocks

How does the fix pass the VirTool:Win32/LsassDump.B signature?

The fix is changing the /type to a value without bit 0x40000. MiniDumpWithFullMemory (0x2) produces a complete memory dump of the process and doesn't have that bit.

0x2 & 0x40000 = 0  → bit absent → Defender doesn't block

ADscan's custom version replaces the fixed argument with a pool of bitmaps without 0x40000, selected randomly on each execution for future variation:

// ADscan wsass_dumper — custom version
// ADscan patch: /type bitmap chosen to evade Defender signature
// VirTool:Win32/LsassDump.B (Microsoft Defender sig 1.449.367.0+),
// which matches any /type with bit 0x40000 (MiniDumpWithFullAuxiliaryState) set.
//
// Only 0x2 (MiniDumpWithFullMemory) is empirically validated end-to-end:
//   - bypasses CreateProcessW kernel callback (no error 5)
//   - WerFaultSecure produces a full ~44MB dump
//   - pypykatz parses NT hashes, AES keys, DPAPI masterkeys, Kerberos tickets
static const DWORD kDumpTypePool[] = {
    0x00002,  // MiniDumpWithFullMemory — validated 2026-04-30
};
DWORD dumpType = kDumpTypePool[GetTickCount() % (sizeof(kDumpTypePool) / sizeof(DWORD))];

Lab result with Defender RTP active: complete ~44MB dump, pypykatz parses NT hashes, Kerberos tickets, and DPAPI master keys. VERDICT: ALL_GREEN.

Beyond the /type fix, ADscan's custom version includes four operational improvements over the original:

  1. Auto-discovery of lsass PID and TID via NtQuerySystemInformation. The original required passing the PID as an argument.
  2. PPL process creation via direct Windows API attributes (PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL + PROC_THREAD_ATTRIBUTE_HANDLE_LIST) without the PPLHelp.h library dependency. Fewer external dependencies, more predictable behavior.
  3. MDMP magic header restoration (0x4D44 4D50) after the dump. WerFaultSecure writes a PNG header during writing for AV evasion; ADscan restores the correct format so pypykatz parses it cleanly.
  4. Dump size validation (minimum 10MB) before declaring success. A dump that's too small indicates silent failure.
# adscan_internal/services/exploitation/binary_ops/catalog.py

"wsass-dumper": WindowsBinary(
    name="wsass-dumper",
    display_name="WSASS PPL Dumper (WerFaultSecure)",
    description="PPL bypass via Win8.1 WerFaultSecure.exe at WinTcb level — opens lsass with RunAsPPL=1",
    filename="WSASS.exe",
    compile_source_url="https://github.com/TwoSevenOneT/WSASS",
    default_remote_dir=r"C:\Windows\Temp",
    common_args=(
        r"C:\Windows\Temp\WerFaultSecure.exe {lsass_pid}",
    ),
    requires_admin=True,
),

Why can't Defender block this structurally?

It can block a specific /type argument. It already does — VirTool:Win32/LsassDump.B is the proof. But it can't block the WerFaultSecure.exe binary itself because it's part of the OS and its legitimate function is exactly this: dumping PPL processes that crash.

Microsoft's posture is audit with telemetry, not block on signature. MDE generates alerts on WerFaultSecure.exe invocations with suspicious arguments when the behavioral engine is active, but it doesn't block the CreateProcessW inline. A SOC with proper alerting can detect the activity. The difference from previous methods is that wsass is not automatically blocked: it dumps and leaves the blue team to investigate the alert after the fact.

The technique only has an expiration date if Microsoft adds signatures for /type bitmaps without 0x40000 one by one — a race the attacker wins as long as they have a broader pool than the signature corpus. That's why ADscan maintains the bitmap pool as an extensible structure: when Microsoft adds a signature for 0x2, another validated bitmap without 0x40000 gets added to the pool.

How does ADscan orchestrate the dump when AV is active?

The interesting part isn't the bypass itself. Wsass is public, has been on GitHub for years, anyone can compile it and use it manually against a machine with RunAsPPL=1 and Defender active. The interesting part is building a system that detects the AV before trying anything, selects the method with the highest probability of success based on the fingerprint, persists which method failed against which product so the next machine with the same AV starts avoiding burned methods, and degrades cleanly when the chosen method fails, trying the next one without waiting for the operator to decide.

The architecture has four components in the public repo. The first is HostFingerprintService, which detects AV/EDR and PPL state before the dump using async registry reads via RRPRPC over aiosmb and IPC$ pipe enumeration. No impacket — it stays in ADscan's native async stack. Detection identifies products by service name, process name, DLL presence under C:\Program Files\Windows Defender, and entries in HKLM\SOFTWARE\Microsoft\Windows Defender\Real-Time Protection to distinguish active Defender from disabled Defender.

The second is LsassMethodSelector, pure ranking with no network or I/O, fully unit-testable. It receives the host fingerprint and persisted EdrIntelligence, and returns the nine methods sorted by score. The specific scoring rules are explicit in the code and represent heuristics learned from repeated testing: opsec score base multiplied by 10, -40 points if PPL is active and the method isn't PPL-safe, additional -20 to -50 points if the catch intelligence has recorded that method failing against that specific AV product, +15 LOTL bonus if the host is clean, +30 bonus for wsass by default and another +30 if any protection is detected.

The third is LsassDumpOrchestrator, which wires the previous three into a single async call. The flow is linear: host fingerprint, method ranking, sequential attempt starting from the top-ranked, catch detection on each failure, registration in EdrIntelligence if the catch matches a known product, and cascade to the next method until success or exhaustion. Catch detection uses specific error response indicators:

# adscan_internal/services/exploitation/lsass_orchestrator.py
# (LsassDumpOrchestrator)

_CATCH_INDICATORS = (
    "access denied",
    "privilege",
    "ntstatus",
    "0xc0000022",
    "0xc0000005",
    "lsass",
)

The fourth is EdrIntelligence, JSON persistence per workspace in adscan_internal/workspaces/edr_intelligence.py. Structure: per detected AV/EDR product, list of methods that failed against it with timestamp and source host. The intel persists across executions of the same workspace. The practical consequence: the first time ADscan encounters a new host with Defender active, the orchestrator tries in standard ranking order. The second time it finds Defender on another host, it already skips the burned methods. Cross-engagement learning without operator intervention.

How to reproduce this in lab?

ADscan Host Intel — PPL RunAsPPL=1 and Windows Defender active. The orchestrator selects WSASS WerFaultSecure as top method and extracts 4 credentials from the ESSOS domain, including NT hashes for khal.drogo and SQL service accounts.

Full reproduction against Defender requires three pieces. A Windows Server 2022 or Windows 11 with Defender active, RunAsPPL=1 configured via HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL, and Defender signatures updated. A second machine with ADscan installed (Docker or native, both work). And local admin credentials on the target — all LSASS methods require SeDebugPrivilege or equivalent that only local admins have by default.

For the nine methods individually without orchestrator cascade, there's a manual flag to force a specific method:

adscan dump lsass <target-ip> -u <user> -p <password> --method <method-name>

where method-name is one of comsvcs, task, nanodump, procdump, silentprocessexit, ppldump, wsass, pss, or rtlcp. Forcing one of the eight that fail against Defender + PPL returns the specific catch error in each case, useful both for verifying the matrix and for deliberately training the catch intelligence.

What about MDE, CrowdStrike, SentinelOne?

The 2026-05-21 test covers Defender only. For other EDR products there are two detection mechanisms that operate differently.

MDE (Microsoft Defender for Endpoint) shares the signature corpus with Defender, which means the 0x40000 bit evasion should apply to MDE as well. However, MDE with behavioral engine active generates process telemetry on WerFaultSecure.exe invocations with arguments that don't correspond to legitimate crash reporting flows — even if it doesn't block inline. A SOC with proper alerting on the relevant event ID can detect the activity after the fact. The difference from previous methods: no automatic block, retrospective alert.

CrowdStrike Falcon and SentinelOne use behavioral engines independent of the Windows error reporting subsystem. These engines analyze the process graph (who spawned what, with what arguments, with what inherited handles) and can detect the pattern even without a static signature on the /type. The result against these products is uncertain without empirical testing.

Blue team detection: if you're on the defensive side, the most reliable indicator isn't the dump signature but the parent process of WerFaultSecure.exe. In a legitimate crash report, WerFaultSecure is spawned by the WER service or the crashing process. When spawned by an attacker process with CREATE_PROTECTED_PROCESS and explicit handle inheritance, the process tree is anomalous.

Honest invitation: if you test the matrix against another EDR and get different results, open an issue in the public repo with the catch log and I'll add it to the orchestrator documentation.

What changes in a real engagement?

The operational difference between having wsass integrated in the orchestrator and knowing it as a loose technique is speed and repeatability. Without the orchestrator, a pentester arriving at a machine with Defender active and PPL enabled has to: manually identify that the host has PPL on, manually identify that it has Defender, manually decide which method to try first, manually configure the correct WerFaultSecure invoke arguments, manually upload the binary, execute, parse the output, and if it fails repeat the cycle with another method. Each step takes three to five minutes. Five attempts before finding the working one consumes twenty minutes per host. In an internal network engagement with a hundred hosts, that's thirty-three hours of glue work.

With the orchestrator, the command is invoked once per host, ADscan fingerprints in seconds, ranks in milliseconds, attempts in under a minute if the method is correct, and if it fails tries the next automatically. The catch intelligence also converts each engagement into an accumulated dataset: the fifth time ADscan encounters Defender, it already has the learned matrix and goes straight to wsass without wasting time testing the eight burned methods.

Beyond speed, the strategic consequence is that the operator focuses on the post-dump decision, not the dump itself. Once you have lsass.exe credentials extracted in pypykatz format, the next step (Pass-the-Hash, Kerberoasting, Domain Admin lateral, persistence via Skeleton Key) is the interesting part of the engagement. The dump should be a command, not a project. That's what the orchestrator solves.

If you're interested in the patch diffing approach to understanding why Microsoft signatures certain patterns before there's a public PoC, there's a related post on how to bindiff a Netlogon CVE in 4 hours using the same analysis methodology.

What did we learn building this?

Four observations that hold for the next year of credential dump techniques research.

First: never assume a public technique works as documented. The original WSASS is on GitHub described as "PPL bypass + Defender evasion". Without testing it directly against Defender RTP active, I would have assumed it worked. It didn't. The investigation into why — analyzing the /type argument, finding the signature in the Defender corpus, identifying the specific bit — took hours, not days. The lesson: compile + test + analyze the failure before concluding something works or doesn't. README descriptions aren't empirical evidence.

Second: AV signatures are more granular than they appear. Defender doesn't sign WerFaultSecure.exe or sign "invoke WerFaultSecure to dump lsass" as a generic pattern. It signatures a specific argument — the value of the /type field with a specific bit set. That granularity is both the strength and weakness of the approach: a very specific signature is easy to evade by changing an argument, but it's also durable because Microsoft can maintain and update it independently of the binary. Heuristic for the next bypass: analyze exactly what part is signed, don't assume the signature covers the entire technique.

Third: techniques that abuse structural OS behavior have long shelf life. The WerFaultSecure PPL bypass has worked for fifteen years because it's not an implementation trick, it's documented behavior Microsoft can't remove without breaking compatibility. Each implementation technique has an expiration date: nanodump --dup lasted three years, ppldump eighteen months, comsvcs was viable LOTL until Defender added signatures in 2019. Structural techniques survive because the cost of patching them is higher than the cost of keeping them. Heuristic for evaluating durability: how much cost does eliminating it create for Microsoft? The higher the cost, the longer the shelf life.

Fourth: catch intelligence is worth more than any specific bypass. A matrix of nine methods with catches persisted per product is worth more operationally than having the undetectable method of the month, because the undetectable method of the month gets burned in weeks when AV vendors update signatures. The learned matrix survives because it already knows which one failed against which product and on what date. The system beats the individual trick.


ADscan public repo (includes orchestrator, all nine backends, and catch intelligence): https://github.com/ADScanPro/adscan

The orchestrator lives in adscan_internal/services/exploitation/lsass_orchestrator.py. The binary catalog with the wsass-dumper entry is in adscan_internal/services/exploitation/binary_ops/catalog.py. The fingerprint service in host_fingerprint_service.py.

If it fails against another EDR (MDE, CrowdStrike, SentinelOne, ESET), open an issue in the repo with the catch log and I'll add it to the public matrix this week.

ADscan PRO beta access (includes orchestrator with full flow automation, report automation, Tier 3 OPSEC layers, attack graph integration): request beta

LSASS Dump Bypassing Defender: WerFaultSecure PPL Bypass (2026) | ADscan