0

I have two systems both running my C# client/server software code. I want to from Computer 1 create a process as a given Active Directory domain user on Computer 2 without having to have my client/server software send the plain text username and password of the AD user from Computer 1 to Computer 2.

The closest I have gotten is it seemed like I could use the function KerberosRequestorSecurityToken on Computer 1 to generate a kerberos ticket and then send that byte[] result via my client/server code to Computer 2 where it should then be able to call KerberosReceiverSecurityToken against the byte[] kerberos ticket that was passed. If all that works then KerberosReceiverSecurityToken would have a WindowsIdentity property that I could then use to create a process on Computer 2 with the user account initially specified on Computer 1 via the KerberosRequestorSecurityToken flow.

I need to use existing Domain User accounts that I want to impersonate vs. registering a service account SPN. This is the crux of the issue that I failed to mention when I originally posted the question.

I cannot seem to get this to work but more so I do not even know if this is actually possible - e.g. should these APIs even allow me to do what I want?

Computer 1 code to generate kerberos ticket. The byte[] result of this is sent via my client/server app to Computer 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;
        }
    }
}

Computer 2 takes that resulting byte[] kerberos ticket and attempts to recieve it to access a WindowsIdentity but it throws an exception saying invalid logon.

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

//Identity for impersonation
var impersonatedIdentity = receiverToken.WindowsIdentity;
Paul W.
  • 299
  • 1
  • 5
  • 15

1 Answers1

2

The origin of the first block of code is functionally wrong. The SPN must be the target receiving the ticket, not the user requesting the ticket. So you need to do the following things...

  1. Register a service principal account (user/computer) in AD.
  2. Add a service principal name to that account of the form foo/host.domain.com where foo is your service name, e.g. http or host, or myapp.
  3. Client needs to request a ticket to foo/host.domain.com
  4. KerberosReceiverSecurityToken should parse that ticket

Now you have an identity of some varying impersonation level. That impersonation level dictates whether you can start a process as that user. As it happens... you just can't because that's not how Windows security works. You have an impersonation NT token since you're impersonating a user, whereas you need a primary NT token to start processes.

So you need to convert that to a primary token using DuplicateTokenEx, and you effectively need to be SYSTEM to do that.

Once you have the primary token you can call CreateProcessAsUser.

At this point you might have started the process as the user, but it doesn't have any credentials to reach other network properties like network shares or web services. You can enable constrained delegation on the service principal to any downstream services for that.

An alternative, and probably safer and easier, option is instead to start a worker process as a low privilege user and tell that process to impersonate. See this answer for more information on that.

Steve
  • 4,463
  • 1
  • 19
  • 24
  • Steve, my apologies I was a bone head in my original question and forgot to mention the crux of the actual issue. I need to use existing Domain User accounts that I want to impersonate vs. registering a service account SPN. – Paul W. Apr 12 '19 at 16:54
  • The code works if SPN is RestrictedKrbHost/[host] can request as Domain\User on one system and decrypt as Domain\User on another. I saw vague security warnings about RestrictedKrbHost though. PowerShell/WinRM seems to use HTTP/[hostname] which is an alias to HOST/[hostname] SPN. That my receiving process need to be SYSTEM - i assume so it defaults to machine keys. My guess is the latter vs. RestrictedKrbHost is safer, any extra info you might have? thank you – Paul W. Apr 12 '19 at 16:59
  • BTW; I am accepting your answer. Just adding the note that HOST (and the various http/etc aliases in SPNmappings in CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=MyDC,DC=com) exist as default SPNs for Computers in AD so you can use them vs. adding an SPN. And need the receiving end to be SYSTEM/NETWORK SERVICE/etc so it decrypts properly. – Paul W. Apr 12 '19 at 17:17
  • Using the host principal is fine. You save yourself permissions issues, but you still need to duplicate tokens and potentially configure delegation. – Steve Apr 12 '19 at 21:10