NtFilterToken
Crée une copie restreinte (filtrée) d'un token d'accès existant en désactivant des SID, supprimant des privilèges ou ajoutant des SID restreints.
Prototype
NTSTATUS NtFilterToken( HANDLE ExistingTokenHandle, ULONG Flags, PTOKEN_GROUPS SidsToDisable, PTOKEN_PRIVILEGES PrivilegesToDelete, PTOKEN_GROUPS RestrictedSids, PHANDLE NewTokenHandle );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| ExistingTokenHandle | HANDLE | in | Handle vers le token source (nécessite l'accès TOKEN_DUPLICATE). |
| Flags | ULONG | in | Combinaison de DISABLE_MAX_PRIVILEGE, SANDBOX_INERT, LUA_TOKEN, WRITE_RESTRICTED. |
| SidsToDisable | PTOKEN_GROUPS | in | Liste optionnelle de SID à marquer en deny-only dans le nouveau token (NULL les laisse activés). |
| PrivilegesToDelete | PTOKEN_PRIVILEGES | in | Liste optionnelle de privilèges à retirer du nouveau token (ex. SeDebugPrivilege, SeImpersonatePrivilege). |
| RestrictedSids | PTOKEN_GROUPS | in | Liste optionnelle de SID restreints ajoutés au nouveau token ; le noyau les évalue en ET avec chaque DACL. |
| NewTokenHandle | PHANDLE | out | Reçoit le handle du token filtré nouvellement créé en cas de succès. |
IDs de syscalls par version de Windows
| Version 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 |
Module noyau
APIs liées
Stub du 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
Notes non documentées
NtFilterToken est l'implémentation noyau derrière advapi32!CreateRestrictedToken. À partir d'un token source, elle retourne un nouveau token primaire ou d'impersonation avec certains SID passés en deny-only, certains privilèges supprimés, et d'éventuels *SID restreints* attachés. Les SID restreints sont puissants : lors d'un contrôle d'accès, le noyau ET-combine l'accès accordé avec ce que la seule liste de SID restreints aurait permis ; le token résultant peut donc être verrouillé bien en dessous des droits normaux de l'utilisateur. Le SSN dérive presque à chaque mise à jour majeure (`0xD5` sur Win10 1507, `0xED` sur Win11 24H2) : la résolution dynamique est obligatoire.
Usage courant par les malwares
Utilisée légitimement par les sandboxes Chromium / Edge / Acrobat et par les brokers AppContainer. L'usage offensif est réel mais plus rare : un attaquant possédant un token à fort privilège (SYSTEM, ou un utilisateur impersonné avec SeDebugPrivilege) peut volontairement *rétrograder* un processus enfant ou un contexte d'impersonation pour ressembler à un utilisateur peu privilégié — une forme de token demotion qui contourne les détections fondées sur « SYSTEM a fait X ». Quelques frameworks red-team et un petit nombre d'échantillons de recherche enveloppent NtFilterToken précisément pour cette raison. Ce n'est *pas* un signal offensif fort en soi.
Opportunités de détection
La couverture des événements de token est le bon point de départ : les événements 4696/4624 Microsoft-Windows-Security-Auditing révèlent les changements de token de logon, et ETW Microsoft-Windows-Kernel-Audit-API-Calls couvre la création de tokens. Les EDR hookent typiquement NtFilterToken pour alimenter leurs graphes de dérivation de tokens. Signal blue-team utile : NtFilterToken appelée *avec* un token source à fort privilège *depuis* un processus inhabituel (ni navigateur, ni broker). À combiner avec un NtCreateUserProcess + AssignPrimaryToken qui suivent, pour repérer une token demotion délibérée avant exécution du processus enfant.
Exemples de syscalls directs
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),
);
}Mappings MITRE ATT&CK
Last verified: 2026-05-20