【问题标题】:Client/Server app, how to create process on remote system as a domain user without transferring that users username/password to the remote system?客户端/服务器应用程序,如何以域用户的身份在远程系统上创建进程,而不将该用户的用户名/密码传输到远程系统?
【发布时间】:2019-04-10 23:40:22
【问题描述】:

我有两个系统都运行我的 C# 客户端/服务器软件代码。我想从计算机 1 创建一个进程作为计算机 2 上的给定 Active Directory 域用户,而不必让我的客户端/服务器软件将 AD 用户的纯文本用户名和密码从计算机 1 发送到计算机 2。

我得到的最接近的是,我似乎可以在计算机 1 上使用函数 KerberosRequestorSecurityToken 来生成 kerberos 票证,然后通过我的客户端/服务器代码将该字节 [] 结果发送到计算机 2,然后它应该能够针对已传递的 byte[] kerberos 票证调用 KerberosReceiverSecurityToken。如果一切正常,那么 KerberosReceiverSecurityToken 将有一个 WindowsIdentity 属性,然后我可以使用该属性在计算机 2 上创建一个进程,并使用最初通过 KerberosRequestorSecurityToken 流在计算机 1 上指定的用户帐户。

我需要使用要模拟的现有域用户帐户,而不是注册服务帐户 SPN。这是我最初发布问题时未能提及的问题的症结所在。

我似乎无法让它工作,但更多,所以我什至不知道这是否真的可能 - 例如这些 API 甚至可以让我做我想做的事吗?

用于生成 kerberos 票证的计算机 1 代码。 byte[] 结果通过我的客户端/服务器应用程序发送到计算机 2。

public static byte[] GetRequestToken(string userName, string password, string domain)
{
    using (var domainContext = new PrincipalContext(ContextType.Domain, domain))
    {
        using (var foundUser = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userName))
        {
            Console.WriteLine("User Principal name" + UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userName).UserPrincipalName);
            string spn = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userName).UserPrincipalName;
            KerberosSecurityTokenProvider k1 = new KerberosSecurityTokenProvider(spn, TokenImpersonationLevel.Impersonation, new NetworkCredential(userName, password, domain));
            KerberosRequestorSecurityToken T1 = k1.GetToken(TimeSpan.FromMinutes(1)) as KerberosRequestorSecurityToken;
            var req = T1.GetRequest();
            return req;
        }
    }
}

计算机 2 获取生成的 byte[] kerberos 票证并尝试接收它以访问 WindowsIdentity,但它引发了一个异常,提示登录无效。

KerberosReceiverSecurityToken receiverToken = new KerberosReceiverSecurityToken(requestToken);
byte[] receiverTokenTicket = receiverToken.GetRequest();

//Identity for impersonation
var impersonatedIdentity = receiverToken.WindowsIdentity;

【问题讨论】:

    标签: c# kerberos windows-identity


    【解决方案1】:

    第一个代码块的来源在功能上是错误的。 SPN 必须是接收票证的目标,而不是请求票证的用户。所以你需要做以下事情...

    1. 在 AD 中注册服务主体帐户(用户/计算机)。
    2. 向该帐户添加服务主体名称,格式为foo/host.domain.com,其中 foo 是您的服务名称,例如httphostmyapp
    3. 客户端需要向foo/host.domain.com申请票证
    4. KerberosReceiverSecurityToken 应该解析 那个

    现在你有了一个不同模拟级别的身份。该模拟级别决定了您是否可以作为该用户启动进程。碰巧......你不能,因为这不是 Windows 安全的工作方式。由于您正在模拟用户,因此您有一个模拟 NT 令牌,而您需要一个主 NT 令牌来启动进程。

    因此,您需要使用 DuplicateTokenEx 将其转换为主令牌,并且您实际上需要成为 SYSTEM 才能执行此操作。

    获得主令牌后,您可以致电CreateProcessAsUser

    此时,您可能已经以用户身份启动了该进程,但它没有任何凭据来访问其他网络属性,例如网络共享或 Web 服务。为此,您可以在服务主体上为任何下游服务启用约束委派。

    另一种可能更安全、更容易的选择是作为低权限用户启动一个工作进程并告诉 那个 进程来模拟。请参阅this answer for more information

    【讨论】:

    • 史蒂夫,我很抱歉,我在最初的问题中是个傻瓜,忘了提及实际问题的症结所在。我需要使用要模拟的现有域用户帐户与注册服务帐户 SPN。
    • 如果 SPN 是 RestrictedKrbHost/[host] 可以在一个系统上以 Domain\User 的身份请求并在另一个系统上以 Domain\User 的身份解密,则代码有效。不过,我看到了关于 RestrictedKrbHost 的模糊安全警告。 PowerShell/WinRM 似乎使用 HTTP/[hostname],它是 HOST/[hostname] SPN 的别名。我的接收过程需要是 SYSTEM - 我假设它默认为机器密钥。我的猜测是后者与 RestrictedKrbHost 更安全,您可能有任何额外信息吗?谢谢
    • 顺便说一句;我接受你的回答。只需添加注释,即 HOST(以及 CN=Directory Service、CN=Windows NT、CN=Services、CN=Configuration、DC=MyDC、DC=com 中 SPNmappings 中的各种 http/etc 别名)作为计算机的默认 SPN 存在AD,以便您可以使用它们而不是添加 SPN。并且需要接收端是SYSTEM/NETWORK SERVICE/etc才能正确解密。
    • 使用主机主体没问题。您可以避免权限问题,但您仍然需要复制令牌并可能配置委派。
    猜你喜欢
    • 1970-01-01
    • 2013-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-31
    • 2011-07-23
    • 1970-01-01
    相关资源
    最近更新 更多