如何解密从安全桌面获取的身份验证缓冲区

fru*_*ela 5 c# winapi credentials

我正在尝试请求用户在安全桌面上输入凭据,但我找不到解密生成的身份验证缓冲区的方法:

凭证通过以下方式获得:

CREDUI_INFO cui = new CREDUI_INFO()
{
    cbSize = (uint)Marshal.SizeOf(typeof(CREDUI_INFO)),
    pszMessageText = "MyMessage",
    pszCaptionText = "MyCaption",
};

bool save = false;
uint authPackage = 0U;
IntPtr authBuffer;
uint authBufferSize;

uint error = CredUIPromptForWindowsCredentials(
                ref cui,
                0U,
                ref authPackage,
                IntPtr.Zero,
                0U,
                out authBuffer,
                out authBufferSize,
                ref save,
                CREDUIWIN_SECURE_PROMPT);
Run Code Online (Sandbox Code Playgroud)

凭据解压缩:

uint userBufferSize = CREDUI_MAX_USERNAME_LENGTH;
uint domainBufferSize = CRED_MAX_STRING_LENGTH;
uint passwordBufferSize = CREDUI_MAX_PASSWORD_LENGTH;
StringBuilder userBuffer = new StringBuilder((int)userBufferSize);
StringBuilder domainBuffer = new String((int)domainBufferSize);
IntPtr passwordBuffer = Marshal.AllocCoTaskMem((int)passwordBufferSize);

bool success = CredUnPackAuthenticationBuffer(
                CRED_PACK_PROTECTED_CREDENTIALS,
                authBuffer, authBufferSize
                userBuffer, ref userBufferSize,
                domainBuffer, ref domainBufferSize,
                passwordBuffer, ref passwordBufferSize);
Run Code Online (Sandbox Code Playgroud)

如果我CREDUIWIN_SECURE_PROMPTCredUIPromptForWindowsCredentials上面的例子中指定in ,则CredUnPackAuthenticationBuffer失败ERROR_NOT_CAPABLE.如果我使用CREDUIWIN_GENERIC而不是安全桌面,它会成功.

如何从安全桌面获取此类加密凭据的名称,域和密码? 或者如果那是不可能的话我怎么能一般使用它们呢?

RbM*_*bMm 2

凭证提供者使用CredProtectWCredProtectEx用于加密密码。withCredUnPackAuthenticationBufferW尝试使用或CRED_PACK_PROTECTED_CREDENTIALS解密身份验证缓冲区中的凭据(密码)。错误 - ERROR_NOT_CAPABLE由 准确返回。CredUnprotectWCredUnprotectExCredUnprotectW

用于加密凭证的安全上下文与用于解密凭证的安全上下文不同。

这是因为,当CREDUIWIN_SECURE_PROMPT用于收集用户凭据时,系统会在上下文中作为系统进程运行LogonUI.exe 。密码在此过程中加密。当您在另一个安全上下文 - 用户进程中运行时。

如果您没有在令牌中启用任何管理员组,则您无法对此执行任何操作 - 您可以将此凭据传递给系统,例如通过LsaLogonUser,但您无法解密密码。如果您有“admin”,您可以模拟系统令牌,然后CredUnPackAuthenticationBuffer(CRED_PACK_PROTECTED_CREDENTIALS,..)在您的线程模拟时调用。

或者您可以CredUnPackAuthenticationBuffer(0,..) 进行调用,然后通过在模拟系统令牌时CRED_PACK_PROTECTED_CREDENTIALS调用来分离不受保护的密码。CredUnprotectW或者,如果您无法模拟系统令牌,那么您至少可以获得用户名。但无法访问密码。如果您调用CredIsProtectedW返回的密码,您将得到CredTrustedProtection

模拟系统token的代码:

inline ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

NTSTATUS ImpersonateSystemToken(PSYSTEM_PROCESS_INFORMATION pspi)
{
    NTSTATUS status;

    ULONG NextEntryOffset = 0;

    do
    {
        (ULONG_PTR&)pspi += NextEntryOffset;

        HANDLE hProcess, hToken;

        CLIENT_ID ClientId = { pspi->UniqueProcessId };

        if (ClientId.UniqueProcess)
        {
            const SECURITY_QUALITY_OF_SERVICE sqos = {
                sizeof (sqos), SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
            };

            const OBJECT_ATTRIBUTES oa_sqos = { sizeof(oa_sqos), 0, 0, 0, 0, const_cast<SECURITY_QUALITY_OF_SERVICE*>(&sqos) };

            if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION,
                const_cast<POBJECT_ATTRIBUTES>(&oa_sqos), &ClientId))
            {
                status = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE|TOKEN_QUERY, &hToken);

                NtClose(hProcess);

                if (0 <= status)
                {
                    TOKEN_STATISTICS ts;
                    if (0 <= (status = NtQueryInformationToken(hToken, TokenStatistics, &ts, sizeof(ts), &ts.DynamicCharged)))
                    {
                        status = STATUS_NOT_FOUND;

                        static const LUID SystemLuid = SYSTEM_LUID;

                        if (ts.AuthenticationId.LowPart == SystemLuid.LowPart &&
                            ts.AuthenticationId.HighPart == SystemLuid.HighPart)
                        {
                            status = NtDuplicateToken(hToken, TOKEN_IMPERSONATE|TOKEN_ADJUST_PRIVILEGES,
                                const_cast<POBJECT_ATTRIBUTES>(&oa_sqos), FALSE, TokenImpersonation, &hProcess);
                        }
                    }

                    NtClose(hToken);

                    if (0 <= status)
                    {
                        const TOKEN_PRIVILEGES tp_TCB = { 1, { { { SE_TCB_PRIVILEGE }, SE_PRIVILEGE_ENABLED } } };

                        if (STATUS_SUCCESS == NtAdjustPrivilegesToken(hProcess, FALSE,
                            const_cast<TOKEN_PRIVILEGES*>(&tp_TCB), sizeof(tp_TCB), 0, 0))
                        {
                            status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hProcess, sizeof(hProcess));
                        }
                        else
                        {
                            status = -1;
                        }

                        NtClose(hProcess);

                        if (-1 != status)
                        {
                            return status;
                        }
                    }
                }
            }
        }

    } while (NextEntryOffset = pspi->NextEntryOffset);

    return STATUS_UNSUCCESSFUL;
}

NTSTATUS ImpersonateSystemToken()
{
    BOOLEAN WasEnabled;
    RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &WasEnabled);

    NTSTATUS status;

    ULONG cb = 0x40000;

    do
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (PBYTE buf = new BYTE[cb += 0x1000])
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                status = ImpersonateSystemToken((PSYSTEM_PROCESS_INFORMATION)buf);

                if (status == STATUS_INFO_LENGTH_MISMATCH)
                {
                    status = STATUS_UNSUCCESSFUL;
                }
            }

            delete [] buf;
        }

    } while(status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}
Run Code Online (Sandbox Code Playgroud)

您可以将其用作:

    if (0 <= ImpersonateSystemToken())
    {
        if (CredUnPackAuthenticationBuffer(CRED_PACK_PROTECTED_CREDENTIALS, pvOutAuthBuffer, ulOutAuthBufferSize,
            szUserName, &cchUserName, szDomainName, &cchDomainName, szPassword, &cchPassword))
        {
            //...
        }
        else
        {
            GetLastError();
        }

        SetThreadToken(0, 0);
    }
Run Code Online (Sandbox Code Playgroud)

或者这样:

    if (CredUnPackAuthenticationBuffer(0, pvOutAuthBuffer, ulOutAuthBufferSize,
        szUserName, &cchUserName, szDomainName, &cchDomainName, szPassword, &cchPassword))
    {
        HRESULT hr = DecryptPassword(szPassword, cchPassword);
        // ...
    }
    else
    {
        GetLastError();
    }
Run Code Online (Sandbox Code Playgroud)

HRESULT DecryptPassword(PWSTR szPassword, ULONG cchPassword)
{
    CRED_PROTECTION_TYPE ProtectionType;

    HRESULT hr = HRESULT_FROM_WIN32(BOOL_TO_ERROR(CredIsProtectedW(szPassword, &ProtectionType)));

    if (S_OK == hr)
    {
        ULONG cch = 0;
        PWSTR pszCredentials = 0;

        switch (ProtectionType)
        {
        case CredUnprotected:
            pszCredentials = szPassword, cch = cchPassword;
            break;

        case CredTrustedProtection:
        case CredForSystemProtection:
            if (0 > (hr = ImpersonateSystemToken()))
            {
                hr |= FACILITY_NT_BIT;
                break;
            }
            [[fallthrough]];

        case CredUserProtection:
            while (ERROR_INSUFFICIENT_BUFFER == (hr = BOOL_TO_ERROR(CredUnprotectW(
                FALSE, szPassword, cchPassword, pszCredentials, &cch))))
            {
                if (pszCredentials)
                {
                    break;
                }

                pszCredentials = (PWSTR)alloca(cch * sizeof(WCHAR));
            }
            if (ProtectionType != CredUserProtection) SetThreadToken(0, 0);
            hr = HRESULT_FROM_WIN32(hr);
            break;
        default:
            hr = HRESULT_FROM_NT(STATUS_NOT_IMPLEMENTED);
        }

        if (0 <= hr)
        {
            DbgPrint("pass= \"%.*S\"\n", cch, pszCredentials);
        }
    }

    return hr;
}
Run Code Online (Sandbox Code Playgroud)