-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 *`cfreg.ProviderName = L"IHATEMICROSOFT";`* That's line 1469 of FunnyApp.cpp, the working proof-of-concept for CVE-2026-33825. To exploit BlueHammer, Nightmare-Eclipse's code registers a fake Cloud Files sync provider, the same Windows feature that backs OneDrive, under that name. The name is editorial. The mechanism it exposes is architectural. This post is a line-by-line walk of the code, because most of the public analysis of BlueHammer describes what the vulnerability is without explaining how the two weapons inside the PoC relate to each other, and the relationship is the point. I've covered [RedSun](/posts/redsun-windows-defender-system-write) on this blog already. BlueHammer is described as a reimplementation in a different language, and technically that's not wrong, both bugs live in the same component and the same fundamental problem. But "reimplementation" undersells how different the attack surface is. RedSun goes through Cloud Files + mount point reparse at the filesystem layer. BlueHammer goes through Defender's definition update RPC endpoint and the NT object manager namespace. They share a class. They don't share a technique. The patch that closes one does not obviously close the other. ## The Two Weapons FunnyApp.cpp contains two independent exploitation paths. I'm going to call them by the functions that carry them: **the oplock path** and **FreezeVSS**. They are not alternatives. They run sequentially in the exploit, and understanding why requires understanding what each one freezes and when. The oplock path exploits a TOCTOU in Defender's remediation step. The FreezeVSS path uses the Cloud Files API to hold Defender suspended while the oplock path completes its redirect. You need both. The oplock gives you the window; the freeze makes the window wide enough. Neither path requires admin. Neither path requires any Windows privilege beyond what a standard user session provides. The whole exploit runs from a temporary directory the attacker creates under `%TEMP%`. When `wmain` checks `IsRunningAsLocalSystem()` at startup and finds the answer is no, the fun begins. ## Weapon One: The Oplock Race The first thing the code does is create a UUID-named working directory under `%TEMP%`, then write a file named `foo.exe` into it: ```c char eicar[] = "*H+H$!ELIF-TSET-SURIVITNA-DRADNATS-RACIE$}7)CC7)^P(45XZP\\4[PA@%P!O5X"; rev(eicar); ``` The EICAR test string is stored reversed in the binary and reversed at runtime, a minimal obfuscation against static analysis of the binary itself. The write lands in `foo.exe`, and then the code opens a handle to trigger Defender's real-time protection: ```c trigger = CreateFile(eicarfilepath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ``` This is the gun. Not the bullet. The EICAR file is bait, its purpose is to make Defender want to remediate something. Before it does, the exploit has already requested a batch oplock on a different file entirely: ```c hlock = CreateFile(rstmgr, GENERIC_READ | SYNCHRONIZE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); DeviceIoControl(hlock, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ovd); ``` `rstmgr` is `%windir%\System32\RstrtMgr.dll`, the Restart Manager DLL. Defender, when it identifies a threat and prepares to remediate it, calls the Restart Manager to check whether any processes have the malicious file locked open. That check requires opening `RstrtMgr.dll`. The batch oplock is sitting on that path. The moment Defender tries to open it, the kernel signals the oplock to the attacker and suspends Defender's open call until the attacker releases. That suspension is the TOCTOU window. The attacker requested it before the EICAR file was visible to Defender. Defender finds the threat, begins remediation, tries to open the Restart Manager DLL, and blocks. The attacker gets the signal: ```c GetOverlappedResult(hlock, &ovd, &nwf, TRUE); printf("Oplock triggered.\n"); ``` Now it has an indefinite window to manipulate the filesystem while Defender is frozen mid-operation. What it does with that window is the second weapon. ## Weapon Two: The Object Manager Redirect I want to be precise about what kind of symlink BlueHammer uses, because this is where most descriptions of the exploit get imprecise. The exploit does not create an NTFS symbolic link. It creates an NT object manager symbolic link, using `NtCreateSymbolicLinkObject`, a kernel API that operates on the NT object namespace, which is a completely separate namespace from the filesystem. The NT object namespace sits below the filesystem in the Windows I/O stack. When a user-mode process opens a path like `C:\foo\bar`, the kernel expands that through the object namespace, `\??` is a per-session alias for `\DosDevices\`, and named objects like directories, events, and symlinks can all live in the namespace tree at `\Sessions\\BaseNamedObjects\`. A symlink in the object namespace redirects path resolution before the filesystem driver ever sees the path. The exploit creates a private object directory in the session namespace: ```c wnsprintf(objdirpath, MAX_PATH, L"\\Sessions\\%d\\BaseNamedObjects\\%s", _sesid, wuid2); ntstat = _NtCreateDirectoryObjectEx(&hobjworkdir, GENERIC_ALL, &ndirobjattr, NULL, NULL); ``` Then, when the oplock fires, inside the window where Defender is frozen mid-open on RstrtMgr.dll, the old directory symlink is closed and a new one is planted: ```c CloseHandle(hsymlink); RtlInitUnicodeString(&_unisrc, L"WDUpdateDirectory"); RtlInitUnicodeString(&_unidest, objdirpath); ntstat = _NtCreateSymbolicLinkObject(&hsymlink, GENERIC_ALL, &_smobjattr, &_unidest); ``` `WDUpdateDirectory` is the name Defender uses to locate its definition update staging directory. The symlink now points the object namespace name that Defender will look up to a path the attacker controls. Defender resumes, follows the redirect, and performs its SYSTEM-privileged write into a directory it has no idea is attacker-controlled. The second symlink is the payload: ```c RtlInitUnicodeString(&objlinkname, L"mpasbase.vdm"); wcscpy(nttargetfile, fullvsspath); wcscat(nttargetfile, filestoleak[x]); ntstat = _NtCreateSymbolicLinkObject(&hobjlink, GENERIC_ALL, &objattr, &objlinktarget); ``` `filestoleak[0]` is `\\Windows\\System32\\Config\\SAM`. `mpasbase.vdm` is Defender's main signature definition file. When Defender opens what it believes is its own definition file to write an update, it is opening SAM, the local Security Account Manager database, which contains every local user's hashed password. The read happens at SYSTEM privilege. The data is yours. ## Why FreezeVSS Is the Enabling Condition There is a timing problem with the oplock path alone. The window between the oplock firing and Defender resuming is real, but it is not infinite, and creating the object namespace symlinks under timing pressure is unreliable on loaded systems. FreezeVSS solves this. ```c CF_SYNC_REGISTRATION cfreg = { 0 }; cfreg.ProviderName = L"IHATEMICROSOFT"; cfreg.ProviderVersion = L"1.0"; syncpolicy.Hydration.Primary = CF_HYDRATION_POLICY_PARTIAL; syncpolicy.Hydration.Modifier = CF_HYDRATION_POLICY_MODIFIER_VALIDATION_REQUIRED; ``` The Cloud Files API is the Windows abstraction layer for cloud storage sync providers. Applications register as sync providers by calling `CfRegisterSyncRoot`, marking a local directory as backed by cloud storage. When a process opens a file in that directory, the Cloud Files filter driver can intercept the open and force a hydration callback, `CF_HYDRATION_POLICY_MODIFIER_VALIDATION_REQUIRED` means the file cannot be opened until the registered provider explicitly validates it. FreezeVSS registers a Volume Shadow Copy directory under this fake provider. Defender, when it processes the SAM redirect, attempts to access the file through VSS. The access hits the Cloud Files filter driver. The filter driver calls back into the "IHATEMICROSOFT" provider to validate hydration. The "IHATEMICROSOFT" provider is the exploit itself. The exploit does not validate. Defender blocks. The timeline in the code makes this sequencing explicit: ```c hthread2 = CreateThread(NULL, NULL, FreezeVSS, &cldthreadargs, NULL, &tid); // ... wait for FreezeVSS to signal hvssready WaitForSingleObject(cldthreadargs.hvssready, INFINITE); printf("WD is frozen and the new VSS can be used.\n"); ``` The exploit does not touch the object namespace symlinks until FreezeVSS has confirmed Defender is suspended. The sequence is: 1. Write EICAR file to trigger detection 2. Batch oplock on RstrtMgr.dll, waits for Defender to begin remediation 3. Oplock fires, Defender is frozen mid-open on the Restart Manager DLL 4. Meanwhile, FreezeVSS registers the Cloud Files provider and blocks Defender's VSS access 5. `hvssready` event fires, Defender is now frozen by both weapons simultaneously 6. Object namespace symlinks planted: `WDUpdateDirectory → attacker path`, `mpasbase.vdm → SAM` 7. Oplock released, Defender resumes, follows the redirect, reads SAM at SYSTEM 8. FreezeVSS cleanup event fires, Cloud Files registration dropped, Defender unsticks The Cloud Files freeze is not a belt-and-suspenders redundancy. It covers the phase after the oplock releases, when Defender has resumed but has not yet finished the file operation. Without FreezeVSS, the race window closes when the oplock releases. With it, the window stays open until the exploit chooses to close it. ## The RPC Trigger There is a third mechanism I haven't covered: how the exploit makes Defender start a definition update in the first place. Defender's real-time protection will scan the EICAR file, but the definition update path, the specific code that walks `C:\ProgramData\Microsoft\Windows Defender\Definition Updates\` and creates subdirectories, requires an explicit trigger. The exploit calls Defender over RPC: ```c RPC_WSTR MS_WD_UUID = (RPC_WSTR)L"c503f532-443a-4c69-8300-ccd1fbdb3839"; RpcStringBindingComposeW(MS_WD_UUID, (RPC_WSTR)L"ncalrpc", NULL, (RPC_WSTR)L"IMpService77BDAF73-B396-481F-9042-AD358843EC24", NULL, &StringBinding); ``` `c503f532-443a-4c69-8300-ccd1fbdb3839` is the UUID of Windows Defender's undocumented `IMpService` LRPC interface, the same interface the Windows Security app uses to talk to the Defender service. `IMpService77BDAF73-B396-481F-9042-AD358843EC24` is the named pipe endpoint. Both are accessible from any user-mode process in the same session; neither requires elevated privilege to call. The RPC call instructs Defender to process a definition update from the directory the exploit has staged. Defender initiates the update, walks into the attacker-controlled directory structure, and follows the symlinks. The `ReadDirectoryChangesW` loop in the exploit is watching `C:\ProgramData\Microsoft\Windows Defender\Definition Updates\` to confirm Defender is doing this before releasing the weapons: ```c hdir = CreateFile(L"C:\\ProgramData\\Microsoft\\Windows Defender\\Definition Updates", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL); ReadDirectoryChangesW(hdir, buff, sizeof(buff), TRUE, FILE_NOTIFY_CHANGE_DIR_NAME, &retbytes, &od, NULL); ``` The exploit will not proceed to the symlink swap until it sees Defender create a new subdirectory in `Definition Updates`. This is the handshake between the RPC trigger and the oplock path. ## What the Author Left In Before the comparison with RedSun, I want to stop at one line of FunnyApp.cpp, because it is the most important line in the file and the one I've seen no other writeup quote: ```c const wchar_t* filestoleak[] = { {L"\\Windows\\System32\\Config\\SAM"} /*,{L"\\Windows\\System32\\Config\\SYSTEM"},{L"\\Windows\\System32\\Config\\SECURITY"}*/ }; ``` `SYSTEM` and `SECURITY` are commented out. Not removed. Commented. The PoC ships SAM as the demonstration target. The author committed the other two targets in the same line, then commented them before publishing. They tested the technique against all three hives. They chose to demo one. This matters operationally. SAM alone is a partial credential dump. The hashes in SAM are encrypted with the boot key. The boot key lives in the SYSTEM hive. Without SYSTEM you have ciphertext. With SAM plus SYSTEM together you have NTLM hashes, passable and crackable offline. The SECURITY hive adds LSA secrets: service account credentials, cached domain logon hashes, DPAPI master keys. Domain penetration, lateral movement, persistent access, the complete package requires all three. The complete credential package is two uncommented lines away from the published proof of concept. The author wrote both lines. They chose what to ship. I read that comment as a technical disclosure with a deliberate scope. The researcher demonstrated the mechanism was real. They did not demonstrate the full extent of it. The difference between a research disclosure and a ready-to-deploy weapon is two characters of C comment syntax, and the researcher drew that line with precision. ## BlueHammer vs. RedSun: Same Class, Different Surface I've written about [RedSun](/posts/redsun-windows-defender-system-write) and said the gap it opened is unclosed. I should be precise about what BlueHammer's April 2026 patch actually patches. RedSun exploits the Cloud Files API at the filesystem level: it registers a sync root, marks files as cloud-backed, forces hydration callbacks, and redirects the resulting SYSTEM-level file operation via NTFS mount point reparse. The reparse point is an NTFS-layer primitive; the redirect happens inside the filesystem driver. BlueHammer exploits Defender's definition update mechanism at the object namespace level: it uses `NtCreateSymbolicLinkObject` to plant symlinks in the session object namespace, then triggers a SYSTEM-level operation via Defender's own RPC interface to follow them. The redirect happens inside the NT I/O manager, before the filesystem driver is involved at all. Two different layers of the Windows I/O stack. The patch for BlueHammer, which eliminated the unsafe object namespace symlink traversal in Defender's update code path, does not address the Cloud Files attack surface that RedSun used. Patch Tuesday closed the BlueHammer class. It said nothing about RedSun because RedSun is a different class. The shared root is narrower than the shared CVE number implies: in both cases, a SYSTEM-privilege Defender operation follows a path name that user-mode code can redirect. The difference is which layer of path resolution the redirect operates at. Microsoft fixed the definition update path. The IMpService RPC interface that triggered the vulnerable operation is undocumented and has other callable methods. The author found one method whose code path walks an object-namespace-redirectable path. The interface's remaining call surface hasn't been audited against this class of attack in the post-patch world. The definition update method is patched. The other methods were never identified as candidates. ## What the Code Tells You About the Fix The patch Microsoft shipped targets the definition update path, the code that Defender's SYSTEM-level process runs when `CallWD()` triggers an update cycle. Specifically, it fixes the unsafe handling of the `WDUpdateDirectory` object namespace name: Defender now resolves that name through a hardened lookup that won't follow symlinks into attacker-controlled locations. What the patch does not change: - - The `IMpService` RPC interface is still callable by any user-mode process - - `NtCreateSymbolicLinkObject` in the session namespace still works from standard user - - The Cloud Files freeze technique (FreezeVSS) still works, the provider registration path is unaffected - - The general pattern of SYSTEM-level file operations following redirectable path names is still Defender's architecture The author named their fake sync provider `IHATEMICROSOFT`. The feeling appears to be informed by source-code familiarity. The patch closed the specific door the PoC walks through. The wall the door is cut into is unchanged. ## What You Can Do on Monday You are on the patched version or you are not. That is the first question, and it has a definite answer. Run `Get-MpComputerStatus | Select-Object AMEngineVersion, AMProductVersion`, compare against Microsoft's April 2026 Patch Tuesday signature. If your fleet is not patched, this is the session you stop reading and start patching. If you are patched, there is a second question: do you have EDR coverage that would have caught the exploitation behavior before the patch existed? Because the behavioral indicators in FunnyApp.cpp are loud: - - A user-mode process creating `NtCreateSymbolicLinkObject` entries under `\Sessions\\BaseNamedObjects\` pointing toward `System32\Config` - - `RpcStringBindingCompose` calls targeting `c503f532-443a-4c69-8300-ccd1fbdb3839` from outside the Windows Security app - - `CF_SYNC_REGISTRATION` events from a process that is not a known sync provider - - `ReadDirectoryChangesW` on `C:\ProgramData\Microsoft\Windows Defender\Definition Updates` Any competent EDR with user-mode syscall telemetry would have seen this. The question is whether yours has a rule for it, or whether it needed the CVE to ship the rule. Check your detection gap. The PoC has been public since March. ## The Close The provider name is not a joke. It is a precise description of the experience of finding a SYSTEM-level write-anywhere primitive inside the process that is supposed to stop those. The researcher found an RPC endpoint, a batch oplock, an object namespace symlink, and a Cloud Files freeze, and assembled them into a five-step sequence that requires no administrative privilege and produces a SAM database read at SYSTEM. The code is a hundred kilobytes and well-commented. It runs in a temp directory. It leaves no persistent artifacts until the payload lands. Microsoft patched the specific path. The RPC interface is still there. The object namespace is still there. The Cloud Files filter driver is still there. The fundamental condition, a SYSTEM-privilege process that follows redirectable paths, is still there. The author hates Microsoft. The code works. The two facts are related. - --- *PGP signature: [bluhammer.md.asc](/sigs/bluhammer.md.asc), Key fingerprint: `5FD2 1B4F E7E4 A3CA 7971 CB09 DE66 3978 8E09 1026`* - --- ## addendum (2026-04-18) The post named the title's promise but not the structure it was describing. Line 2884 of `wmain`: ```cpp const wchar_t* filestoleak[] = { {L"\\Windows\\System32\\Config\\SAM"} /*,{L"\\Windows\\System32\\Config\\SYSTEM"},{L"\\Windows\\System32\\Config\\SECURITY"}*/ }; ``` `filestoleak` is the array Defender is directed to open from the VSS snapshot via the object manager symlink redirect. SAM is live. SYSTEM and SECURITY are the two commented-out entries. **What SAM gives:** `DoSpawnShellAsAllUsers` opens the leaked SAM handle offline using the `offreg` library, reads the NT hash for every local account, and decrypts them using the bootkey read from the live `HKLM\SYSTEM\CurrentControlSet\Control\Lsa` registry path (accessible to any standard user process). Each result is printed: `User: ... RID: ... NTLM: ...`. **What the code does with the hashes:** It does not stop at printing. For every local account except the current user and `WDAGUtilityAccount`, `ChangeUserPassword` is called with the decrypted old NT hash and a hardcoded new password: `$PWNed666!!!WDFAIL`. The call uses `SamiChangePasswordUser`, which accepts the old hash as proof of identity in place of old plaintext. After the password change, `LogonUserEx` logs in as each modified account and spawns a shell with that token. **What SYSTEM and SECURITY would add:** SYSTEM from the VSS gives an offline copy of the bootkey for post-exploitation contexts without live registry access. SECURITY gives cached domain logon hashes (DCC2) for domain users who have authenticated on the machine, plus LSA secrets: service account plaintext passwords, auto-logon credentials, and DPAPI machine master keys. The PoC ships with password replacement active and the domain credential exfiltration commented out. That is what the researcher chose not to ship. -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQRf0htP5+SjynlxywneZjl4jgkQJgUCaeUiawAKCRDeZjl4jgkQ JtOUAP94bqdzHfst+gvgPLAJivyG/k6cB9nuvC4vypZmgljyRQEAyJ7jF/VZ9EHl feVi8UlmYhL31TMVqmGbQtG8NQSo6QY= =VBDT -----END PGP SIGNATURE-----