> Windows Syscalls
ntoskrnl.exeT1106T1033

NtPrivilegeCheck

Comprueba si los privilegios listados en un PRIVILEGE_SET están habilitados en un token de impersonación.

Prototipo

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

Argumentos

NameTypeDirDescription
ClientTokenHANDLEinHandle a un token de impersonación (TOKEN_QUERY). Los tokens primarios se rechazan con STATUS_NO_IMPERSONATION_TOKEN.
RequiredPrivilegesPPRIVILEGE_SETin/outPrivilegios a comprobar. El flag PRIVILEGE_SET_ALL_NECESSARY exige todos; en caso contrario basta una coincidencia. Cada LUID_AND_ATTRIBUTES recibe su campo Attributes actualizado con SE_PRIVILEGE_USED_FOR_ACCESS al retornar.
ResultPBOOLEANoutRecibe TRUE si la verificación pasa, FALSE en caso contrario. El NTSTATUS de la función solo reporta errores del propio llamada.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
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

Módulo del kernel

ntoskrnl.exeNtPrivilegeCheck

APIs relacionadas

PrivilegeCheckGetTokenInformationAdjustTokenPrivilegesNtQueryInformationTokenNtAdjustPrivilegesTokenNtAccessCheck

Stub del syscall

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

Notas no documentadas

NtPrivilegeCheck es la compañera de kernel de AccessCheck: pregunta «¿este token de impersonación tiene actualmente estos privilegios *habilitados*?». El PRIVILEGE_SET pasado lleva un campo Control — PRIVILEGE_SET_ALL_NECESSARY exige todos los privilegios listados, en otro caso el kernel devuelve TRUE en cuanto coincide uno. Al volver, cada LUID_AND_ATTRIBUTES recibe SE_PRIVILEGE_USED_FOR_ACCESS para los privilegios que satisficieron la prueba, alimentando la auditoría de uso de privilegios. Estrictamente una comprobación de *habilitado*: los privilegios presentes pero deshabilitados cuentan como ausentes.

Uso común por malware

Los atacantes no llaman realmente a NtPrivilegeCheck con fines ofensivos — no concede nada, solo reporta. Donde sí aparece en malware es en payloads conscientes de la detección que sondean su propio token antes de intentar una operación privilegiada (p. ej. verificar que SeDebugPrivilege esté efectivamente habilitado antes de abrir LSASS), para retroceder en silencio si no lo está. Evaluación honesta: es un syscall de servicios legítimos y defensores; tratarlo como señal ofensiva de baja confianza.

Oportunidades de detección

NtPrivilegeCheck no emite por sí misma eventos de auditoría de uso de privilegios (4674) — esos se disparan cuando se ejecuta realmente una operación privilegiada. El ángulo más útil está en el EDR: hookear ntdll!NtPrivilegeCheck y correlacionar «el proceso X sondeó repetidamente SeDebugPrivilege sin tenerlo» (reconocimiento / descubrimiento de capacidades), o «el proceso X sondeó SeImpersonatePrivilege justo antes de forjar un token». En un controlador de dominio, comprobaciones repetidas de SeBackupPrivilege/SeRestorePrivilege desde software no de backup son notables. Señal débil aislada; útil en cadenas.

Ejemplos de syscalls directos

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),
    );
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20