NtAccessCheck
Führt eine Access Check gegen einen Security Descriptor mit einem Impersonation-Token durch und liefert die gewährte Zugriffsmaske zurück.
Prototyp
NTSTATUS NtAccessCheck( PSECURITY_DESCRIPTOR SecurityDescriptor, HANDLE ClientToken, ACCESS_MASK DesiredAccess, PGENERIC_MAPPING GenericMapping, PPRIVILEGE_SET PrivilegeSet, PULONG PrivilegeSetLength, PACCESS_MASK GrantedAccess, PNTSTATUS AccessStatus );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| SecurityDescriptor | PSECURITY_DESCRIPTOR | in | Self-relative Security Descriptor der zu prüfenden geschützten Ressource. |
| ClientToken | HANDLE | in | Handle auf ein Impersonation-Token (TOKEN_QUERY) — muss ein Impersonation-Token sein, kein Primary-Token. |
| DesiredAccess | ACCESS_MASK | in | Vom Aufrufer angeforderte Zugriffsrechte (kann generische Rechte enthalten — via GenericMapping aufgelöst). |
| GenericMapping | PGENERIC_MAPPING | in | Objekttypspezifisches Mapping von GENERIC_READ/WRITE/EXECUTE/ALL auf konkrete Zugriffsbits. |
| PrivilegeSet | PPRIVILEGE_SET | out | Erhält die Privilegien, die der Kernel beim Check tatsächlich genutzt hat (z. B. SeSecurityPrivilege beim Lesen von SACLs). |
| PrivilegeSetLength | PULONG | in/out | Eingabe: Größe des PrivilegeSet-Puffers in Bytes. Ausgabe: tatsächlich benötigte Größe. |
| GrantedAccess | PACCESS_MASK | out | Erhält die Zugriffsmaske, die ClientToken tatsächlich gewährt würde. |
| AccessStatus | PNTSTATUS | out | Erhält STATUS_SUCCESS, falls Zugriff gewährt würde, sonst STATUS_ACCESS_DENIED. Vom NTSTATUS der Funktion selbst unabhängig. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | 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 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
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
Undokumentierte Hinweise
NtAccessCheck belegt buchstäblich den ersten Slot der SSDT — SSN `0x0` auf jeder Windows-Build seit NT 4 — was sie zu einer netten Kuriosität macht, aber als Offensiv-Primitive nutzlos. Sie ist der User-Mode-Einstiegspunkt für *manuelle* Access-Check-Logik: ein Dienst, der gerade einen Client imitiert, nutzt NtAccessCheck (üblicherweise über den Win32-AccessCheck-Wrapper), um zu entscheiden, ob der Client eine Operation auf einer vom Dienst kontrollierten Ressource ausführen *dürfte*. Sie öffnet selbst nichts — sie ist eine reine Auswertungsfunktion über SECURITY_DESCRIPTOR + Token + GENERIC_MAPPING.
Häufige Malware-Nutzung
Wird fast nie direkt offensiv eingesetzt. Die zwei Muster, die vorkommen: (1) Post-Exploitation-Tools, die enumerieren, welche DACL-geschützten Objekte ein gestohlenes Impersonation-Token erreichen kann, ohne sie tatsächlich zu öffnen (lautlose Aufklärung — kein Audit feuert, sofern nicht SACLs konfiguriert sind); (2) Exploit-Code, der prüft, ob ein gefälschtes oder wiedereingespieltes Token den gewünschten Zugriff gewährt. Ehrliche Einschätzung: NtAccessCheck wird von legitimer Nutzung dominiert und ist ein schwaches Malware-Signal.
Erkennungsmöglichkeiten
Kaum nutzbare Telemetrie vorhanden. NtAccessCheck löst selbst kein Object-Access-Auditing aus (Event ID 4663) — nur ein nachfolgendes Open tut das. Privilege-Use-Events in ETW Microsoft-Windows-Security-Auditing können feuern, falls die Auswertung ein Objekt mit SACL berührt, doch die Abdeckung ist dünn. Brauchbar als *Korrelations*-Signal: ein ungewöhnlicher Prozess, der viele NtAccessCheck-Aufrufe über verschiedene Security Descriptors hintereinander absetzt und anschließend selektiv nur die erfolgreichen mit NtOpen* öffnet, ist ein klassisches lautloses Aufklärungsmuster.
Direkte Syscall-Beispiele
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),
);
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20