Month of Bypasses Iteration 3: Winlogon's Got Your Credentials
- 2 days ago
- 4 min read
By the Persistent Security Research Team - May 2026
Process Injection - MITRE ATT&CK T1055.002
Last time, we loaded mimikatz entirely from memory without Defender noticing. This time, we take the next step: cross-process injection into one of Windows' most sensitive processes to check if AV or EDRs will catch this!
We inject a .NET CLR runtime into winlogon.exe, read stored credentials from registry, and exfiltrate them over DNS - all without triggering a single Defender alert. Specifically: We're bootstrapping the .NET Common Language Runtime inside the remote process via custom shellcode, as malicoius payload we're executing arbitrary managed code in the context of a SYSTEM-level critical process to do DNS-based data exfiltration of credentials or keystrokes.
All of the above including was created completely automated and verified via Nemesis AI!
What Defender Should Block
Cross-process injection into system processes like winlogon.exe represents one of the highest-severity attack patterns. Here's what we would expect to trigger alerts and lead to a catch:
Block 1: Remote thread creation into critical processes
$hProc = OpenProcess(0x1F0FFF, $false, $winlogonPid)
$mem = VirtualAllocEx($hProc, [IntPtr]::Zero, $size, 0x3000, 0x40)
WriteProcessMemory($hProc, $mem, $shellcode, $size, [ref]$written)
CreateRemoteThread($hProc, [IntPtr]::Zero, 0, $mem, [IntPtr]::Zero, 0, [ref]$tid)Sysmon EventID 8 fires when CreateRemoteThread targets a process like winlogon.exe. EDRs flag PROCESS_ALL_ACCESS handles to system processes. VirtualAllocEx with PAGE_EXECUTE_READWRITE into a remote process is a textbook injection indicator.
Block 2: CLR loading in system processes
winlogon.exe should never host the .NET runtime. The appearance of clrjit.dll or coreclr.dll in winlogon's module list is an anomaly so severe that any behavioral detection engine should flag it.
Block 3: DNS-based data exfiltration from system processes
Base32-encoded subdomain queries to unusual TLDs from a process that normally makes zero DNS calls is a high-fidelity exfiltration signal.
The conventional wisdom says all three layers should catch this. Let's see what actually happens.
How Nemesis Created The Technique
We tasked the Nemesis variant analysis engine with a precise objective:
Inject .NET CLR into winlogon.exe, read AutoLogon credentials from registry within winlogon's context, and exfiltrate them via DNS - entirely undetected by Defender on a fully patched Windows 11 system.
The success check was rigorous: verify CLR appears in winlogon's loaded modules, confirm the proof file contains the randomly-generated credential (preventing hardcoding), validate the winlogon PID matches, and confirm DNS exfiltration evidence. A random credential is planted before each run - the payload must dynamically read it from registry to prove it works. The proof-of-concept is also verified to not cheat by reading the registry directly etc..
Generation 1: The Diverse Injection Primitive Survey (5 variants, all failed)
Hypothesis: Try every known injection primitive against winlogon — one of them must evade Defender's CreateRemoteThread monitoring.
Variant | Injection Method | Result |
NtMapViewOfSection | Shared memory mapping (no WriteProcessMemory) | Failed |
QueueUserAPC | APC to alertable threads (no new thread) | Failed |
D/Invoke NtCreateThreadEx | Indirect syscalls (avoid kernel32 hooks) | Failed |
Thread Hijack | GetThreadContext/SetThreadContext (no thread creation) | Failed |
Early Bird APC | APC before EDR hooks initialize | Failed |
Five different approaches, five different ways to avoid CreateRemoteThread - all failed. One explanation: Defender could be blocking cross-process injection at a fundamental level, regardless of the primitive used. But that conclusion was wrong!
The Debug Breakthrough
Nemesis has a heuristic to detect such cases where everything fails and spawned a dedicated debug agent on Gen 3 CLR hosting shellcode variant to capture full stdout/stderr. The output revealed:
Add-Type : Constraint cannot be special class 'System.Delegate'
c:\Windows\SystemTemp\v1s2sbko.0.cs(2) : >>> public class ClrSC{...
static T G<T>(string n) where T:Delegate { ... }The failure wasn't Defender. It was a C# compiler error.
The where T : Delegate generic constraint is a C# 7.3+ feature. The .NET Framework's in-box csc.exe only supports C# 5.0. Every single variant so far had this same bug in the injector code - the injection never executed correctly. We were debugging phantom Defender blocks that didn't exist. The agent course corrected quickly.
Generation 4: The Truth (5 variants, 1 immediate success)
With the compile bug identified, Generation 4 used explicit delegate types instead of generic constraints.
It worked on the first try.
The "canary test" using the most basic, unsophisticated injection possible passed completely undetected. No D/Invoke needed. No syscall unhooking. No thread hijack tricks. Just OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread into winlogon.exe.
The Complete Attack Chain
Here's what executes end-to-end without detection:
Stage 1 — Compile a .NET payload DLL using csc.exe. The DLL contains a Run(string) method that reads DefaultPassword from the Winlogon registry key, base32-encodes it, calls DnsQuery_A to exfiltrate via DNS, and writes a proof file.
Stage 2 — Build x64 shellcode that bootstraps the CLR hosting API chain inside the target process:
LoadLibraryA("mscoree.dll")
→ GetProcAddress("CLRCreateInstance")
→ CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)
→ ICLRMetaHost::GetRuntime("v4.0.30319")
→ ICLRRuntimeInfo::GetInterface(CLSID_CLRRuntimeHost)
→ ICLRRuntimeHost::Start()
→ ExecuteInDefaultAppDomain(payload.dll, "PL", "Run", "go")Stage 3 — Inject into winlogon.exe: allocate a data page (strings, GUIDs) and a code page (shellcode), write both via WriteProcessMemory, fire via CreateRemoteThread.
Stage 4 — The shellcode executes inside winlogon, bootstraps CLR, loads the payload DLL, which reads the credential, performs DNS exfiltration to <base32>.k.exfil-not-exist.example, and writes evidence.
The Result
Stage | Defender Detection | Behavior Block | Result |
OpenProcess on winlogon (PROCESS_ALL_ACCESS) | None | None | NOT PREVENTED |
VirtualAllocEx RWX in winlogon | None | None | NOT PREVENTED |
WriteProcessMemory (shellcode + data) | None | None | NOT PREVENTED |
CreateRemoteThread in winlogon | None | None | NOT PREVENTED |
CLR bootstrap (clrjit.dll loaded in winlogon) | None | None | NOT DETECTED |
Registry credential read from winlogon context | None | None | NOT DETECTED |
DnsQuery_A exfiltration from winlogon | None | None | NOT DETECTED |
Complete attack chain: NOT PREVENTED, NOT DETECTED.
This matters because it demonstrates how easy standard defenses can be bypassed by automated AI systems in fully-patched Windows 11 configuration, not triggering on the fundamental primitives even. Advanced EDRs could and surely find this (which we can test with a few clicks!).
The Cost
Metric | Value |
Generations | 4 |
Variants tested | 13 |
Compute cost | < 5 EUR |
Time to solution | ~1 hours (including debugging and verification on a single thread) |
Effective variants needed | 4 |
The full atomic technique is available as a Nemesis BAS atomic: T1055.002_2 — Winlogon CLR Injection with DNS Credential Exfiltration.
The Month of Bypasses is a research project by the Persistent Security team. We are describing important aspects of variant analysis and limits of current defenses. Should we feature actual vulnerabilities beyond detection gaps, we will disclose them through the Belgian Coordinated Vulnerability Disclosure (CVD) framework via the Centre for Cybersecurity Belgium (CCB).
Nemesis BAS is available at persistent-security.net.
Have questions or want to test your own defenses? Contact us at info@persistent-security.net.


