> Windows Syscalls
ntoskrnl.exeT1106T1033

NtPrivilegeCheck

Prüft, ob die in einem PRIVILEGE_SET genannten Privilegien in einem Impersonation-Token aktiviert sind.

Prototyp

NTSTATUS NtPrivilegeCheck(
  HANDLE         ClientToken,
  PPRIVILEGE_SET RequiredPrivileges,
  PBOOLEAN       Result
);

Argumente

NameTypeDirDescription
ClientTokenHANDLEinHandle auf ein Impersonation-Token (TOKEN_QUERY). Primary-Tokens werden mit STATUS_NO_IMPERSONATION_TOKEN abgewiesen.
RequiredPrivilegesPPRIVILEGE_SETin/outZu prüfende Privilegien. Das Flag PRIVILEGE_SET_ALL_NECESSARY verlangt alle Einträge; sonst genügt eine einzige Übereinstimmung. Jedes LUID_AND_ATTRIBUTES erhält beim Rückkehren sein Attributes-Feld mit SE_PRIVILEGE_USED_FOR_ACCESS aktualisiert.
ResultPBOOLEANoutErhält TRUE, falls die Prüfung erfolgreich ist, sonst FALSE. Der NTSTATUS der Funktion meldet nur Fehler des Aufrufs selbst.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x122win10-1507
Win10 16070x128win10-1607
Win10 17030x12Cwin10-1703
Win10 17090x12Ewin10-1709
Win10 18030x130win10-1803
Win10 18090x131win10-1809
Win10 19030x132win10-1903
Win10 19090x132win10-1909
Win10 20040x137win10-2004
Win10 20H20x137win10-20h2
Win10 21H10x137win10-21h1
Win10 21H20x138win10-21h2
Win10 22H20x138win10-22h2
Win11 21H20x13Ewin11-21h2
Win11 22H20x140win11-22h2
Win11 23H20x140win11-23h2
Win11 24H20x142win11-24h2
Server 20160x128winserver-2016
Server 20190x131winserver-2019
Server 20220x13Dwinserver-2022
Server 20250x142winserver-2025

Kernel-Modul

ntoskrnl.exeNtPrivilegeCheck

Verwandte APIs

PrivilegeCheckGetTokenInformationAdjustTokenPrivilegesNtQueryInformationTokenNtAdjustPrivilegesTokenNtAccessCheck

Syscall-Stub

4C 8B D1            mov r10, rcx
B8 42 01 00 00      mov eax, 0x142
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

NtPrivilegeCheck ist das kernelseitige Gegenstück zu AccessCheck: sie fragt „hat dieses Impersonation-Token diese Privilegien aktuell *aktiviert*?". Das übergebene PRIVILEGE_SET trägt ein Control-Feld — PRIVILEGE_SET_ALL_NECESSARY verlangt alle aufgeführten Privilegien, sonst gibt der Kernel TRUE zurück, sobald eines passt. Beim Rückkehren ist in jedem LUID_AND_ATTRIBUTES SE_PRIVILEGE_USED_FOR_ACCESS für die Privilegien gesetzt, die den Check erfüllt haben — das speist die Privilege-Use-Auditierung. Strikt ein *aktiviert*-Check: vorhandene, aber deaktivierte Privilegien gelten als fehlend.

Häufige Malware-Nutzung

Angreifer rufen NtPrivilegeCheck nicht wirklich offensiv auf — sie gewährt nichts, sondern berichtet nur. In Malware taucht sie auf, wenn detektionsbewusste Payloads ihr eigenes Token prüfen, bevor sie eine privilegierte Operation versuchen (z. B. SeDebugPrivilege wirklich aktiv ist, bevor LSASS geöffnet wird), um sonst leise zurückzuziehen. Ehrliche Einschätzung: ein Syscall für legitime Dienste und Verteidiger; als offensives Signal mit geringer Aussagekraft zu behandeln.

Erkennungs­möglichkeiten

NtPrivilegeCheck emittiert selbst keine Privilege-Use-Audit-Events (4674) — diese feuern, wenn eine privilegierte Operation tatsächlich ausgeführt wird. Der nützlichste Blickwinkel liegt im EDR: ntdll!NtPrivilegeCheck hooken und korrelieren „Prozess X hat wiederholt SeDebugPrivilege geprüft, ohne es zu besitzen" (Aufklärung / Capability-Discovery) oder „Prozess X hat SeImpersonatePrivilege geprüft, unmittelbar bevor er ein Token gefälscht hat". Auf einem Domain Controller sind wiederholte Prüfungen auf SeBackupPrivilege/SeRestorePrivilege durch Nicht-Backup-Software auffällig. Allein schwaches Signal; in Ketten brauchbar.

Direkte Syscall-Beispiele

asmx64 direct stub (Win11 24H2 SSN)

; Direct syscall stub for NtPrivilegeCheck (SSN 0x142 on Win11 24H2 / Server 2025)
NtPrivilegeCheck PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 142h         ; SSN for win11-24h2
    syscall
    ret
NtPrivilegeCheck ENDP

cProbe SeDebugPrivilege before opening LSASS

// Defensive *or* offensive recon: only proceed if the token already has SeDebug enabled.
HANDLE hThreadToken = NULL;
NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken);
if (!hThreadToken) {
    NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hThreadToken);
    // primary token — must duplicate to impersonation before NtPrivilegeCheck
}

LUID seDebug;
LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &seDebug);

struct {
    DWORD             PrivilegeCount;
    DWORD             Control;
    LUID_AND_ATTRIBUTES Privilege[1];
} req = {
    1,
    PRIVILEGE_SET_ALL_NECESSARY,
    {{ seDebug, SE_PRIVILEGE_ENABLED }}
};

BOOLEAN granted = FALSE;
NtPrivilegeCheck(hThreadToken, (PPRIVILEGE_SET)&req, &granted);
if (granted) {
    // SeDebugPrivilege is enabled — safe to attempt PROCESS_VM_READ on LSASS
}

rustwindows-sys + naked syscall stub

// Cargo: windows-sys = "0.59"  (Win32_Security)
use std::arch::asm;

#[unsafe(naked)]
unsafe extern "system" fn nt_privilege_check_stub() {
    asm!(
        "mov r10, rcx",
        "mov eax, 0x142",   // Win11 24H2; resolve dynamically for other builds
        "syscall",
        "ret",
        options(noreturn),
    );
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20