Most user-mode persistence on Windows shows up as a registry write. EDR vendors hook CmRegisterCallbackEx and watch the predictable spots: Run keys, AppInit_DLLs, IFEO. The assumption is that getting persistence into HKCU requires touching a registry API, and that touch is observable.
NTUSER.MAN persistence skips the API entirely. The keys still land in HKCU, but they arrive via a file drop, not a RegSetValueEx. Credit for the original writeup goes to Rad Kawar at DeceptIQ (Registry Writes Without Registry Callbacks). This post focuses on the defender side: what's actually in scope to hunt, plus a starter Sigma rule.
How Windows loads HKCU
At logon, the profile loader looks in the target user's profile directory (C:\Users\<name>\) for the hive that becomes HKEY_CURRENT_USER. It checks two filenames in order:
NTUSER.MANNTUSER.DAT
If NTUSER.MAN is present, it wins. The hive is mapped as the user's HKCU for the session. This is the legitimate "mandatory profile" feature, originally meant for kiosks and shared workstations. Changes made during the session live in memory only and are discarded at logout.
The relevant detail for this post: the hive contents are loaded by mounting the file. No call to the registry API surface, so EDR callbacks attached to those APIs see exactly nothing for whatever keys are inside.
The blind spot
Registry callbacks are the standard mechanism for HKCU telemetry. If your EDR's persistence detections rely on observing a RegSetValue for HKCU\Software\Microsoft\Windows\CurrentVersion\Run, those detections never fire when the value enters the world via NTUSER.MAN. The hive load itself can be logged (Sysmon Event 11 for the file create, plus kernel hive-load events), but the contents of the hive don't reach the registry-callback channel.
In practice: an attacker with regular user privileges on a machine can drop a crafted hive into their own profile directory, log off, and on next logon their persistence is live with no registry-write trail.
Walkthrough
Worth reproducing in a lab once. The shape:
-
Export the target user's HKCU as a
.regfile. No elevation needed; a user can export their own hive.export-hive.cmd reg export HKCU C:\Users\Public\hkcu.reg /y -
Edit the
.regfile to add Run keys, or whatever persistence flavor you're testing. -
Convert the
.regback into a binary hive. The deceptiq post points at HiveSwarming.python-registryand libregf can do similar work programmatically. -
Write the resulting binary hive to
C:\Users\<target>\NTUSER.MAN. The user themselves has write permission on their own profile directory; an attacker who already controls the user account does too. -
Log the user off. The active hive is locked while the session is open, so the swap only takes effect on next logon.
-
On next logon, NTUSER.MAN wins the priority check and becomes HKCU. Anything under
Runexecutes.
This is post-exploitation. It assumes the attacker is already a regular-user-on-the-host. The point isn't initial access, it's that the persistence write step is silent on the registry-callback channel.
What defenders should hunt
The technique relies on a file that has no business existing on a modern machine. Mandatory profiles are a legacy feature. Outside of specifically deployed environments (some VDI, some kiosk setups, niche AD configurations), NTUSER.MAN should not appear in user profile directories.
Concrete hunt items:
- File creation of
NTUSER.MANin any subdirectory ofC:\Users\outside legitimate mandatory-profile deployments. - AD
profilePathattribute audits. Legitimate mandatory profiles are configured at the domain level. Inventory once, alert on drift. - Roaming profile shares (e.g.
\\server\share\profile.v6\) sprouting unexpected.MANfiles. - Hive load events (Microsoft-Windows-Kernel-General Event ID 16) for hive paths ending in
.MAN, correlated with the file-create. - Logon events correlated with new
NTUSER.MANfiles in the logging-on user's profile within a short window before the logon.
If your environment has zero legitimate mandatory profiles (most don't), file-create is a single-stage detection with near-zero false positives. If you do have legitimate ones, allowlist by path once and treat anything outside the list as high-priority.
A starter Sigma rule
title: NTUSER.MAN created in user profile directory
id: 7e9a7c2b-3d1e-4b8a-9d2e-1f4b8c0d2a91
status: experimental
description: |
Detects creation of NTUSER.MAN inside a user profile directory.
Mandatory profile hives are rare in modern environments; their
appearance can indicate HKCU persistence that bypasses registry
callback monitoring.
references:
- https://deceptiq.com/blog/ntuser-man-registry-persistence
author: 0xFuzz
date: 2026/04/25
logsource:
product: windows
category: file_event
detection:
selection:
TargetFilename|endswith: '\NTUSER.MAN'
TargetFilename|contains: '\Users\'
filter_system_image:
Image|contains:
- '\System32\'
- '\SysWOW64\'
condition: selection and not filter_system_image
falsepositives:
- Legitimate mandatory profile deployment (rare; allowlist by path)
- Admin tooling copying profiles during migration
level: highDrop that into your detection-as-code pipeline, allowlist legitimate mandatory-profile paths, and it's a single-event high-fidelity catch.
What to test in the lab
Two questions worth answering once with your real EDR in a real test environment:
- Does the EDR fire on the file create of
NTUSER.MAN? - After logon, when the Run-key payload executes, does the EDR fire on the child process spawned from
userinit.exeor the user's shell?
If both answers are yes, you're in good shape. If only (2), you have detection but it's lagged by a logon cycle. If neither, the Sigma rule above (or its equivalent in your stack) is the patch.
References
- DeceptIQ: Registry Writes Without Registry Callbacks (Rad Kawar, 2025). Original research, deeper dive on the kernel-callback mechanics.
- Microsoft Learn: mandatory user profiles (search the current docs path; Microsoft moves them).
python-registryandlibregffor hive parsing in lab work.