NtFilterToken
Crea una copia restringida (filtrada) de un token de acceso existente desactivando SIDs, eliminando privilegios o agregando SIDs restringidos.
Prototipo
NTSTATUS NtFilterToken( HANDLE ExistingTokenHandle, ULONG Flags, PTOKEN_GROUPS SidsToDisable, PTOKEN_PRIVILEGES PrivilegesToDelete, PTOKEN_GROUPS RestrictedSids, PHANDLE NewTokenHandle );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ExistingTokenHandle | HANDLE | in | Handle al token origen (requiere acceso TOKEN_DUPLICATE). |
| Flags | ULONG | in | Combinación de DISABLE_MAX_PRIVILEGE, SANDBOX_INERT, LUA_TOKEN, WRITE_RESTRICTED. |
| SidsToDisable | PTOKEN_GROUPS | in | Lista opcional de SIDs a marcar como deny-only en el nuevo token (NULL los deja habilitados). |
| PrivilegesToDelete | PTOKEN_PRIVILEGES | in | Lista opcional de privilegios a quitar del nuevo token (p. ej. SeDebugPrivilege, SeImpersonatePrivilege). |
| RestrictedSids | PTOKEN_GROUPS | in | Lista opcional de SIDs restringidos añadidos al nuevo token; el kernel los comprueba en AND con cada DACL. |
| NewTokenHandle | PHANDLE | out | Recibe el handle del token filtrado recién creado si tiene éxito. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0xD5 | win10-1507 |
| Win10 1607 | 0xD8 | win10-1607 |
| Win10 1703 | 0xDB | win10-1703 |
| Win10 1709 | 0xDC | win10-1709 |
| Win10 1803 | 0xDD | win10-1803 |
| Win10 1809 | 0xDE | win10-1809 |
| Win10 1903 | 0xDF | win10-1903 |
| Win10 1909 | 0xDF | win10-1909 |
| Win10 2004 | 0xE4 | win10-2004 |
| Win10 20H2 | 0xE4 | win10-20h2 |
| Win10 21H1 | 0xE4 | win10-21h1 |
| Win10 21H2 | 0xE5 | win10-21h2 |
| Win10 22H2 | 0xE5 | win10-22h2 |
| Win11 21H2 | 0xEA | win11-21h2 |
| Win11 22H2 | 0xEB | win11-22h2 |
| Win11 23H2 | 0xEB | win11-23h2 |
| Win11 24H2 | 0xED | win11-24h2 |
| Server 2016 | 0xD8 | winserver-2016 |
| Server 2019 | 0xDE | winserver-2019 |
| Server 2022 | 0xE9 | winserver-2022 |
| Server 2025 | 0xED | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 ED 00 00 00 mov eax, 0xED 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
NtFilterToken es la implementación de kernel detrás de advapi32!CreateRestrictedToken. A partir de un token origen devuelve un nuevo token primario o de impersonación con ciertos SIDs marcados como deny-only, ciertos privilegios eliminados y opcionales *restricted SIDs* adjuntos. Los restricted SIDs son potentes: en cada chequeo de acceso el kernel hace AND entre el acceso concedido y lo que la lista de restricted SIDs por sí sola habría concedido, así que el token resultante puede quedar muy por debajo de los derechos normales del usuario. La SSN cambia casi en cada actualización mayor (`0xD5` en Win10 1507, `0xED` en Win11 24H2), por lo que la resolución dinámica es obligatoria.
Uso común por malware
Usada legítimamente por los sandboxes de Chromium / Edge / Acrobat y por los brokers de AppContainer. El uso ofensivo existe pero es más raro: un atacante con un token de alto privilegio (SYSTEM, o un usuario impersonado con SeDebugPrivilege) puede *degradar* deliberadamente un proceso hijo o un contexto de impersonación para parecerse a un usuario poco privilegiado — una forma de token demotion que evade detecciones basadas en "SYSTEM hizo X". Algunos frameworks de red team y un puñado de muestras de investigación envuelven NtFilterToken precisamente por eso. *No* es una señal ofensiva fuerte por sí sola.
Oportunidades de detección
La cobertura de eventos de token es el punto de partida correcto: los eventos 4696/4624 de Microsoft-Windows-Security-Auditing exponen cambios de token de logon, y ETW Microsoft-Windows-Kernel-Audit-API-Calls cubre la creación de tokens. Los EDR suelen hookear NtFilterToken para alimentar sus grafos de derivación de tokens. Señal blue-team útil: NtFilterToken invocada *con* un token origen de alto privilegio *desde* un proceso inusual (ni navegador, ni broker). Combinar con NtCreateUserProcess + AssignPrimaryToken posteriores para detectar una token demotion deliberada antes de la ejecución del proceso hijo.
Ejemplos de syscalls directos
asmx64 direct stub (Win11 24H2 SSN)
; Direct syscall stub for NtFilterToken (SSN 0xED on Win11 24H2 / Server 2025)
NtFilterToken PROC
mov r10, rcx ; syscall convention
mov eax, 0EDh ; SSN for win11-24h2
syscall
ret
NtFilterToken ENDPcToken demotion: strip privileges from current token
// Take the current process token and produce a filtered copy with SeDebugPrivilege
// and SeImpersonatePrivilege removed — useful for spawning a less-suspicious child.
HANDLE hCurrent = NULL;
NtOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &hCurrent);
LUID seDebug, seImpersonate;
LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &seDebug);
LookupPrivilegeValueW(NULL, L"SeImpersonatePrivilege", &seImpersonate);
struct {
DWORD Count;
LUID_AND_ATTRIBUTES Priv[2];
} toDelete = { 2, {{seDebug, 0}, {seImpersonate, 0}} };
HANDLE hFiltered = NULL;
NtFilterToken(hCurrent,
DISABLE_MAX_PRIVILEGE, // also strip every non-mandatory privilege
NULL, // SidsToDisable
(PTOKEN_PRIVILEGES)&toDelete,
NULL, // RestrictedSids
&hFiltered);rustwindows-sys + naked syscall stub
// Cargo: windows-sys = "0.59" (Win32_Security)
use std::arch::asm;
#[unsafe(naked)]
unsafe extern "system" fn nt_filter_token_stub() {
asm!(
"mov r10, rcx",
"mov eax, 0xED", // Win11 24H2; resolve dynamically for other builds
"syscall",
"ret",
options(noreturn),
);
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20