2018年4月5日 星期四

[Windows][C++] User impersonation

最近在寫一個功能,想要對有 TLS 連線的 Process 檢查一下封包裡的憑證是不是有問題。好不容易實作出來了卻發現了一個問題。我寫的這個程式是跑在 System 身分裡的,如果說有用戶把憑證加入目前使用者的白名單,那程式透過 Cypto API 就會讀不到使用者加入的白名單憑證。這個問題可以用 User impersonation 來解決。

在 Windows 管理憑證中,每個使用者都會有自己的 Certificate Store, 當使用者一號把一張有問題的憑證加入自己 Store 白名單時,並不會影響其他的使用者仍判定這張憑證是有問題的。

搜尋 MMC ->  檔案 -> 新增/移除嵌入式管理元件 -> 新增"憑證",可以發現是針對用戶、或是本機來管理憑證

HANDLE hToken = NULL;
wchar_t szCurrentUserName[260] = {};

// 取得 PID 為 4612 的 handle
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 4612);

// 透過 handle 取得對應的 token
if (!OpenProcessToken(processHandle, TOKEN_ALL_ACCESS, &hToken))
{
    cout << "OpenProcessToken failed. GetLastError returned:" << GetLastError() << endl;
}
else
{
    // ImpersonateLoggedOnUser 模仿該使用者
    if (!ImpersonateLoggedOnUser(hToken))
    {
        cout << "ImpersonateLoggedOnUser Error" << endl;
    }
    else
    {
        // 印出 Current User Name 是否改成模仿的使用者名稱
        ZeroMemory(szCurrentUserName, sizeof(szCurrentUserName));
 nSize = ARRAYSIZE(szCurrentUserName);
 if (!GetUserName(szCurrentUserName, &nSize))
 {
            ReportError(L"GetUserName");
            goto Cleanup;
 }
        wprintf(L"The current user is %s\n\n", szCurrentUserName);

        // 試著讀取 Current User 裡的 My 憑證是否是改變成模仿的使用者
        HCERTSTORE hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L"My");
        PCCERT_CONTEXT pCertContext = NULL;
        while (pCertContext = CertEnumCertificatesInStore(hStore, pCertContext))
        {
            // 印出憑證裡的 Issuer 和 Subject (此為自定義的 Function)
            printCertContent(pCertContext);
        }
    }
}

最主要的重點在於 ImpersonateLoggedOnUser Function, 權限高的才能模擬權限低的,符合我 System 身分模擬一般使用者。

這邊附上微軟提供的 Demo 程式。