NtAccessCheck
Effectue un contrôle d'accès d'un descripteur de sécurité contre un token d'impersonation, retournant le masque d'accès accordé.
Prototype
NTSTATUS NtAccessCheck( PSECURITY_DESCRIPTOR SecurityDescriptor, HANDLE ClientToken, ACCESS_MASK DesiredAccess, PGENERIC_MAPPING GenericMapping, PPRIVILEGE_SET PrivilegeSet, PULONG PrivilegeSetLength, PACCESS_MASK GrantedAccess, PNTSTATUS AccessStatus );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| SecurityDescriptor | PSECURITY_DESCRIPTOR | in | Descripteur de sécurité auto-relatif de la ressource protégée à évaluer. |
| ClientToken | HANDLE | in | Handle vers un token d'impersonation (TOKEN_QUERY) — doit être un token d'impersonation, pas un token primaire. |
| DesiredAccess | ACCESS_MASK | in | Droits d'accès demandés par l'appelant (peut inclure des droits génériques — résolus via GenericMapping). |
| GenericMapping | PGENERIC_MAPPING | in | Mappage spécifique au type d'objet, de GENERIC_READ/WRITE/EXECUTE/ALL vers les bits d'accès concrets. |
| PrivilegeSet | PPRIVILEGE_SET | out | Reçoit les privilèges effectivement utilisés par le noyau pendant le contrôle (ex. SeSecurityPrivilege pour lire la SACL). |
| PrivilegeSetLength | PULONG | in/out | En entrée : taille en octets du buffer PrivilegeSet. En sortie : taille réellement nécessaire. |
| GrantedAccess | PACCESS_MASK | out | Reçoit le masque d'accès qui serait effectivement accordé à ClientToken. |
| AccessStatus | PNTSTATUS | out | Reçoit STATUS_SUCCESS si l'accès serait accordé, STATUS_ACCESS_DENIED sinon. Distinct du NTSTATUS de la fonction elle-même. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x0 | win10-1507 |
| Win10 1607 | 0x0 | win10-1607 |
| Win10 1703 | 0x0 | win10-1703 |
| Win10 1709 | 0x0 | win10-1709 |
| Win10 1803 | 0x0 | win10-1803 |
| Win10 1809 | 0x0 | win10-1809 |
| Win10 1903 | 0x0 | win10-1903 |
| Win10 1909 | 0x0 | win10-1909 |
| Win10 2004 | 0x0 | win10-2004 |
| Win10 20H2 | 0x0 | win10-20h2 |
| Win10 21H1 | 0x0 | win10-21h1 |
| Win10 21H2 | 0x0 | win10-21h2 |
| Win10 22H2 | 0x0 | win10-22h2 |
| Win11 21H2 | 0x0 | win11-21h2 |
| Win11 22H2 | 0x0 | win11-22h2 |
| Win11 23H2 | 0x0 | win11-23h2 |
| Win11 24H2 | 0x0 | win11-24h2 |
| Server 2016 | 0x0 | winserver-2016 |
| Server 2019 | 0x0 | winserver-2019 |
| Server 2022 | 0x0 | winserver-2022 |
| Server 2025 | 0x0 | winserver-2025 |
Module noyau
APIs liées
Stub du syscall
4C 8B D1 mov r10, rcx B8 00 00 00 00 mov eax, 0x00 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
NtAccessCheck occupe littéralement le premier slot de la SSDT — SSN `0x0` sur toutes les builds Windows depuis NT 4 — ce qui en fait une curiosité amusante mais inutile comme primitive offensive. C'est le point d'entrée user-mode de la logique de contrôle d'accès *manuel* : un service qui vient d'impersonner un client utilise NtAccessCheck (typiquement via le wrapper Win32 AccessCheck) pour décider si le client *serait* autorisé à effectuer une opération sur une ressource contrôlée par le service. Elle n'ouvre rien — c'est une fonction d'évaluation pure sur SECURITY_DESCRIPTOR + token + GENERIC_MAPPING.
Usage courant par les malwares
Presque jamais utilisée directement à des fins offensives. Deux schémas apparaissent : (1) des outils post-exploitation énumérant quels objets DACL'és un token d'impersonation volé pourrait atteindre sans les ouvrir réellement (reconnaissance silencieuse — aucun audit ne se déclenche sauf si des SACL sont configurées) ; (2) du code d'exploit qui sonde si un token forgé ou rejoué accorde bien l'accès souhaité. Évaluation honnête : NtAccessCheck est dominée par l'usage légitime et reste un signal faible de malveillance.
Opportunités de détection
Très peu de télémétrie utile existe. NtAccessCheck ne déclenche pas elle-même l'audit d'accès aux objets (Event ID 4663) — seul l'ouverture qui suit le fait. Les événements de privilege-use d'ETW Microsoft-Windows-Security-Auditing peuvent se déclencher si l'évaluation référence un objet porteur de SACL, mais la couverture est inégale. Utile comme signal de *corrélation* : un processus inhabituel qui enchaîne de nombreux NtAccessCheck sur des descripteurs variés puis n'ouvre sélectivement (NtOpen*) que ceux qui ont réussi est un schéma classique de reconnaissance silencieuse.
Exemples de syscalls directs
asmx64 direct stub (SSN 0x0 — first in the SSDT)
; Direct syscall stub for NtAccessCheck (SSN 0x00 on every Windows build)
NtAccessCheck PROC
mov r10, rcx ; syscall convention
mov eax, 0 ; SSN
syscall
ret
NtAccessCheck ENDPcService-style: impersonate client and probe access
// After ImpersonateNamedPipeClient / RpcImpersonateClient, the service evaluates
// whether the caller would be allowed FILE_WRITE_DATA on a private resource.
HANDLE hClientToken = NULL;
NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, FALSE, &hClientToken);
GENERIC_MAPPING fileMapping = {
.GenericRead = FILE_GENERIC_READ,
.GenericWrite = FILE_GENERIC_WRITE,
.GenericExecute = FILE_GENERIC_EXECUTE,
.GenericAll = FILE_ALL_ACCESS,
};
BYTE privBuf[256];
ULONG privLen = sizeof(privBuf);
ACCESS_MASK granted = 0;
NTSTATUS accessStatus = 0;
NtAccessCheck(pResourceSD,
hClientToken,
FILE_WRITE_DATA,
&fileMapping,
(PPRIVILEGE_SET)privBuf,
&privLen,
&granted,
&accessStatus);
if (NT_SUCCESS(accessStatus) && (granted & FILE_WRITE_DATA)) {
// proceed under impersonation
}rustwindows-sys + naked syscall stub
// Cargo: windows-sys = "0.59" (Win32_Security)
use std::arch::asm;
#[unsafe(naked)]
unsafe extern "system" fn nt_access_check_stub() {
asm!(
"mov r10, rcx",
"mov eax, 0x00",
"syscall",
"ret",
options(noreturn),
);
}Mappings MITRE ATT&CK
Last verified: 2026-05-20