簡體   English   中英

子進程中的C ++ Windows LocalSystem模擬失敗

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM