> Windows Syscalls
ntoskrnl.exeT1134T1134.001T1068

NtAdjustGroupsToken

Enables or disables groups (SIDs) in an access token, or resets group attributes to their default state.

Prototype

NTSTATUS NtAdjustGroupsToken(
  HANDLE         TokenHandle,
  BOOLEAN        ResetToDefault,
  PTOKEN_GROUPS  NewState,
  ULONG          BufferLength,
  PTOKEN_GROUPS  PreviousState,
  PULONG         ReturnLength
);

Arguments

NameTypeDirDescription
TokenHandleHANDLEinToken handle opened with TOKEN_ADJUST_GROUPS access (and TOKEN_QUERY if PreviousState is requested).
ResetToDefaultBOOLEANinTRUE restores every group's attributes to its default (NewState is ignored); FALSE applies NewState.
NewStatePTOKEN_GROUPSinArray of SID_AND_ATTRIBUTES to set. Attributes typically toggle SE_GROUP_ENABLED / SE_GROUP_ENABLED_BY_DEFAULT.
BufferLengthULONGinSize of PreviousState buffer in bytes. Pass 0 if PreviousState is NULL.
PreviousStatePTOKEN_GROUPSoutOptional buffer that receives the prior group-attribute set so the caller can later revert.
ReturnLengthPULONGoutReceives the number of bytes written to PreviousState (or required if STATUS_BUFFER_TOO_SMALL).

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x6Awin10-1507
Win10 16070x6Awin10-1607
Win10 17030x6Bwin10-1703
Win10 17090x6Bwin10-1709
Win10 18030x6Bwin10-1803
Win10 18090x6Bwin10-1809
Win10 19030x6Bwin10-1903
Win10 19090x6Bwin10-1909
Win10 20040x6Cwin10-2004
Win10 20H20x6Cwin10-20h2
Win10 21H10x6Cwin10-21h1
Win10 21H20x6Cwin10-21h2
Win10 22H20x6Cwin10-22h2
Win11 21H20x6Cwin11-21h2
Win11 22H20x6Cwin11-22h2
Win11 23H20x6Cwin11-23h2
Win11 24H20x6Cwin11-24h2
Server 20160x6Awinserver-2016
Server 20190x6Bwinserver-2019
Server 20220x6Cwinserver-2022
Server 20250x6Cwinserver-2025

Kernel module

ntoskrnl.exeNtAdjustGroupsToken

Related APIs

AdjustTokenGroupsCheckTokenMembershipNtAdjustPrivilegesTokenNtFilterTokenNtDuplicateTokenNtOpenProcessToken

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

Undocumented notes

The kernel side of the Win32 `AdjustTokenGroups` API. Sibling of `NtAdjustPrivilegesToken` — privileges live in TOKEN_PRIVILEGES, group membership lives in TOKEN_GROUPS. `NtAdjustGroupsToken` cannot *add* a SID to a token; it can only enable, disable, or restore-default the SIDs that are already in the token (whether enabled, disabled, deny-only or restricted). To turn off the SE_GROUP_USE_FOR_DENY_ONLY attribute of a SID you typically have to first call `NtFilterToken` to produce a non-deny variant. Each SID in NewState pairs with an Attributes mask (SE_GROUP_ENABLED, SE_GROUP_ENABLED_BY_DEFAULT, SE_GROUP_OWNER, SE_GROUP_USE_FOR_DENY_ONLY); supplying SE_GROUP_ENABLED on a previously disabled SID activates it.

Common malware usage

**Group-SID re-enabling for privilege escalation chains**: the canonical setup is an attacker who has stolen a token (via SeImpersonatePrivilege, Rotten/Juicy Potato, PrintSpoofer, etc.) which contains BUILTIN\Administrators as a deny-only SID (the typical state under UAC split-token Medium IL). The attacker calls NtFilterToken or token duplication to produce a non-deny variant, then NtAdjustGroupsToken to flip SE_GROUP_ENABLED back on for the Administrators SID — instantly granting admin group membership on the live thread without an interactive UAC prompt. Less commonly, it is used to suppress a SID an attacker does not want to be evaluated (disable a logging-group SID that triggers extra auditing on file access). Documented in offensive toolchains around the AlwaysInstallElevated and SeImpersonatePrivilege families.

Detection opportunities

Microsoft-Windows-Security-Auditing event ID 4672 (special privileges assigned) and 4673 (privileged service called) help with the privilege side; the group-adjust side is covered by 4670 (permissions on an object changed) only indirectly. EDRs that track token-modification primitives surface a sequence like (NtOpenProcessToken on a higher-privileged process → NtDuplicateToken → NtFilterToken → NtAdjustGroupsToken → NtSetInformationThread(ImpersonationToken)) as a token-abuse chain. The defender-friendly invariant: legitimate processes almost never call NtAdjustGroupsToken to *enable* a previously disabled high-privilege SID; auditors that snapshot token state on process create and compare to runtime state can flag the delta. ETW Microsoft-Windows-Kernel-Audit-API-Calls surfaces the syscall but at moderate volume.

Direct syscall examples

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 ENDP

cPrivesc — 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