NtSetCachedSigningLevel
Schreibt ein von Code Integrity gecachetes Signaturlevel-Ergebnis in ein NTFS-Extended-Attribute der Zieldatei.
Prototyp
NTSTATUS NtSetCachedSigningLevel( ULONG Flags, SE_SIGNING_LEVEL InputSigningLevel, PHANDLE SourceFiles, ULONG SourceFileCount, HANDLE TargetFile );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| Flags | ULONG | in | Bitmaske zur Cache-Steuerung; 0x4 erzwingt Neubewertung, 0x8 überspringt die Vertrauensprüfung. |
| InputSigningLevel | SE_SIGNING_LEVEL | in | Angefordertes Signaturlevel zum Stempeln: 0=Unchecked, 4=Authenticode, 6=Store, 8=Antimalware, 12=Microsoft, 14=Windows. |
| SourceFiles | PHANDLE | in | Array von Datei-Handles, deren gecachetes Signaturlevel geerbt werden soll; meist NULL. |
| SourceFileCount | ULONG | in | Anzahl der Einträge in SourceFiles; 0, wenn SourceFiles NULL ist. |
| TargetFile | HANDLE | in | Handle auf die Datei, deren Extended-Attribute $Kernel.Purge.ESBCache geschrieben wird. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x16E | win10-1507 |
| Win10 1607 | 0x176 | win10-1607 |
| Win10 1703 | 0x17C | win10-1703 |
| Win10 1709 | 0x17F | win10-1709 |
| Win10 1803 | 0x181 | win10-1803 |
| Win10 1809 | 0x182 | win10-1809 |
| Win10 1903 | 0x183 | win10-1903 |
| Win10 1909 | 0x183 | win10-1909 |
| Win10 2004 | 0x189 | win10-2004 |
| Win10 20H2 | 0x189 | win10-20h2 |
| Win10 21H1 | 0x189 | win10-21h1 |
| Win10 21H2 | 0x18B | win10-21h2 |
| Win10 22H2 | 0x18B | win10-22h2 |
| Win11 21H2 | 0x193 | win11-21h2 |
| Win11 22H2 | 0x196 | win11-22h2 |
| Win11 23H2 | 0x196 | win11-23h2 |
| Win11 24H2 | 0x198 | win11-24h2 |
| Server 2016 | 0x176 | winserver-2016 |
| Server 2019 | 0x182 | winserver-2019 |
| Server 2022 | 0x191 | winserver-2022 |
| Server 2025 | 0x198 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 98 01 00 00 mov eax, 0x198 F6 04 25 08 03 FE 7F 01 test byte ptr [0x7FFE0308], 1 75 03 jne short +3 0F 05 syscall C3 ret CD 2E int 2Eh C3 ret
Undokumentierte Hinweise
Teil der **Code-Integrity (CI)**-Infrastruktur, eingeführt mit Windows 10. CI hält ein EA-gecachtes Signaturverdikt auf jeder ausführbaren Datei, damit nachfolgende Image-Loads den teuren Authenticode-Hash-Walk vermeiden. Der Cache lebt im NTFS-Extended-Attribute `$Kernel.Purge.ESBCache` (das EA ist versteckt — `fsutil file queryea` zeigt es standardmäßig nicht; CI nutzt ein privates FSCTL). `NtSetCachedSigningLevel` ist der *Writer* — er bittet CI (CI-Modul aus `ci.dll` innerhalb von ntoskrnl), ein Signaturlevel zu berechnen oder zu akzeptieren und ins EA zu stempeln. Das Pendant `NtGetCachedSigningLevel` ist der *Reader*. Die privilegierten Levels (8 Antimalware, 12 Microsoft, 14 Windows) erfordern `SeTcbPrivilege` *und* dass der aufrufende Prozess selbst auf gleichem oder höherem CI-Level läuft — ein normaler User-Mode-Prozess kann also nicht einfach `SE_SIGNING_LEVEL_WINDOWS` stempeln. Der Aufruf wurde für Forscher attraktiv, weil *ein Teil* seiner Parameter-Validierung in der CI-Policy lebt, nicht in `Nt*` selbst, und historische Policy-Resolution-Bugs genutzt wurden, um CI zur Annahme unsignierter Dateien zu zwingen.
Häufige Malware-Nutzung
Kein Commodity-Malware-Syscall, aber ein **wiederkehrender Forschungsvektor für Sandbox-Escapes und CIG-(Code-Integrity-Guard)-Bypasses**. James Forshaw (Project Zero) dokumentierte mehrere Pfade, auf denen `NtSetCachedSigningLevel` dazu gezwungen werden konnte, eine angreiferkontrollierte DLL mit einem Cache-Eintrag auf `Microsoft`-Level zu stempeln — ausreichend, um die DLL an einem Prozess vorbeizuschmuggeln, der mit `ProcessSignaturePolicy = MicrosoftSignedOnly` geschützt ist. Edge-/Chrome-Sandbox-Escape-PoCs (Ära CVE-2020-0938 und Folgen) nutzten dies, um nach RCE im Renderer Angreifercode in den Browser-Broker zu laden. Die Print-Spooler-/`Win32k`-Bugklassen, die App Container kreuzen, berühren ebenfalls diesen Pfad. **Echte Malware ruft dies praktisch nie auf** — Missbrauch erscheint in Red-Team-/Research-Code, **PPLFault** (Gabriel Landau, 2022) und Process-Mitigation-Policy-Bypasses. Sieht man einen nicht-Microsoft-signierten Prozess, der `NtSetCachedSigningLevel(SE_SIGNING_LEVEL_WINDOWS)` aufruft, beobachtet man Exploitation, kein normales Verhalten.
Erkennungsmöglichkeiten
Der ETW-Provider `Microsoft-Windows-CodeIntegrity` (GUID `{4ee76bd8-3cf4-44a0-a0ac-3937643e37a3}`) emittiert Event 3023 / 3033 bei Cache-Schreibvorgängen, inklusive angefordertem Signaturlevel und Image-Pfad des aufrufenden Prozesses. CI loggt zudem in den Application-&-Services-Kanal `CodeIntegrity-Operational` — Events 3033 (Datei blockiert) und 3089 (Signaturinfo) referenzieren Cache-Lookups. Das stärkste Signal: jeder Prozess, dessen eigenes Image nicht auf `SE_SIGNING_LEVEL_MICROSOFT` oder höher signiert ist, ruft `NtSetCachedSigningLevel` überhaupt — und besonders mit `InputSigningLevel >= 8`. Die Defender-ASR-Regel `Block credential stealing from the Windows local security authority subsystem` erwischt indirekt einige Missbrauchspfade, weil der Post-Exploitation-Schritt meist in einen geschützten Prozess injiziert.
Direkte Syscall-Beispiele
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtSetCachedSigningLevel (SSN 0x198 on Win11 24H2 / Server 2025)
NtSetCachedSigningLevel PROC
mov r10, rcx ; syscall convention
mov eax, 198h ; SSN — drifts; resolve dynamically for portability
syscall
ret
NtSetCachedSigningLevel ENDPcCIG-bypass research pattern
// Forshaw-style CI cache stamping. Requires the calling process to already
// run at SE_SIGNING_LEVEL_MICROSOFT or above — the abuse vector is *getting
// that level* via a policy resolution bug, not this call itself.
#define SE_SIGNING_LEVEL_UNCHECKED 0
#define SE_SIGNING_LEVEL_AUTHENTICODE 4
#define SE_SIGNING_LEVEL_STORE 6
#define SE_SIGNING_LEVEL_ANTIMALWARE 8
#define SE_SIGNING_LEVEL_MICROSOFT 12
#define SE_SIGNING_LEVEL_WINDOWS 14
HANDLE hFile = /* opened with FILE_READ_DATA | FILE_WRITE_EA */;
NTSTATUS st = NtSetCachedSigningLevel(
/* Flags */ 0x4, // force recompute
/* InputSigningLevel */ SE_SIGNING_LEVEL_MICROSOFT,
/* SourceFiles */ NULL,
/* SourceFileCount */ 0,
/* TargetFile */ hFile);cRead the resulting EA with NtGetCachedSigningLevel
// Verify the cache stamp succeeded by reading it back.
ULONG flags = 0, level = 0, thumbAlg = 0, thumbSize = 32;
BYTE thumb[32];
LARGE_INTEGER lastBlackList = { 0 }, lastTimeStamp = { 0 };
NTSTATUS st = NtGetCachedSigningLevel(
hFile, &flags, (PSE_SIGNING_LEVEL)&level,
thumb, &thumbSize, &thumbAlg,
&lastBlackList, &lastTimeStamp);
// level should now equal what NtSetCachedSigningLevel stamped, e.g. 12 (Microsoft).MITRE ATT&CK-Mappings
Last verified: 2026-05-20