How can I impersonate a user across un-trusted domains?
Asked Answered
S

2

6

Machine A and Machine B are in different domains. Machine A uses a VPN to gain access to Machine B's network.

I am trying to impersonate a user for the purpose of using Microsoft.Web.Adminstration to do some administrationy things in IIS on Machine B.

What I have tried

  • I have tried the solution here: https://forums.iis.net/t/1162205.aspx?Using+Microsoft+Web+Administration+on+a+remote+machine+not+in+the+domain. The returnValue from LogonUser is false for me. When I started writing this I thought maybe the difference was that the poster was working with machines in two different but trusted domains though now I see one of his machines was just in a workgroup. I'm honestly not sure how that scenario compares to mine... whether it should be effectively the same or not.

  • I also tried another solution that used different values for the logon type (NewCredential instead of Interactive) and different logon provider (WinNt50 instead of Default) because it claimed it addressed my issue of the domains being untrusted.

  • Also tried combining the two solutions, but so far nothing has worked.

Extra Info:

I started out doing this in an asp.net mvc project, but switched to a console application when the 1st solution required me to call CoInitializeSecurity which, apparently, requires that it has not been called beforehand and I didn't know how to prevent it in an mvc project (in a console application I just had to disable the Visual Studio Hosting Process).

And I should mention that I am able to use the NetworkCredential class with the NetworkConnection class (https://gist.github.com/AlanBarber/92db36339a129b94b7dd) to connect to the target machine and write files. So I absolutely know that the user/pass work and I am able to use them from the client machine for file access. I just can't figure out how to impersonate with them.

I don't think I can put a bounty on this right away, but if someone is able to provide a solution I will add a bounty as soon as I can and give it to you.

Sherly answered 27/4, 2018 at 4:18 Comment(5)
Thank-you for update good sirPooi
Thank you for the improved formatting.Sherly
@BVernon, are you forgetting to include the domain name when impersonating? (such as <domain>\username -or- username@domain)Indicatory
@Indicatory no, but I wish it were that simple! :)Sherly
As you mentioned it is likely a trust issue. You might have to use delegation to establish trust between the 2 domains and machines. Aside from going that route, what about a web service on Machine B's network using an AppPool with the appropriate credentials to affect IIS on Machine B's network. So it would be like a proxy, or you create a proxy script on Machine B's network and access it from Machine A's network since you are able to access the network via VPN.Indicatory
S
2

I am not sure what you're trying to do, I guess you would like to execute some code "as the user" that you use in the VPN tunel and not the user that you used to login, am I correct?

if so you would need to "impersonate", can you try to execute you application using "Run As" and then execute your code, should execute at the user on the VPN domain (if authorised on your pc)

You can then keep using the "Run As" method or have a look at:

[DllImport("advapi32.DLL")]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport("advapi32.DLL")]
public static extern bool RevertToSelf();

void SomeMethod()
{
   IntPtr phToken = IntPtr.Zero;
   ImpersonateLoggedOnUser(phToken);
   //... some code
   RevertToSelf();
}

The bummer about hardcoded usernames is that

  1. users sometimes (need to) change passwords
  2. Sorce code containing passwords is generally "exploitable" by anyone with ILSpy
  3. Passwords provided to developers in the source code was a bit of an issue with Uber sometime ago

    Run As is "as safe" as providing VPN credentials to some one... you can provide a "reminder" if the user starts the application in his own credentials by comparing the user with the reverted one.

An other option is to use the windows credentials manager (type credential manger or open it from your control panel) and you will see something like this. go ahead and try and see if your target can be configured with this credentials provider. You'd add something like this, do not forget the port if you need a port, the sample shows a trusted connection on a remote server Credentials Manager to SQL server. If you find that this works then you can manage user's via the credentials manager API as answered here. I'd prompt the user for user name and password and store those when a connection fails.

Somatic answered 7/5, 2018 at 16:13 Comment(4)
I wish I could. Problem is that my local Domain and the Domain I vpn into are totally separate... meaning that I can't "Run as" the remote domain user because my local Domain does not recognize or communicate with the remote Domain.Sherly
Well if there is no "trust" between both domains and your security engineers are worth their money you'd not be able to execute in code locally using an un-trusted account. I guess you'd need to be considering executing the code on the server using a service that you then control via a socket. I would not use a web service as it could be down. alternatively you can look at serverfault.com/questions/429426/…Somatic
If we really wanted to setup trust we probably could, but there generally isn't a need and this task doesn't justify the need... but there is also no security issue with doing the task this way if I could get it to work. I've given up on the approach for now though, but as mentioned I can run code to access files on the remote domain using the creds for my account on that domain. So I don't yet see why this shouldn't also be possible.Sherly
Code must execute in the context of "someone", this can be a service or a user. If you would like your local maschine to accept the user there needs to be a trust between the LDAP's to exchange credentials in order to evaluate if the user has access to a resource. just because you can access a server doesn't mean you can do this in an "unauthorized way" The same is for the remote machine taking control of your execution security context. Have a look at msdn.microsoft.com/en-us/library/cc723472.aspxSomatic
I
1

Perhaps this will help, I have a solution working using a similar approach.

Imports

using System.Security.Principal;
using System.Runtime.InteropServices;

private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;

static WindowsImpersonationContext impersonationContext;

[DllImport("advapi32.dll")]
public static extern int LogonUserA(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);

Methods:

static private bool impersonateValidUser(string userName, string domain, string password)
        {
            WindowsIdentity tempWindowsIdentity;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            if (RevertToSelf())
            {
                if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                        if (impersonationContext != null)
                        {
                            CloseHandle(token);
                            CloseHandle(tokenDuplicate);
                            return true;
                        }
                    }
                }
            }
            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (tokenDuplicate != IntPtr.Zero)
                CloseHandle(tokenDuplicate);
            return false;
        }

        static private void undoImpersonation()
        {
            impersonationContext.Undo();
        }

I hope this gets you there!

Indicatory answered 9/5, 2018 at 20:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.