> Windows Syscalls
ntoskrnl.exeT1134T1134.001T1003

NtAdjustPrivilegesToken

Habilita o deshabilita privilegios en un token de acceso especificado.

Prototipo

NTSTATUS NtAdjustPrivilegesToken(
  HANDLE            TokenHandle,
  BOOLEAN           DisableAllPrivileges,
  PTOKEN_PRIVILEGES NewState,
  ULONG             BufferLength,
  PTOKEN_PRIVILEGES PreviousState,
  PULONG            ReturnLength
);

Argumentos

NameTypeDirDescription
TokenHandleHANDLEinHandle a un token abierto con TOKEN_ADJUST_PRIVILEGES (y TOKEN_QUERY si se solicita PreviousState).
DisableAllPrivilegesBOOLEANinSi es TRUE, deshabilita todos los privilegios del token; se ignora NewState.
NewStatePTOKEN_PRIVILEGESinArray de LUID_AND_ATTRIBUTES que describe qué privilegios habilitar/deshabilitar/quitar.
BufferLengthULONGinTamaño en bytes del buffer PreviousState. 0 si PreviousState es NULL.
PreviousStatePTOKEN_PRIVILEGESoutBuffer opcional que recibe el estado anterior de los privilegios modificados, útil para revertir.
ReturnLengthPULONGoutRecibe el número de bytes realmente escritos en PreviousState.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x41win10-1507
Win10 16070x41win10-1607
Win10 17030x41win10-1703
Win10 17090x41win10-1709
Win10 18030x41win10-1803
Win10 18090x41win10-1809
Win10 19030x41win10-1903
Win10 19090x41win10-1909
Win10 20040x41win10-2004
Win10 20H20x41win10-20h2
Win10 21H10x41win10-21h1
Win10 21H20x41win10-21h2
Win10 22H20x41win10-22h2
Win11 21H20x41win11-21h2
Win11 22H20x41win11-22h2
Win11 23H20x41win11-23h2
Win11 24H20x41win11-24h2
Server 20160x41winserver-2016
Server 20190x41winserver-2019
Server 20220x41winserver-2022
Server 20250x41winserver-2025

Módulo del kernel

ntoskrnl.exeNtAdjustPrivilegesToken

APIs relacionadas

AdjustTokenPrivilegesAdjustTokenGroupsNtPrivilegeCheckNtOpenProcessTokenExNtFilterTokenLookupPrivilegeValueW

Stub del syscall

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

NtAdjustPrivilegesToken es lo que envuelve `advapi32!AdjustTokenPrivileges`. Comportamiento sutil pero crítico: la función devuelve `STATUS_SUCCESS` (`0`) incluso cuando uno o más privilegios solicitados *no* se poseen — los llamantes deben verificar `GetLastError() == ERROR_NOT_ALL_ASSIGNED` (o `STATUS_NOT_ALL_ASSIGNED` en NTSTATUS). Omitirlo produce bugs silenciosos de elevación en herramientas defensivas. El SSN ha permanecido **estable en `0x41`** desde Windows 10 1507 hasta Windows 11 24H2, lo que lo hace popular para tablas de SSN codificadas.

Uso común por malware

Paso obligatorio antes de cualquier manipulación de token entre procesos: habilitar SeDebugPrivilege para que el implante pueda llamar a `NtOpenProcess` sobre LSASS u otros servicios protegidos. Otros privilegios comúnmente habilitados por malware: SeImpersonatePrivilege (requerido por la familia Potato para impersonación), SeAssignPrimaryTokenPrivilege (requerido por `CreateProcessWithTokenW`), SeTcbPrivilege (nivel LSASS), SeBackupPrivilege / SeRestorePrivilege (robo de hives del registro para dumps SAM/SYSTEM mediante `reg save HKLM\SAM`). `privilege::debug` de Mimikatz es literalmente un wrapper sobre este syscall.

Oportunidades de detección

El evento Windows Security **4703** (Token Right Adjusted) registra cada ajuste de privilegio exitoso, incluyendo qué privilegios se habilitaron y el proceso llamante. El evento 4672 (Special Privileges Assigned) se dispara en el logon y de nuevo cuando se habilitan privilegios sensibles (SeDebug, SeTcb, SeImpersonate, SeAssignPrimaryToken, SeLoadDriver, SeBackup, SeRestore, SeTakeOwnership, SeCreateToken) en un nuevo proceso. Correlacionar 4703 con 4663/4673 posteriores contra LSASS es un patrón de muy alta fidelidad para detección de credential dumping. El provider ETW `Microsoft-Windows-Kernel-Audit-API-Calls` también lo cubre.

Ejemplos de syscalls directos

asmx64 direct stub (stable SSN 0x41)

NtAdjustPrivilegesToken PROC
    mov  r10, rcx
    mov  eax, 41h          ; SSN stable across all builds
    syscall
    ret
NtAdjustPrivilegesToken ENDP

cEnable SeDebugPrivilege the classic way

// Enable SeDebugPrivilege via direct Nt* calls — required before opening LSASS.
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tp = { 0 };

NtOpenProcessTokenEx(NtCurrentProcess(),
                    TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                    0, &hToken);

LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &luid);
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

NTSTATUS s = NtAdjustPrivilegesToken(hToken,
                                     FALSE,
                                     &tp,
                                     0, NULL, NULL);
// IMPORTANT: also test STATUS_NOT_ALL_ASSIGNED (0x06000000+) — success isn't enough.
if (s == 0x00000106 /* STATUS_NOT_ALL_ASSIGNED */) {
    // Privilege wasn't held — abort the elevation chain.
}

rustPrivilege-enable helper inside an impersonation chain

// Tiny helper used right after NtOpenProcessTokenEx, before NtDuplicateToken.
use windows_sys::Win32::Foundation::{HANDLE, LUID};
use windows_sys::Win32::Security::{LookupPrivilegeValueW, TOKEN_PRIVILEGES, SE_PRIVILEGE_ENABLED};

pub unsafe fn enable_privilege(token: HANDLE, name: &str) -> bool {
    let wide: Vec<u16> = name.encode_utf16().chain(std::iter::once(0)).collect();
    let mut luid: LUID = std::mem::zeroed();
    if LookupPrivilegeValueW(std::ptr::null(), wide.as_ptr(), &mut luid) == 0 {
        return false;
    }
    let mut tp = TOKEN_PRIVILEGES {
        PrivilegeCount: 1,
        Privileges: [windows_sys::Win32::Security::LUID_AND_ATTRIBUTES {
            Luid: luid,
            Attributes: SE_PRIVILEGE_ENABLED,
        }],
    };
    // Direct syscall stub elsewhere; signature matches NtAdjustPrivilegesToken.
    extern "system" { fn NtAdjustPrivilegesToken(
        h: HANDLE, disable_all: u8,
        new_state: *mut TOKEN_PRIVILEGES, len: u32,
        prev: *mut TOKEN_PRIVILEGES, ret_len: *mut u32) -> i32; }
    let s = NtAdjustPrivilegesToken(token, 0, &mut tp, 0, std::ptr::null_mut(), std::ptr::null_mut());
    // STATUS_NOT_ALL_ASSIGNED == 0x00000106 — treat as failure for elevation.
    s == 0
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20