简体   繁体   English

C ++:如何在Windows Credential Manager中以编程方式创建本地用户登录凭据,以便“ runas / savecred”可以使用它?

[英]C++: How to programmatically create a local user logon credential in Windows Credential Manager so “runas /savecred” can use it?

I am trying to set an executable to run as a local low-privileged user (different from the current logged-on user) by default. 我试图将可执行文件默认设置为以本地低特权用户身份运行(不同于当前登录的用户)。 The Windows command "runas /savecred /user:username appname.exe" can do it, but it requires the current logged-on user to manually type in password of the different user at the first time. Windows命令“ runas / savecred / user:username appname.exe”可以执行此操作,但是它要求当前登录的用户第一次手动输入其他用户的密码。 Ideally, I want the installer of the program set the credential, so no interaction would be needed for the current logged-on user. 理想情况下,我希望程序的安装程序设置凭据,因此当前登录的用户不需要交互。 (I don't want to make the program as a Windows Service for other good reasons.) (出于其他良好的原因,我不想将该程序作为Windows服务。)

When I ran the "runas" command, I observed that a new "interactive logon" Windows Credential got created in the Credential Manager, where "Internet or network address" was set as "$computer_name\\$username (Interactive Logon)", "User name" was set as "$computer_name\\$username", "password" was displayed as "********", and "Persistence" was set as "Enterprise". 当我运行“ runas”命令时,我发现在凭据管理器中创建了一个新的“交互式登录” Windows凭据,其中“ Internet或网络地址”设置为“ $ computer_name \\ $ username(交互式登录)”,“用户名”设置为“ $ computer_name \\ $ username”,“密码”显示为“ ********”,“持久性”设置为“企业”。

Based on that observation, I found a Win32 API, CredWrite, and but I'm having trouble to make it work. 基于该观察,我找到了Win32 API,CredWrite,但是我无法使其正常工作。 I got error code 87 (ERROR_INVALID_PARAMETER - The parameter is incorrect.) when CredWrite was called. 调用CredWrite时收到错误代码87(ERROR_INVALID_PARAMETER-参数不正确。)。

#include <windows.h>
#include <wincred.h>
#include <tchar.h>

void main ()
{
    char* password = "randompassword";
    DWORD blobsize= 1 + strlen(password);

    CREDENTIAL cred = {0};
    cred.Flags = CRED_FLAGS_USERNAME_TARGET;
    cred.Type = CRED_TYPE_DOMAIN_PASSWORD;
    cred.TargetName = L"computername\\username";
    cred.CredentialBlobSize = blobsize;
    cred.CredentialBlob = (LPBYTE) password;
    cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
    cred.UserName = L"computername\\username";

    if (!CredWrite(&cred, 0))
    {
        std::cerr << GetLastError() << std::endl;
    }
}

The references that I have been looking at are listed at the bottom. 我一直在查看的参考文献列在底部。 I was able to create a CRED_TYPE_GENERIC type credential but it was not recognized by RUNAS. 我能够创建CRED_TYPE_GENERIC类型的凭据,但是RUNAS无法识别它。 I'm really puzzled on how to set fields like TargetName and Type in order to programmatically create an interactive logon credential as RUNAS does. 我真的很困惑如何设置像TargetName和Type这样的字段,以便像RUNAS一样以编程方式创建交互式登录凭据。 The official MSDN documentation isn't clear and there is no example. 官方的MSDN文档尚不清楚,也没有示例。

CredWrite : https://msdn.microsoft.com/en-us/library/windows/desktop/aa375187(v=vs.85).aspx CREDENTIAL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx The sample code for creating a CRED_TYPE_GENERIC type credential: How do I store and retrieve credentials from the Windows Vault credential manager? CredWrite: https ://msdn.microsoft.com/zh-cn/library/windows/desktop/aa375187( v= vs.85).aspx凭据: https : //msdn.microsoft.com/zh-cn/library/ Windows / Desktop / aa374788(v = vs.85).aspx用于创建CRED_TYPE_GENERIC类型凭据的示例代码: 如何从Windows Vault凭据管理器存储和检索凭据?

There is an undocumented Flag of the CREDENTIAL structure that you pass to CredWrite. 您将传递给CredWrite的CREDENTIAL结构的未记录标志。

If you call the API with flag = 8196, you will get the result you desire 如果使用标志= 8196调用API,则将获得所需的结果

char* password1 = "randompassword";
DWORD blobsize = strlen(password1); // Do NOT add 1 here.

CREDENTIAL cred = { 0 };
cred.Flags = 8196;
cred.Type = 2;
cred.TargetName = L"computername\\username";
cred.CredentialBlobSize = blobsize;
cred.CredentialBlob = (LPBYTE)password1;
cred.Persist = 3;
cred.UserName = L"computername\\username";

if (!CredWrite(&cred, 0))
{
    std::cerr << GetLastError() << std::endl;
}

The ERROR_INVALID_PARAMETER error (error code 87) was caused by the wrong value of CredentialBlobSize. ERROR_INVALID_PARAMETER错误(错误代码87)是由CredentialBlobSize的错误值引起的。 "If the Type member is CRED_TYPE_DOMAIN_PASSWORD, this member contains the plaintext Unicode password for UserName. The CredentialBlob and CredentialBlobSize members do not include a trailing zero character." “如果Type成员为CRED_TYPE_DOMAIN_PASSWORD,则此成员包含UserName的纯文本Unicode密码。CredentialBlob和CredentialBlobSize成员不包含结尾的零字符。” (CREDENTIAL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx ) (凭据: https ://msdn.microsoft.com/zh-cn/library/windows/desktop/aa374788( v= vs.85).aspx)

With this fix a Windows Credential (not Generic or Certificate-based Credential) can be created without error. 有了此修复程序,可以无错误地创建Windows凭据(不是通用或基于证书的凭据)。

#include <windows.h>
#include <wincred.h>
#include <tchar.h>

void main ()
{
    char* password = "randompassword";
    DWORD blobsize= strlen(password); // Do NOT add 1 here.

    CREDENTIAL cred = {0};
    cred.Flags = CRED_FLAGS_USERNAME_TARGET;
    cred.Type = CRED_TYPE_DOMAIN_PASSWORD;
    cred.TargetName = L"computername\\username";
    cred.CredentialBlobSize = blobsize;
    cred.CredentialBlob = (LPBYTE) password;
    cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
    cred.UserName = L"computername\\username";

    if (!CredWrite(&cred, 0))
    {
        std::cerr << GetLastError() << std::endl;
    }
} 

However, there is still a subtle difference. 但是,仍然存在细微的差异。 The "Internet or network address" of the one that I created was "$computer_name\\$username ( Windows Identity )" which cannot be recognized by RUNAS. 我创建的地址的“ Internet或网络地址”是RUNAS无法识别的“ $ computer_name \\ $ username( Windows Identity )”。 The one created by RUNAS was "$computer_name\\$username ( Interactive Logon )". RUNAS创建的一个是“ $ computer_name \\ $ username( 交互式登录 )”。 How does that bit get set? 如何设置该位?

I can use CredRead to read the credential that I created, but fail to do that for the one created by RUNAS. 我可以使用CredRead读取我创建的证书,但是对于RUNAS创建的证书却无法做到。

CREDENTIAL cred = { 0 };
PCREDENTIAL pcred = &cred;
// Works for Windows Identity. TargetName is case-insensitive.
CredRead(L"$computer_name\$username", CRED_TYPE_DOMAIN_PASSWORD, 0, &pcred); 
// Fails for Interactive Logon with error code 87 or 1168. I tried all possible CRED_TYPE_*.
CredRead(L"$computer_name\$username", CRED_TYPE_*, 0, &pcred); 

Similar threads: 1. Runas Command, Bypass Credentials ( https://community.spiceworks.com/topic/485852-runas-command-bypass-credentials ) 2 .runas /savecred… don't accept cmdkey /add (credentials) ( runas /savecred... don't accept cmdkey /add (credentials) ) 3. runas with the /savecred switch does not accept a credential stored by the cmdkey command ( https://social.technet.microsoft.com/Forums/windowsserver/en-US/c0e398cc-224f-4390-bc69-0ee544f9b5cb/runas-with-the-savecred-switch-does-not-accept-a-credential-stored-by-the-cmdkey-command?forum=winserversecurity ) 类似的线程:1. Runas命令,绕过凭据( https://community.spiceworks.com/topic/485852-runas-command-bypass-credentials)2 .runas / savecred…不接受cmdkey / add(凭据)( runas / savecred ...不接受cmdkey / add(凭据) )3.使用/ savecred开关的runas不接受cmdkey命令存储的凭据( https://social.technet.microsoft.com/Forums/ WindowsServer / zh-CN / c0e398cc-224f-4390-bc69-0ee544f9b5cb / runas-with-savecred开关不接受由cmdkey命令存储的凭据吗?forum = winserversecurity

CMDKEY command allowed me to create a credential closest to the one created by RUNAS. CMDKEY命令允许我创建与RUNAS创建的证书最接近的证书。 Both look the same in the result returned by "cmdkey /list". 两者在“ cmdkey / list”返回的结果中看起来相同。 However, the one created by CMDKEY is not honored by RUNAS. 但是,RUNAS不支持CMDKEY创建的一个。

cmdkey /add:Domain:interactive=$computer_name\$username1 /user:$computer_name\$username1 /pass:$password

cmdkey /list

# created by CMDKEY
Target: Domain:interactive=$computer_name\$username1
Type: Domain Password
User: $computer_name\$username1

# created by RUNAS
Target: Domain:interactive=$computer_name\$username2
Type: Domain Password
User: $computer_name\$username2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM