如何解密从安全桌面获得的身份验证缓冲区
本文关键字:桌面 缓冲区 身份验证 安全 何解密 解密 | 更新日期: 2023-09-27 18:20:52
我试图请求用户在安全桌面上输入凭据,但我找不到解密生成的身份验证缓冲区的方法:
凭证通过:获得
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);
凭证使用:解包
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);
如果像上面的例子一样在CredUIPromptForWindowsCredentials
中指定CREDUIWIN_SECURE_PROMPT
,那么CredUnPackAuthenticationBuffer
将使用ERROR_NOT_CAPABLE
失败。如果我使用CREDUIWIN_GENERIC
而不是安全桌面,它会成功。
如何从安全桌面上的此类加密凭据中获取名称、域和密码或者,如果不可能的话,我怎么能普遍使用它们
凭据提供程序使用CredProtectW
或CredProtectEx
加密密码。具有CRED_PACK_PROTECTED_CREDENTIALS
的CredUnPackAuthenticationBufferW
尝试通过使用CredUnprotectW
或CredUnprotectEx
来解密认证缓冲器中的凭证(密码)。错误-error_NOT_CAPABLE完全由CredUnprotectW
返回。
用于加密凭据的安全上下文与用于解密凭据的安全环境不同
这是因为,当CREDUIWIN_SECURE_PROMPT
用于收集用户凭据时,系统在上下文中作为系统进程运行LogonUI.exe。并在此过程中对密码进行加密。当您在另一个安全上下文-用户进程中运行时。
如果您在令牌中没有启用任何管理员组,则无法对此执行任何操作——您可以通过LsaLogonUser
将此凭据传递给系统,但不能解密密码。如果您有";admin";,您可以使用系统令牌进行模拟,然后在线程进行模拟时调用CredUnPackAuthenticationBuffer(CRED_PACK_PROTECTED_CREDENTIALS,..)
。
或者,您可以在不使用CRED_PACK_PROTECTED_CREDENTIALS
的情况下调用CredUnPackAuthenticationBuffer(0,..)
,然后在模拟系统令牌时通过调用CredUnprotectW
来分离未受保护的密码。或者,如果您不能模拟系统令牌,那么您可以获得用户名的最小值。但它无法访问密码。如果您使用返回的密码调用CredIsProtectedW
,您将获得CredTrustedProtection
。
模拟系统令牌的代码:
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;
}
您可以将其用作:
if (0 <= ImpersonateSystemToken())
{
if (CredUnPackAuthenticationBuffer(CRED_PACK_PROTECTED_CREDENTIALS, pvOutAuthBuffer, ulOutAuthBufferSize,
szUserName, &cchUserName, szDomainName, &cchDomainName, szPassword, &cchPassword))
{
//...
}
else
{
GetLastError();
}
SetThreadToken(0, 0);
}
或者通过这种方式:
if (CredUnPackAuthenticationBuffer(0, pvOutAuthBuffer, ulOutAuthBufferSize,
szUserName, &cchUserName, szDomainName, &cchDomainName, szPassword, &cchPassword))
{
HRESULT hr = DecryptPassword(szPassword, cchPassword);
// ...
}
else
{
GetLastError();
}
和
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;
}