What the Tool Actually Does
UnDefend.cpp is 452 lines. It has four independent mechanisms that collectively lock Defender out of its own update pipeline. No mechanism depends on another, if one fails, the others still run. Standard user throughout.
The entry point (wmain) does three things before entering its main loop:
- Reads
HKLM\SOFTWARE\Microsoft\Windows Defender\ProductAppDataPath to find Defender's data directory
- Constructs the
Definition Updates subdirectory path
- Immediately calls
TryLockBackup()
That third step is why rollback doesn't save you.
Mechanism One: Kill the Rollback Before the Fight Starts
TryLockBackup() runs before any threads are spawned. It opens two files:
wcscat(gbackupfile1, L"\\Backup\\mpavbase.lkg");
wcscat(gbackupfile2, L"\\Backup\\mpavbase.vdm");
mpavbase.vdm is the active virus definition module. mpavbase.lkg is the last-known-good backup, the copy Defender falls back to when an update fails or corrupts the active signatures.
The open call is NtCreateFile with no sharing flags at all, the fourth sharing parameter is NULL:
ntstat = NtCreateFile(&hlock1, GENERIC_READ | SYNCHRONIZE | GENERIC_EXECUTE,
&objattr, &iostat, NULL, FILE_ATTRIBUTE_NORMAL,
NULL, // <-- no sharing
FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, NULL, NULL);
FILE_SYNCHRONOUS_IO_ALERT means the kernel holds the thread's I/O context synchronously, this is an internal NT flag that Defender's own file operations use. The open succeeds because nothing has locked these files yet. Once it succeeds, LockFileEx places a blocking exclusive byte-range lock across the full file:
LockFileEx(hlock1, LOCKFILE_EXCLUSIVE_LOCK, NULL, li.LowPart, li.HighPart, &ov);
Now: if a signature update fails and Defender tries to reload from backup, it calls NtCreateFile on mpavbase.lkg and gets STATUS_SHARING_VIOLATION. The fallback is gone. Defender is stuck on whatever signatures it had when TryLockBackup() ran.
Mechanism Two: The Staging Directory Race
The main loop in wmain watches Definition Updates\, the directory where Windows Update stages new signature packages before Defender loads them. It uses ReadDirectoryChangesW:
ReadDirectoryChangesW(hmonitordir, notifydata, sizeof(notifydata),
TRUE, FILE_NOTIFY_CHANGE_SIZE, &retbytes, NULL, NULL);
FILE_NOTIFY_CHANGE_SIZE specifically. Not FILE_NOTIFY_CHANGE_LAST_WRITE. The notification fires on file size changes, which means the write must already be in progress or complete before the callback triggers. This is intentional: the attacker doesn't want to race against an empty file, they want to grab a file with content that Defender is about to load.
When a notification arrives with FILE_ACTION_MODIFIED, the main loop spawns UpdateBlockerThread for that file. The thread opens the file:
stat = NtCreateFile(&hlock, GENERIC_READ | SYNCHRONIZE, &objattr, &iostat,
NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_WRITE | FILE_SHARE_DELETE, // <-- no FILE_SHARE_READ
FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, NULL);
The sharing flags are the mechanism. FILE_SHARE_WRITE | FILE_SHARE_DELETE is explicitly present, which means Windows Update's write process can keep writing. FILE_SHARE_READ is explicitly absent, which means any process that tries to open this file for reading gets STATUS_SHARING_VIOLATION.
Defender's signature loader (MsMpEng.exe) opens mpavbase.vdm for reading. UnDefend holds it open while denying read-sharing. The loader fails. The update fails. The FILE_SHARE_WRITE flag is why the attacker doesn't fight Windows Update at all, it lets the write finish, then blocks the read.
After the open, LockFile places a second layer, a non-blocking exclusive byte-range lock on the full file:
if (!LockFile(hlock, offset.LowPart, offset.HighPart, li.LowPart, li.HighPart)) { ... }
LockFile (not LockFileEx) returns immediately, if it fails, the thread moves on. This is belt-and-suspenders: the sharing violation is the primary block, the byte-range lock is the fallback if something manages to open a compatible handle.
There's a large block of commented-out code in UpdateBlockerThread (lines 101-122) that attempted to filter files by CLSID-formatted subdirectory names, only lock files in legitimate update packages, not everything. The author disabled it:
if (1/*(!CLSIDFromString(mx, &tmp)) || _wcsicmp(__cmp, L"Backup") == 0*/)
The condition is always true. Lock everything that moves in the directory. Crude, but it means zero false negatives.
Mechanism Three: The Engine Restart Hook
WDKillerThread registers an asynchronous service status notification for when WinDefend stops:
NotifyServiceStatusChangeW(hsvc, SERVICE_NOTIFY_STOPPED, &svcnotify);
SleepEx(INFINITE, TRUE);
SleepEx with TRUE puts the thread in an alertable wait, Windows will interrupt it via APC when the service notification fires. When WinDefend stops (as it does during platform updates, when MsMpEng.exe itself is being replaced), WDKillerCallback executes:
DWORD retcode = RegQueryValueEx(wdkey, L"SignatureLocation", NULL, NULL,
(LPBYTE)sigpath, &retsz);
// ...
wcscat(sigpath, L"\\mpavbase.vdm");
The callback reads HKLM\SOFTWARE\Microsoft\Windows Defender\Signature Updates\SignatureLocation to find where the live signatures are (distinct from the staging directory), then opens and locks mpavbase.vdm at that path. No sharing, exclusive lock, FILE_SYNCHRONOUS_IO_ALERT again:
ntstat = NtCreateFile(&hlock, GENERIC_READ | SYNCHRONIZE | GENERIC_EXECUTE,
&objattr, &iostat, NULL, FILE_ATTRIBUTE_NORMAL,
NULL, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, NULL, NULL);
LockFileEx(hlock, LOCKFILE_EXCLUSIVE_LOCK, NULL, li.LowPart, li.HighPart, &ov);
When the new MsMpEng.exe starts after the platform update completes, it tries to load signatures. The live mpavbase.vdm is locked. Defender comes up running, service status shows RUNNING, but it can't load its signature database. It's running blind.
This is the aggressive mode path. The README notes it only triggers during major platform updates (when the engine binary itself is replaced), which don't happen on the same cadence as definition updates. The passive path (mechanism two) runs continuously regardless.
The commented-out line at the end of wmain makes the relationship explicit:
// run in killer mode
// WaitForSingleObject(wdkiller,INFINITE);
Uncommenting that would block the main thread on the killer thread, disabling the passive path. The author chose to run both simultaneously.
Mechanism Four: MRT
MRTWorkerThread runs the same ReadDirectoryChangesW + UpdateBlockerThread pattern against a hardcoded path:
wchar_t _wdupdatedir[] = { L"\\??\\C:\\Windows\\System32\\MRT" };
The Malicious Software Removal Tool maintains its own detection database, separate from Defender's signature files. UnDefend locks that too. Every component of Microsoft's on-device malware detection, Defender signatures, Defender engine, MRT, has a corresponding lock.
Why It Stays Running
Every lock in UnDefend is a byte-range lock held by an open file handle. Byte-range locks are advisory and process-scoped: they release automatically when the handle closes, which happens when the process exits.
AddHandle() exists to keep every handle alive in the process:
void AddHandle(HANDLE hlock)
{
// shit code but works i guess
static unsigned int handlecount = 0;
HANDLE* ntracker = (HANDLE*)malloc((++handlecount + 1) * sizeof(HANDLE));
// ...
}
The author's self-assessment is accurate, it's a manually-grown heap array, realloc'd on every call, protected by a CRITICAL_SECTION. But the goal is just to prevent the handles from going out of scope. As long as UnDefend.exe runs, the locks hold. If the process is killed, all locks release within milliseconds and Defender can update normally.
This is the detection surface. A process with a large number of open handles to files under %ProgramData%\Microsoft\Windows Defender\Definition Updates\ is the indicator. Specifically: handles to .vdm and .lkg files in that tree, held by a non-Defender process.
What Chaotic Eclipse Held Back
The code that shipped blocks updates and makes them fail visibly. Windows displays an error: Defender can't update, last update failed. An EDR operator watching their dashboard sees red. They know something is wrong.
The README describes code that wasn't shipped:
"I found a way to lie to the EDR web console to show that defender is up and running with the latest update even if it's not."
Defender reports its health status, signature version, last update time, engine version, overall protection status, through the MSFT_MpComputerStatus WMI class. This is the provider that Microsoft Endpoint Manager, SCCM, Intune, and every third-party SIEM integration queries to determine whether a given machine has current protection. If you can write to that provider, or intercept its output, you can report a signature date and version that doesn't match the locked files on disk.
Without the console spoofing code, UnDefend is a DoS with a visible signature: update errors accumulate, the security dashboard shows protection degraded. Noisy.
With it, the dashboard shows current signatures. The security team sees green. The machine is actually running signatures from whenever UnDefend started, weeks ago, a month ago, however long it's been running. Any new malware developed after that date is invisible to Defender. The attacker gets an indefinite detection window, and the defenders have no signal that anything is wrong.
The difference between the two is the same difference Chaotic Eclipse navigated in BlueHammer: the line between "this demonstrates the vulnerability" and "this is a weapon for sustained compromise." In BlueHammer, SYSTEM and SECURITY hives were commented out of filestoleak[]. Here, the WMI spoofing code was written and withheld.
The researcher drew the line in the same place twice. It's worth noting that both times, the withheld component is the one that makes the attack silent.
The Trilogy
Three tools. One month. One researcher. They compose into a complete attack chain, and nobody in the public coverage has connected the sequence with the code.
RedSun uses Defender's SYSTEM write primitive against itself. The attack plants a batch oplock on a file in the update staging path, registers a fake Cloud Files sync provider named "SERIOUSLYMSFT" to stall Defender's VSS access, then stamps a mount point reparse on the update directory pointing to \??\C:\Windows\System32. When the oplock breaks and Defender resumes, it follows the redirected path and writes TieringEngineService.exe into System32 with NtCreateFile using FILE_SUPERSEDE, create-or-replace, at SYSTEM privilege. The Storage Tiering Engine Service is a legitimate Windows service. The next time it starts, it runs attacker-supplied code as SYSTEM.
BlueHammer (CVE-2026-33825) uses the same oplock plus Cloud Files plus object manager architecture, but changes the fake sync provider from "SERIOUSLYMSFT" to "IHATEMICROSOFT" and redirects the write at \Windows\System32\Config\SAM instead of a system binary. The result isn't code execution, it's the credential database, readable by the attacker after Defender writes it to their staging path.
UnDefend doesn't use any of that. Standard user. No race condition, no object namespace manipulation, no Cloud Files freeze. It holds byte-range locks on the files Defender needs to load signatures. Permanent update block, no elevation required.
The three form a sequenced operation: UnDefend creates a detection window by preventing new signatures from loading. Once the window is established, BlueHammer (or any other LPE) runs against a Defender that cannot see whatever payloads have been released since the lock started. RedSun provides the SYSTEM code execution that closes the loop. No tool in the chain communicates with any other. Each is independently deployable.
The Security Model These Three Tools Break
Windows Defender requires SYSTEM privilege to remediate files because it needs to modify locked or protected binaries that user-space processes cannot touch. That design decision is correct, the kernel enforces it and it cannot be bypassed. RedSun and BlueHammer do not bypass it. They weaponize it.
The trust model's assumption is: "Defender is elevated, therefore its writes are legitimate." The actual mechanism is: "Defender writes to whatever path resolves at write time." These two statements are only equivalent if path resolution is controlled by the OS and not the user. A standard user can influence path resolution before the write happens. FSCTL_SET_REPARSE_POINT requires no elevation for a directory the user owns. CfRegisterSyncRoot requires no elevation. NtCreateSymbolicLinkObject in a session-local namespace requires no elevation. None of these are bugs. They're designed behaviors. The attack chains them in a sequence Defender's designers did not consider.
UnDefend and the withheld spoofing code break a different assumption. Defender's health reporting is authoritative because it runs as a SYSTEM service queried through a trusted channel, MSFT_MpComputerStatus, the WMI class that Endpoint Manager, SCCM, and Intune read to assess machine protection state. The management console trusts that class because the service that populates it is trusted. The withheld code demonstrates that trust is not structurally enforced. It can be spoofed. The mechanism is unshipped, but the researcher confirmed it works.
Both assumptions, that elevated writes are legitimate, and that the health report is authoritative, are reasonable. Neither is verifiable at runtime without changes to how Windows handles path resolution and WMI provider trust. A patch for BlueHammer doesn't fix the class of vulnerability. RedSun remains unpatched. UnDefend has no CVE.
The author changed the sync provider name from "SERIOUSLYMSFT" to "IHATEMICROSOFT" between the first and second drops. The code got more sophisticated. The message got clearer. The third tool dropped with no CVE assigned and no patch in sight.
The comment at line 209 was directed at whoever opened the source file. You opened it. The code works.
What You Can Do on Monday
Detection. Hunt for non-Defender processes holding file handles under %ProgramData%\Microsoft\Windows Defender\Definition Updates\. The specific tells: handles to *.vdm and *.lkg files in that tree, exclusive byte-range locks, open handles to files in C:\Windows\System32\MRT\. Sysinternals Handle (handle.exe -a mpavbase) surfaces this immediately.
Monitoring. Don't trust the Defender health dashboard alone. Cross-reference reported signature dates against actual .vdm file timestamps on disk. If the WMI-reported signature date is newer than the file's last-modified timestamp, something is wrong. (The withheld spoofing code would break this check. It isn't shipped yet.)
Process monitoring. UpdateBlockerThread spawns a new thread per directory notification. A process that has been running against an active machine will have accumulated dozens of threads, each holding a file handle. The thread count and handle count profile is distinctive.
Defender update failures. If MsMpEng.exe is failing to apply updates (EVENT_ID 2001 in the Windows Defender operational log) with no system or policy explanation, check for non-Defender handle holders on the signature files.
RedSun and UnDefend. For RedSun: look for CfRegisterSyncRoot calls from processes other than known sync clients, mount point reparse points on Cloud Files sync roots, and unexpected writes to %WINDIR%\System32\TieringEngineService.exe. For UnDefend: the process must persist. Terminate it and Defender recovers. Find it before it daemonizes.
PGP signature: undefend.md.asc, Key fingerprint: 5FD2 1B4F E7E4 A3CA 7971 CB09 DE66 3978 8E09 1026
The comment at line 209 was directed at whoever opened the source file. You opened it. The code works.