> Windows Syscalls
ntoskrnl.exeT1134T1134.001T1003

NtAdjustPrivilegesToken

Aktiviert oder deaktiviert Privilegien in einem angegebenen Zugriffstoken.

Prototyp

NTSTATUS NtAdjustPrivilegesToken(
  HANDLE            TokenHandle,
  BOOLEAN           DisableAllPrivileges,
  PTOKEN_PRIVILEGES NewState,
  ULONG             BufferLength,
  PTOKEN_PRIVILEGES PreviousState,
  PULONG            ReturnLength
);

Argumente

NameTypeDirDescription
TokenHandleHANDLEinHandle auf ein Token, das mit TOKEN_ADJUST_PRIVILEGES (und TOKEN_QUERY, falls PreviousState angefordert wird) geöffnet wurde.
DisableAllPrivilegesBOOLEANinFalls TRUE, deaktiviert alle Privilegien des Tokens; NewState wird ignoriert.
NewStatePTOKEN_PRIVILEGESinArray von LUID_AND_ATTRIBUTES, das beschreibt, welche Privilegien aktiviert/deaktiviert/entfernt werden sollen.
BufferLengthULONGinGröße des PreviousState-Puffers in Bytes. 0, wenn PreviousState NULL ist.
PreviousStatePTOKEN_PRIVILEGESoutOptionaler Puffer, der den vorherigen Zustand der geänderten Privilegien empfängt — nützlich zum Zurücksetzen.
ReturnLengthPULONGoutErhält die tatsächlich in PreviousState geschriebene Byte-Anzahl.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x41win10-1507
Win10 16070x41win10-1607
Win10 17030x41win10-1703
Win10 17090x41win10-1709
Win10 18030x41win10-1803
Win10 18090x41win10-1809
Win10 19030x41win10-1903
Win10 19090x41win10-1909
Win10 20040x41win10-2004
Win10 20H20x41win10-20h2
Win10 21H10x41win10-21h1
Win10 21H20x41win10-21h2
Win10 22H20x41win10-22h2
Win11 21H20x41win11-21h2
Win11 22H20x41win11-22h2
Win11 23H20x41win11-23h2
Win11 24H20x41win11-24h2
Server 20160x41winserver-2016
Server 20190x41winserver-2019
Server 20220x41winserver-2022
Server 20250x41winserver-2025

Kernel-Modul

ntoskrnl.exeNtAdjustPrivilegesToken

Verwandte APIs

AdjustTokenPrivilegesAdjustTokenGroupsNtPrivilegeCheckNtOpenProcessTokenExNtFilterTokenLookupPrivilegeValueW

Syscall-Stub

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

Undokumentierte Hinweise

NtAdjustPrivilegesToken ist die Funktion, die `advapi32!AdjustTokenPrivileges` umhüllt. Subtile, aber kritische Eigenheit: Die Funktion liefert `STATUS_SUCCESS` (`0`) zurück, selbst wenn ein oder mehrere angeforderte Privilegien *nicht* gehalten wurden — Aufrufer müssen `GetLastError() == ERROR_NOT_ALL_ASSIGNED` (oder `STATUS_NOT_ALL_ASSIGNED` auf NTSTATUS-Seite) prüfen. Wer das vergisst, produziert stille Eskalations-Bugs in defensiver Tooling. Der SSN ist von Windows 10 1507 bis Windows 11 24H2 **stabil bei `0x41`**, was ihn zum beliebten Eintrag für hartkodierte SSN-Tabellen macht.

Häufige Malware-Nutzung

Pflichtschritt vor jeder Cross-Process-Token-Manipulation: SeDebugPrivilege aktivieren, damit der Implant `NtOpenProcess` auf LSASS oder andere geschützte Dienste anwenden kann. Weitere von Malware häufig aktivierte Privilegien: SeImpersonatePrivilege (für die Potato-Familie zur Impersonation), SeAssignPrimaryTokenPrivilege (für `CreateProcessWithTokenW`), SeTcbPrivilege (LSASS-Stufe), SeBackupPrivilege / SeRestorePrivilege (Registry-Hive-Diebstahl für SAM/SYSTEM-Dumps via `reg save HKLM\SAM`). Mimikatz' `privilege::debug` ist buchstäblich ein Wrapper um diesen Syscall.

Erkennungs­möglichkeiten

Windows-Security-Event **4703** (Token Right Adjusted) protokolliert jede erfolgreiche Privilegien-Anpassung samt aktivierter Privilegien und Aufrufer-Prozess. Event 4672 (Special Privileges Assigned) feuert beim Logon und erneut, wenn sensible Privilegien (SeDebug, SeTcb, SeImpersonate, SeAssignPrimaryToken, SeLoadDriver, SeBackup, SeRestore, SeTakeOwnership, SeCreateToken) in einem neuen Prozess aktiviert werden. Die Korrelation 4703 + nachfolgende 4663/4673 auf LSASS ist ein hochpräzises Muster zur Erkennung von Credential-Dumping. Der ETW-Provider `Microsoft-Windows-Kernel-Audit-API-Calls` erfasst den Aufruf ebenfalls.

Direkte Syscall-Beispiele

asmx64 direct stub (stable SSN 0x41)

NtAdjustPrivilegesToken PROC
    mov  r10, rcx
    mov  eax, 41h          ; SSN stable across all builds
    syscall
    ret
NtAdjustPrivilegesToken ENDP

cEnable 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
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20