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
| Name | Type | Dir | Description |
|---|---|---|---|
| TokenHandle | HANDLE | in | Handle a un token abierto con TOKEN_ADJUST_PRIVILEGES (y TOKEN_QUERY si se solicita PreviousState). |
| DisableAllPrivileges | BOOLEAN | in | Si es TRUE, deshabilita todos los privilegios del token; se ignora NewState. |
| NewState | PTOKEN_PRIVILEGES | in | Array de LUID_AND_ATTRIBUTES que describe qué privilegios habilitar/deshabilitar/quitar. |
| BufferLength | ULONG | in | Tamaño en bytes del buffer PreviousState. 0 si PreviousState es NULL. |
| PreviousState | PTOKEN_PRIVILEGES | out | Buffer opcional que recibe el estado anterior de los privilegios modificados, útil para revertir. |
| ReturnLength | PULONG | out | Recibe el número de bytes realmente escritos en PreviousState. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x41 | win10-1507 |
| Win10 1607 | 0x41 | win10-1607 |
| Win10 1703 | 0x41 | win10-1703 |
| Win10 1709 | 0x41 | win10-1709 |
| Win10 1803 | 0x41 | win10-1803 |
| Win10 1809 | 0x41 | win10-1809 |
| Win10 1903 | 0x41 | win10-1903 |
| Win10 1909 | 0x41 | win10-1909 |
| Win10 2004 | 0x41 | win10-2004 |
| Win10 20H2 | 0x41 | win10-20h2 |
| Win10 21H1 | 0x41 | win10-21h1 |
| Win10 21H2 | 0x41 | win10-21h2 |
| Win10 22H2 | 0x41 | win10-22h2 |
| Win11 21H2 | 0x41 | win11-21h2 |
| Win11 22H2 | 0x41 | win11-22h2 |
| Win11 23H2 | 0x41 | win11-23h2 |
| Win11 24H2 | 0x41 | win11-24h2 |
| Server 2016 | 0x41 | winserver-2016 |
| Server 2019 | 0x41 | winserver-2019 |
| Server 2022 | 0x41 | winserver-2022 |
| Server 2025 | 0x41 | winserver-2025 |
Módulo del kernel
APIs relacionadas
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 ENDPcEnable 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