[英]C++ windows LocalSystem impersonation in child process failing
試圖解決它,但到目前為止所有努力都是徒勞的。 工作流程如下
以LocalSystem運行的Windows服務使用CreateProcessAsUser(...)
和當前登錄用戶的令牌創建子級。
const auto session = WTSGetActiveConsoleSessionId();
auto result = WTSQueryUserToken(session, &token);
HANDLE primary;
result = DuplicateTokenEx(token,
TOKEN_QUERY_SOURCE | TOKEN_ALL_ACCESS | TOKEN_IMPERSONATE |
TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_PRIVILEGES,
nullptr, SecurityImpersonation, TokenPrimary, &primary);
const auto args = std::to_string(reinterpret_cast<long>(access));
CreateProcessAsUser(primary,
const_cast<LPSTR>(command.c_str()), // module name
const_cast<LPSTR>(args.c_str()), // Command line
nullptr, // Process handle not inheritable
nullptr, // Thread handle not inheritable
TRUE, // Set handle inheritance to TRUE
0, // No creation flags
nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi); // Pointer to PROCESS_INFORMATION structure
子進程在用戶工作站\\桌面中啟動,主線程捕獲用戶I / O事件。 子進程模擬如下
void impersonate() {
const auto args = GetCommandLine();
const auto system_token = reinterpret_cast<HANDLE>(std::stol(args, nullptr));
if (SetThreadToken(nullptr, system_token) == TRUE) {
auto result = OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY | TOKEN_QUERY_SOURCE, TRUE, &token);
if (result == TRUE)
{
DWORD dwSize = 0;
if (!GetTokenInformation(token, TokenStatistics, NULL, 0, &dwSize)) {
const auto dwResult = GetLastError();
if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
cout << "GetTokenInformation Error: " << dwResult;
} else {
// Allocate the buffer.
PTOKEN_STATISTICS statistics =
(PTOKEN_STATISTICS)GlobalAlloc(GPTR, dwSize);
// Call GetTokenInformation again to get the group information.
if (!GetTokenInformation(token, TokenStatistics, statistics, dwSize,
&dwSize)) {
cout << "GetTokenInformation Error: " << error;
} else {
const auto level = statistics->ImpersonationLevel;
std::string str;
switch (level) {
case SecurityAnonymous:
str = R"(anonymous)";
break;
case SecurityIdentification:
str = R"(identification)";
break;
case SecurityImpersonation:
str = R"(impersonation)";
break;
case SecurityDelegation:
str = R"(delegation)";
break;
}
// This outputs identification.
cout << "impersonation level : " << str;
}
}
}
}
void thread_main()
{
impersonate();
// if impersonation is successful, file opening fails otherwise not.
const auto file = CreateFile(R"(C:\foo.txt)", // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_NEW, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (file == INVALID_HANDLE_VALUE) {
} else {
// Rest of code;
}
}
盡管當前用戶是管理員,並添加了“身份驗證后模擬客戶端”,但仍報告“安全性標識”。
問:還有其他要求將其提升為安全模擬角色嗎? 謝謝,
我接下來的工作方式-您將LocalSystem令牌從服務復制到子進程(通過繼承句柄),然后在命令行中將其傳遞給句柄值。 然后調用SetThreadToken
。
但是SetThreadToken
文檔錯誤SetThreadToken
完整。
這里只說令牌必須具有TOKEN_IMPERSONATE
訪問權限。 關於線程句柄訪問權限的任何說明-它必須具有THREAD_SET_THREAD_TOKEN
但主要:
使用SetThreadToken函數進行模擬時,必須具有模擬特權 ,並確保SetThreadToken函數成功
你必須擁有什么意思? 通常,這意味着調用線程(或在線程沒有令牌的情況下,調用線程所屬的進程)必須在令牌中具有模擬特權。
但這是錯誤的,而不是正確的。 您(調用線程)具有哪種特權-沒關系。 目標(未調用!)線程所屬的進程( 即使目標線程具有令牌 )也必須具有SeImpersonatePrivilege
特權或具有與模擬令牌相同的登錄會話ID,否則SeImpersonatePrivilege
,函數不會失敗,並且返回成功,但是將令牌中的SECURITY_IMPERSONATION_LEVEL
成員靜默替換為SecurityIdentification
(在WRK-v1.2 \\ base \\ ntos \\ ps \\ security.c中 PsImpersonateClient
函數-從SeTokenCanImpersonate
開始(在WRK-v1.2 \\ base \\ ntos \\ se \\ token.c中實現 -在此處檢查TOKEN_HAS_IMPERSONATE_PRIVILEGE
和LogonSessionId),以及如果失敗( STATUS_PRIVILEGE_NOT_HELD
)由SeTokenCanImpersonate
返回PsImpersonateClient
函數集ImpersonationLevel = SecurityIdentification ;
因此,即使您從服務(具有模擬特權)為子進程線程調用SetThreadToken
如果子進程沒有模擬特權,則調用為“失敗”。 反之亦然-如果您說將自己的線程句柄(具有THREAD_SET_THREAD_TOKEN
訪問權限)傳遞給不具有模擬特權的受限進程,則他可以為您的線程成功調用SetThreadToken
模擬級別不會重置為SecurityIdentification
在您的情況下,因為子進程沒有SeImpersonatePrivilege
(通常僅存在於提升的進程中,但是如果用戶使用LOGON32_LOGON_INTERACTIVE
進入系統-甚至“管理員”也確實限制了令牌(因此他們不是真正的管理員))並且具有不同的會話ID (比較本地系統令牌會話ID)-在SetThreadToken
之后,您的線程具有SecurityIdentification
模擬級別。 結果,任何進行安全檢查(例如打開文件或注冊表項)的系統調用都將失敗,並顯示錯誤ERROR_BAD_IMPERSONATION_LEVEL
。
解決方案怎么樣? 如果用戶具有管理員特權 -您需要在用戶會話中創建提升的子進程(例如“以admin身份運行”)。 為此,您需要查詢WTSQueryUserToken
返回的令牌的提升類型,如果它是TokenElevationTypeLimited
我們需要通過GetTokenInformation
調用使用TokenLinkedToken
獲得鏈接令牌 。
這是完全未記錄的,但是TOKEN_LINKED_TOKEN
結構中返回的令牌取決於調用線程(或進程)是否具有SE_TCB_PRIVILEGE
如果是,則返回TokenPrimary
。 否則,返回TokenImpersonation
並將SECURITY_IMPERSONATION_LEVEL
設置為SecurityIdentification
(因此,此令牌只能用於查詢)。 因為在本地系統帳戶下運行的服務具有SE_TCB_PRIVILEGE
您獲得了主令牌,因此需要在CreateProcessAsUser
調用中使用它。 所以你需要下一個功能:
ULONG GetElevatedUserToken(PHANDLE phToken)
{
union {
ULONG SessionId;
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
TOKEN_TYPE tt;
};
SessionId = WTSGetActiveConsoleSessionId();
if (SessionId == MAXDWORD)
{
return ERROR_NO_SUCH_LOGON_SESSION;
}
HANDLE hToken;
if (!WTSQueryUserToken(SessionId, &hToken))
{
return GetLastError();
}
ULONG len;
ULONG dwError = NOERROR;
if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &len))
{
if (tet == TokenElevationTypeLimited)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &len))
{
CloseHandle(hToken);
hToken = tlt.LinkedToken;
}
else
{
dwError = GetLastError();
}
}
}
else
{
dwError = GetLastError();
}
if (dwError == NOERROR)
{
if (GetTokenInformation(hToken, TokenType, &tt, sizeof(tt), &len))
{
if (tt != TokenPrimary)
{
dwError = ERROR_INVALID_HANDLE;
}
}
else
{
dwError = GetLastError();
}
if (dwError == NOERROR)
{
*phToken = hToken;
return NOERROR;
}
CloseHandle(hToken);
}
return dwError;
}
並使用下一個代碼作為開始子對象
HANDLE hToken;
ULONG dwError = GetElevatedUserToken(&hToken);
if (dwError == NOERROR)
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
//***
if (CreateProcessAsUser(hToken, ***, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
CloseHandle(hToken);
}
在這種情況下,您可能根本不需要在子進程中模擬LocalSystem 。 但是,如果仍然需要LocalSystem ,則可以在子進程中復制此類令牌,在這種情況下, SetThreadtoken
完全可以,因為子進程將具有模擬特權
請原諒我提出哪些顯而易見的要求,但需要提出以下要求:
您是否正在檢查這些函數的返回值? 它們失敗時調用GetLastError嗎? 您返回什么錯誤代碼?
如果這是C ++,是否要設置未處理的異常處理程序?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.