LegacyHive Exposed: Dissecting Chaotic Eclipse’s Unpatched Windows Registry Hive-Loading Zero-Day and the Adversarial Pattern Behind Nine Back-to-Back Uncoordinated Disclosures

Hours after Microsoft shipped its July 2026 cumulative updates, a persona that has spent the past year embarrassing MSRC dropped another one. No CVE. No advisory. No patch. Just a GitHub repo, a stripped-down proof of concept, and a name: LegacyHive. It is the tenth uncoordinated Windows release from Chaotic Eclipse / NightmareEclipse, and the throughline connecting the nine that share its lineage is no longer subtle. Somebody is assembling a modular post-exploitation kit in public, one primitive at a time, and daring defenders to keep up.


LegacyHive drops on Patch Tuesday, on purpose

Timing is the message. Patch Tuesday for July 2026 landed on the 14th. LegacyHive appeared within hours, which means defenders got a full 28-day wait for the earliest possible fix before they even had a chance to triage it. That is not an accident of the calendar. Releasing immediately after the monthly train leaves the maximum possible exposure window, and the persona knows it.

The vulnerability is a local privilege-escalation and cross-user data-access bug in the Windows User Profile Service, ProfSvc, the SYSTEM-hosted service inside svchost.exe that loads and unloads per-user registry hives during logon and logoff. Independent researchers, including Kevin Beaumont and Will Dormann, confirmed the primitive works. Dormann’s summary was blunt: a non-admin user can modify the classes registry hive of an administrator, which he called “a pretty powerful primitive.” His throwaway demo was associating .txt files with calc.exe. His warning was the part worth reading twice: “Clever attackers or people who want to accomplish something will easily be able to figure out how to do things that are more interesting and/or don’t even require user interaction.”

Because this is live and unpatched, I am going to walk the mechanics all the way down to the kernel objects involved, and I am going to show you the shape of each primitive so your detections are grounded in reality. What I am not going to do is hand you an assembled, copy-paste weaponized chain that drops SYSTEM on a machine that has no fix available today. Everything short of that final trigger loop is on the table, because you cannot defend a bug class you refuse to understand.

Windows registry hive architecture, from a defender’s chair

A hive is a file on disk that gets mapped into a branch of the registry tree. When you read HKEY_CURRENT_USER, you are reading NTUSER.DAT from the profile directory. When you read HKCU\Software\Classes, you are reading UsrClass.dat, the per-user classes hive that stores COM object registrations, file-type associations, and shell extension data. There are machine hives too (SYSTEM, SOFTWARE, SAM, SECURITY), but LegacyHive is about the per-user ones.

Under the hood, a mounted hive lives in the kernel as a pair of structures. _HHIVE is the base container holding the hive type, the allocation map, and the storage arrays. _CMHIVE extends it into the kernel’s full in-memory representation: it carries a HiveList LIST_ENTRY that links it into the global hive list, a FileObject pointer to the section-backed file, and a HiveLock push lock. Individual keys are _CM_KEY_NODE cells (signature 0x6B6E, the ASCII nk), and values are _CM_KEY_VALUE cells (signature 0x6B76, vk). On modern Windows, most hives are mapped into the address space of the thin Registry process you can spot in Task Manager, using sections rather than pool.

The call chain that matters is short:

// User-mode entry the profile service walks when mounting a per-user hive
RegLoadAppKey(...)      // advapi32 wrapper
  -> NtLoadKeyEx(...)   // ntdll syscall stub  (also NtLoadKey / NtLoadKey2)
    -> CmLoadKey(...)   // ntoskrnl: validates the file, builds _CMHIVE,
                        // links into the global hive list

The introduction of Application Hives back in Vista is why this surface is so exposed. It let unprivileged processes reach kernel code that used to be reserved for system services, opening up hive file operations, binary-level hive parsing, and the validation logic in CmpCheckRegistry and its subfunctions. NtLoadKey validates the contents of the file it is told to open. What it does not do, and what LegacyHive abuses, is re-verify which file it was told to open after path resolution has been tampered with.

The exploit primitive: hive-load redirection, step by step

The bug is a TOCTOU (time-of-check to time-of-use) race, but calling it “a race” undersells it. The PoC turns the race into a deterministic pause-and-swap using an opportunistic lock. There is no luck involved. Here is the five-stage chain.

Stage 1: poison the path value

ProfSvc resolves where a user’s UsrClass.dat lives by reading a shell-folder value out of that user’s own NTUSER.DAT:

HKCU\Software\Microsoft\Windows\CurrentVersion\
    Explorer\User Shell Folders\Local AppData

The attacker controls a secondary local account (helperuser in a lab), so they modify that account’s offline NTUSER.DAT and repoint Local AppData at a native NT Object Manager path they control instead of the normal %USERPROFILE%\AppData\Local. This is the foothold into ProfSvc’s path resolution.

Stage 2: build the Object Manager redirect

The redirection happens in the kernel object namespace, not the Win32 filesystem namespace, using two undocumented ntdll routines. NtCreateDirectoryObjectEx creates a directory object under \BaseNamedObjects\Restricted, and NtCreateSymbolicLinkObject plants a symlink inside it. These are not CreateSymbolicLink from the Win32 layer; they operate one level below, on the object manager itself, which is exactly why they slip past the assumptions in the file-path checks.

// Illustrative: object-manager directory + symlink, not the Win32 API.
// Shows the namespace being abused, not an assembled exploit.
UNICODE_STRING objName;
OBJECT_ATTRIBUTES objAttr;
HANDLE hDir = NULL;

RtlInitUnicodeString(&objName, L"\\BaseNamedObjects\\Restricted\\HiveSpoofDir");
InitializeObjectAttributes(&objAttr, &objName, OBJ_CASE_INSENSITIVE, NULL, NULL);

NtCreateDirectoryObjectEx(&hDir, DIRECTORY_ALL_ACCESS, &objAttr, NULL, FALSE);
// A symbolic link named to match the resolved leaf is then created inside hDir,
// initially pointing at a benign decoy so nothing breaks before the swap.

Stage 3: freeze ProfSvc mid-open with an oplock

Instead of gambling on timing, the PoC opens the decoy hive and requests a batch opportunistic lock:

// The oplock is the deterministic TOCTOU controller.
// When ProfSvc opens the same file, its open blocks and this handle is notified.
DeviceIoControl(hDecoy,
                FSCTL_REQUEST_BATCH_OPLOCK,
                NULL, 0, NULL, 0,
                &bytesReturned, &overlapped);
// Execution waits here for the break notification before touching the symlink.

When any other process, meaning ProfSvc, tries to open that file, the open pauses and the oplock holder gets notified. That is the frozen instant the whole attack depends on.

Stage 4: trigger the load without a full logon

You do not need to actually log the secondary user in interactively. CreateProcessWithLogonW with LOGON_WITH_PROFILE forces ProfSvc to mount that user’s hive, and CREATE_SUSPENDED keeps the spawned process frozen so the attacker owns the exact moment ProfSvc reads the poisoned path.

// Cross-SID spawn that makes SYSTEM's ProfSvc do the hive load for you.
CreateProcessWithLogonW(
    L"helperuser", NULL, L"<password>",
    LOGON_WITH_PROFILE,                         // forces the hive mount
    NULL, L"C:\\Windows\\System32\\notepad.exe",
    CREATE_SUSPENDED,                           // freeze the trigger process
    NULL, NULL, &si, &pi);

Stage 5: swap on the break

When the oplock break fires, ProfSvc is paused mid-open. The symlink is repointed from the decoy at the target administrator’s UsrClass.dat, the decoy handle is released, and ProfSvc resumes, follows the new link, and calls the NtLoadKey family on the admin’s hive. That hive is now mounted under the low-privileged user’s registry namespace. I am deliberately not publishing the assembled break-handler-to-swap-to-signal loop, because that ordered sequence is the difference between understanding the bug and weaponizing it against an unpatched box.

The net result is cross-user hive redirection: a target user’s classes hive, sitting inside a low-privileged user’s registry tree, readable and writable by that low-privileged user.

Flow diagram showing the five-stage LegacyHive attack chain: poisoning the Local AppData registry value, planting an Object Manager symlink, freezing ProfSvc with an oplock, triggering a cross-SID process creation, and swapping the symlink so NtLoadKey mounts the administrator's hive.
The five deterministic stages of LegacyHive – an oplock replaces timing luck, turning a TOCTOU race into a controlled pause-and-swap that coerces SYSTEM’s ProfSvc into mounting a foreign hive.

“Any hive”: why the public PoC understates the threat

Read the persona’s own notes and you learn the published PoC is a floor, not a ceiling. The researcher stripped it down on purpose. Two admissions matter for how you model risk:

  • The original exploit did not require the secondary account’s credentials. The CreateProcessWithLogonW-with-a-password step in the public version is an artificial handrail, not an architectural constraint.
  • It was never limited to UsrClass.dat. In their words, “Any hive could be loaded using this vulnerability, but you would need some brain cells to make the PoC do it.”

Do not build your threat model around the credential requirement or the classes-hive scope. Treat the primitive as: an authenticated local user can coerce SYSTEM into mounting an arbitrary hive into a namespace they control. Once you frame it that way, NTUSER.DAT of an administrator, application credential caches, and auto-run configuration all come into scope, not just file associations.

Nested Russian dolls cracked open revealing a glowing skeleton key at the center, symbolizing that the public proof-of-concept understates the true scope of the LegacyHive vulnerability.
The published PoC is a floor, not a ceiling – the credential requirement is artificial and the primitive reaches any hive, not just UsrClass.dat.

What you can do with a mounted foreign hive

The .txt-to-calc.exe demo is intentionally boring. The real value is that you now have write access to a privileged user’s live configuration surface:

Abuse pathMechanismWhy it matters
COM hijackingWrite a malicious CLSID / InprocServer32 into the admin’s mounted HKCU\Software\ClassesNext time the admin instantiates that COM object, your DLL loads in their context
File-type handler swapRepoint HKCU\Software\Classes\.ext\shell\open\commandAdmin opens a document, your command runs. Low interaction, high payoff
Persistence in admin namespaceAdd entries to the admin’s run/load keys reachable through the mounted hiveSurvives across the admin’s sessions without touching machine-wide keys
Forensic harvestRead shellbags, MRU lists, typed paths, Explorer history from the target’s UsrClass.datReconnaissance and lateral targeting without dropping tools
# Lab-only demonstration of the primitive once a foreign classes hive is mounted.
# This proves write access into a privileged user's namespace; it is not the exploit.
reg add "HKCU\Software\Classes\.txt\shell\open\command" /ve /d "cmd.exe /c whoami" /f

The reason this is dangerous is not a remote break-in. Exploitation requires you already have code execution on the box, valid access, and a second local profile to mount. That makes it useless for internet-scale spraying and extremely useful for the place attackers actually live: shared desktops, jump boxes, RDP session hosts, terminal servers, developer workstations, and CI runners. Any host where a standard user shares silicon with an administrator profile is the ideal target. This is a lateral and local weapon, and it is a good one.

The nine-exploit campaign: reading the toolkit being built

LegacyHive did not appear in a vacuum. It is the latest in a recognizable throughline the community has been tracking, and looking at the set changes how you should read it. The naming drifts through themes (color-plus-noun, then plasma, then planet, then hive), and the targets cluster around exactly the components an operator would want to neutralize before and during a post-exploitation engagement: Windows Defender, BitLocker, and the Text Services Framework host ctfmon.exe.

ReleaseRead on the target surfaceClass of primitiveRole in a post-ex kit
BlueHammerKernel / driver-adjacent LPEMemory corruption to elevationInitial local elevation
UnDefendWindows DefenderTamper / capability disableBlind the endpoint
RedSunToken / process abuseAccess token manipulationContext pivoting
RoguePlanetService abusePath or DLL hijackPersistence + elevation
YellowKeyBitLockerKey handling / bypassData-at-rest defeat
GreatXMLParser surfaceInjection / entity abuseConfig or policy tamper
GreenPlasmaKernel object abuseHandle / object primitiveElevation building block
MiniPlasmaTrimmed variant of the aboveSame class, smaller footprintPortable variant
LegacyHiveProfSvc / registry hivesTOCTOU hive redirectionCross-user config control

Individually each is “just another LPE.” Collectively they read like a deliberately curated kit: get local, disable Defender, defeat BitLocker, manipulate identity and services, and now reach into any user’s registry configuration to plant execution and persistence. When the same author ships tools that each solve a different stage of the intrusion lifecycle, and each targets a defensive control specifically, you are not watching a hobbyist find bugs. You are watching a capability being packaged.

The accelerating cadence is the part that should worry you more than any single bug. Nine-plus back-to-back drops, each timed against the patch cycle, means the mean time from “public primitive exists” to “it is in your environment” is collapsing. The persona’s dispute with MSRC over disclosure ethics is real, but for defenders it is noise. The operational reality is that the patch pipeline is no longer the first line of defense for this class of releases, because the release lands before the patch can.

Hierarchy diagram grouping all nine Chaotic Eclipse releases into three functional pillars: local elevation primitives, defense-defeat tools targeting Defender and BitLocker, and persistence and configuration-control tools including LegacyHive.
Read as a set, the nine releases form a deliberate modular kit – each tool solving a distinct phase of post-exploitation, from initial elevation to blinding defenses to planting durable access.

Detection is the defense

There is no fix for LegacyHive today, which means detection is not a supplement to patching here, it is your control. The good news: the chain is loud if you are listening in the right places. The full sequence involves cross-SID process creation with a profile load, a suspended benign binary, and hive redirection all clustered within seconds of NTUSER.DAT modification, UsrClass.dat access, and Object Manager path creation under \BaseNamedObjects\Restricted. That combination has no legitimate explanation.

Windows Security audit events

Event IDWhat to alert on
4657Modification of the User Shell Folders\Local AppData value by anything other than known profile binaries. Turn on Object Access auditing for that key.
4624 / 4648Logon Type 9 (NewCredentials) sessions created by a standard user for a different account, from a source process that is not runas.exe or explorer.exe. This is the CreateProcessWithLogonW signature.
4798 / 4799Local account and group enumeration, the recon that precedes picking an admin target.

Sysmon coverage

Sysmon EventSignal
13 (Value Set)Local AppData set to any path outside C:\Users\<user>\AppData\Local. This is your highest-fidelity single indicator.
12 (Key create/delete)Unexpected images touching the User Shell Folders key.
14 (Key/Value rename)Renames near hive-load activity.
11 (FileCreate)New .dat files staged in C:\ root or non-profile directories (decoy hives).
1 (ProcessCreate)Medium-integrity, untrusted parent invoking cross-SID process creation; svchost.exe doing anything unusual around the same window.
10 (ProcessAccess)A non-SYSTEM process opening the ProfSvc svchost.exe with PROCESS_VM_READ.

ETW providers worth a dedicated consumer

  • Microsoft-Windows-Kernel-Registry {70EB4F03-C1DE-4F73-A051-33D13D5413BD}: emits NtLoadKey operations with the key path. Alert on hive loads resolving to a profile directory that does not match the loading SID.
  • Microsoft-Windows-User Profile Service {DB00DFB6-29F9-4A9C-9B3B-1F4F9E7D9770}: watch Event IDs 1500/1501 (profile load start/complete) for accounts that are not interactively logging on, and 1552 (hive locked by another process), which is abnormal during the attack window.
  • Microsoft-Windows-Kernel-File {EDD08927-9CC4-4E65-B970-C2560FB5C289}: .dat opens outside normal profile directories.

Note the blind spot: Sysmon does not natively log Object Manager directory or symlink creation. Until you deploy a custom ETW consumer, hunt with WinObj and Process Monitor filtered on REPARSE and NAME NOT FOUND results under \BaseNamedObjects\Restricted.

Behavioral rules that actually fire

The strongest detection is temporal correlation, not any single event:

  1. High fidelity: Sysmon 13 sets Local AppData to a non-%USERPROFILE% path, AND within 60 seconds Sysmon 1 shows the same process calling CreateProcessWithLogonW. That two-event pair is the core of the attack.
  2. Medium fidelity hunt: the ProfSvc instance of svchost.exe accessing a UsrClass.dat located in a directory whose SID does not match the account being loaded, a hive file in the wrong user’s folder.
  3. Namespace anomaly: a mounted classes hive containing COM ProgIDs or file associations inconsistent with that user’s installed software, compared against a baseline.

Sigma starting point

title: Suspicious Local AppData Registry Value Modification (LegacyHive Pattern)
status: experimental
description: >
  Local AppData User Shell Folder value repointed to a non-standard path,
  the prerequisite step for LegacyHive hive-load redirection against ProfSvc.
tags:
  - attack.privilege_escalation
  - attack.t1112
  - attack.t1546.015
logsource:
  product: windows
  category: registry_set
detection:
  selection:
    EventID: 13
    TargetObject|contains: '\Explorer\User Shell Folders\Local AppData'
    Details|not|startswith:
      - 'C:\Users\'
      - '%USERPROFILE%'
  filter_legit:
    Image|endswith:
      - '\userinit.exe'
      - '\winlogon.exe'
  condition: selection and not filter_legit
falsepositives:
  - GPO-driven profile redirection (validate against your baseline)
  - Roaming profile solutions
level: high

KQL for Sentinel / Defender XDR

DeviceRegistryEvents
| where RegistryKey has @"\Explorer\User Shell Folders"
| where RegistryValueName == "Local AppData"
| where RegistryValueData !startswith @"C:\Users\"
| join kind=inner (
    DeviceProcessEvents
    | where ProcessCommandLine has_any ("CreateProcessWithLogon","runas /profile")
        or ActionType == "CreateProcessWithLogon"
    | project DeviceId, InitiatingProcessId, ProcTime = Timestamp
) on DeviceId
| where abs(datetime_diff('second', Timestamp, ProcTime)) <= 60
| project Timestamp, DeviceId, RegistryValueData, InitiatingProcessFileName

ATT&CK mapping and hardening

The chain maps cleanly: T1068 (Exploitation for Privilege Escalation), T1134 (Access Token Manipulation) for the cross-SID logon, T1112 (Modify Registry) for the value poison, T1546.015 (COM Hijacking) and T1546.001 (Change Default File Association) for the payoff, and T1547.001 (Registry Run Keys) for persistence.

For hardening while you wait for a patch: minimize multi-user footprints on high-value hosts, keep administrators off shared session hosts entirely where you can, enforce that admin work happens on dedicated Privileged Access Workstations, audit which machines carry stale secondary local profiles (the attack needs one), and turn on registry object-access auditing for the shell-folder keys today. None of this fixes the bug. All of it shrinks the blast radius and lights up the telemetry you need.

An industrial siren casting a beam of light across tangled wiring, symbolizing active detection as the primary defense when no patch is available.
With no patch available, detection is the control – temporal correlation of a poisoned registry value and a cross-SID suspended spawn is the highest-fidelity signal in the chain.

The bigger problem is the cadence, not the bug

LegacyHive is a clean, well-engineered local primitive: a deterministic TOCTOU against ProfSvc’s hive path resolution, weaponized with an oplock instead of a coin flip, reaching straight into any user’s registry configuration. On its own it is serious but survivable with good telemetry and disciplined host segregation. The reason to take it seriously beyond its own merits is what it represents. Ten releases in, the pattern is a curated toolkit aimed at the exact defensive controls you rely on, dropped on a schedule engineered to maximize the gap between disclosure and remediation. When an adversary industrializes the release process, defenders have to industrialize detection to match, because the patch is going to keep arriving second.

Key takeaways

  • LegacyHive is an unpatched local/lateral privilege and cross-user data-access bug in ProfSvc; it needs prior code execution and a second local profile, so treat it as a post-compromise weapon on shared and multi-user hosts, not a remote threat.
  • The mechanism is a deterministic TOCTOU: poison Local AppData, redirect via Object Manager symlinks under \BaseNamedObjects\Restricted, freeze ProfSvc with an FSCTL_REQUEST_BATCH_OPLOCK, trigger the load with CreateProcessWithLogonW + CREATE_SUSPENDED, and swap the link so NtLoadKey mounts the wrong hive.
  • The public PoC is a floor. The credential requirement is artificial and the scope is not limited to UsrClass.dat. Model it as “arbitrary hive mounted into an attacker-controlled namespace.”
  • There is no patch, so detection is the control. The two-event chain (non-standard Local AppData value plus a cross-SID suspended spawn within a minute) is your highest-fidelity signal; back it with ETW on Kernel-Registry and the User Profile Service.
  • The nine predecessors matter more than any single drop. This is a modular kit targeting Defender, BitLocker, and CTFMON, released on a cadence built to outrun Patch Tuesday. Build your program to detect fast, not to patch fast, because for this actor the patch is always late.

Related Tutorials

References