Click here to Skip to main content
Click here to Skip to main content

A Complete Impersonation Demo in C#.NET

, 5 Nov 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
This is a complete Impersonation demo in C#, impersonating a user and accessing its files and HKCU Registry entries.

Introduction

Impersonation is the ability of a thread to execute using a different security information than the process that owns the thread. Typically, a thread in a server application impersonates a client. This allows the server thread to act on behalf of that client to access objects on the server or validate access to the client's own objects.

Background

Under some scenarios, we need to impersonate another Windows account and do some work under that user's session, for example:

  • An enterprise ASP.NET web application provides server administrators' ability to access the server under some specific privilege set; server admin input their NT account information (domain\account + password) on the page, we need to get WinNT Access Token, and then impersonate this server user so that we acquire its specific privilege and do the things only this account can do.
  • We developed a Windows Service which needs internet access periodically, but if a specific user sets an Sock5 proxy to access the internet, then your Windows Service needs to know the Sock5 proxy information so that it could access the internet, and you must impersonate this user and read the settings.

Functionality

I created a local user TempUser which belongs to "Administrators"(make sure to log on using TempUser at least once). I logged on using my own account and I impersonate TempUser and do two things:

  1. Create a folder C:\TempFolder, modify its default privilege; only TempUser has full control of it. I will create a text file under this folder after impersonating to prove the impersonation is successfull.

    Tempfolder1

    Notes: After setting TempUser as the only owner, my current account cannot access this folder except for privilege promotion (I disable UAC; if UAC is enabled, a prompt window will pop up and ask for Admin confirmation).

    Tempfolder2

    In addition, I try to access this folder programmatically under my account, and an UnauthorizedAccessException is thrown!

    Unauthorized

  2. I then access TempUser's HKEY_CURRENT_USER and do the Registry key creation, reading, and deleting.

Code Snippet

We need to invoke three very popular Win32 APIs: LogonUser, DuplicateToken, and RevertToSelf.

[DllImport("advapi32.dll")]
public static extern int LogonUser(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);

///
/// A process should call the RevertToSelf function after finishing
/// any impersonation begun by using the DdeImpersonateClient,
/// ImpersonateDdeClientWindow, ImpersonateLoggedOnUser,
/// ImpersonateNamedPipeClient, ImpersonateSelf,
/// ImpersonateAnonymousToken or SetThreadToken function.
/// If RevertToSelf fails, your application continues to run in the context
/// of the client, which is not appropriate.
/// You should shut down the process if RevertToSelf fails.
/// RevertToSelf Function:
/// http://msdn.microsoft.com/en-us/library/aa379317(VS.85).aspx
///
/// A boolean value indicates the function succeeded or not.
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();

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

An important note: In order to access HKCU, we need to invoke another Win32 API, LoadUserProfile, to acquire the handle of the HKCU under TempUser. The code below, as the highlighted lines 45 and 49 show, after invoking LoadUserProfile, hProfile will be set as the handle to HKCU:

[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
    ///
    /// Specifies the size of the structure, in bytes.
    ///
    public int dwSize;

    ///
    /// This member can be one of the following flags: PI_NOUI or PI_APPLYPOLICY
    ///
    public int dwFlags;

    ///
    /// Pointer to the name of the user.
    /// This member is used as the base name
    /// of the directory in which to store a new profile.
    ///
    public string lpUserName;

    ///
    /// Pointer to the roaming user profile path.
    /// If the user does not have a roaming profile, this member can be NULL.
    ///
    public string lpProfilePath;

    ///
    /// Pointer to the default user profile path. This member can be NULL.
    ///
    public string lpDefaultPath;

    ///
    /// Pointer to the name of the validating domain controller, in NetBIOS format.
    /// If this member is NULL, the Windows NT 4.0-style policy will not be applied.
    ///
    public string lpServerName;

    ///
    /// Pointer to the path of the Windows NT 4.0-style policy file. This member can be NULL.
    ///
    public string lpPolicyPath;

    ///
    /// Handle to the HKEY_CURRENT_USER registry key.
    ///
    public IntPtr hProfile;
}

    [DllImport("userenv.dll", 
               SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool LoadUserProfile(IntPtr hToken, 
                         ref ProfileInfo lpProfileInfo);

    [DllImport("Userenv.dll", 
       CallingConvention = CallingConvention.Winapi, 
       SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool UnloadUserProfile(IntPtr hToken, 
                         IntPtr lpProfileInfo);

Code to execute impersonation:

WindowsIdentity m_ImpersonatedUser;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
const int SecurityImpersonation = 2;
const int TokenType = 1;

try
{
    if (RevertToSelf())
    {
        Console.WriteLine("Before impersonation: " +
                          WindowsIdentity.GetCurrent().Name);

        String userName = "TempUser";
        IntPtr password = GetPassword();

        if (LogonUser(userName, Environment.MachineName, 
                      "!@#$QWERasdf", LOGON32_LOGON_INTERACTIVE,
                      LOGON32_PROVIDER_DEFAULT, ref token) != 0)
        {
            if (DuplicateToken(token, SecurityImpersonation, ref tokenDuplicate) != 0)
            {
                m_ImpersonatedUser = new WindowsIdentity(tokenDuplicate);
                using (m_ImpersonationContext = m_ImpersonatedUser.Impersonate())
                {
                    if (m_ImpersonationContext != null)
                    {
                        Console.WriteLine("After Impersonation succeeded: " + 
                            Environment.NewLine +
                            "User Name: " +
                            WindowsIdentity.GetCurrent(
                               TokenAccessLevels.MaximumAllowed).Name +
                            Environment.NewLine +
                            "SID: " +
                            WindowsIdentity.GetCurrent(
                               TokenAccessLevels.MaximumAllowed).User.Value);

                        #region LoadUserProfile
                        // Load user profile
                        ProfileInfo profileInfo = new ProfileInfo();
                        profileInfo.dwSize = Marshal.SizeOf(profileInfo);
                        profileInfo.lpUserName = userName;
                        profileInfo.dwFlags = 1;
                        Boolean loadSuccess = 
                                LoadUserProfile(tokenDuplicate, ref profileInfo);

                        if (!loadSuccess)
                        {
                            Console.WriteLine("LoadUserProfile() failed with error code: " +
                                              Marshal.GetLastWin32Error());
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }

                        if (profileInfo.hProfile == IntPtr.Zero)
                        {
                            Console.WriteLine(
                                "LoadUserProfile() failed - HKCU handle " + 
                                "was not loaded. Error code: " +
                                Marshal.GetLastWin32Error());
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                        #endregion

                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);

                        // Do tasks after impersonating successfully
                        AccessFileSystem();

                        // Access HKCU after loading user's profile
                        AccessHkcuRegistry(profileInfo.hProfile);

                        // Unload user profile
                        // MSDN remarks
                        // http://msdn.microsoft.com/en-us/library/bb762282(VS.85).aspx
                        // Before calling UnloadUserProfile you should
                        // ensure that all handles to keys that you have opened in the
                        // user's registry hive are closed. If you do not
                        // close all open registry handles, the user's profile fails
                        // to unload. For more information, see Registry Key
                        // Security and Access Rights and Registry Hives.
                        UnloadUserProfile(tokenDuplicate, profileInfo.hProfile);

                        // Undo impersonation
                        m_ImpersonationContext.Undo();
                    }
                }
            }
            else
            {
                Console.WriteLine("DuplicateToken() failed with error code: " + 
                                  Marshal.GetLastWin32Error());
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }
}
catch (Win32Exception we)
{
    throw we;
}
catch
{
    throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
    if (token != IntPtr.Zero) CloseHandle(token);
    if (tokenDuplicate != IntPtr.Zero) CloseHandle(tokenDuplicate);

    Console.WriteLine("After finished impersonation: " + 
                      WindowsIdentity.GetCurrent().Name);
}

AccessFileSystem method:

private static void AccessFileSystem()
{
    // Access file system %appdata% will be "C:\Users\TempUser\appdata\Roaming"
    String appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
    File.AppendAllText("C:\\TempFolder\\Temp.txt", "some text...");
}

AccessHkcuRegistry method:

private static void AccessHkcuRegistry(IntPtr hkcuHandle)
{
    // Access registry HKCU
    using (SafeRegistryHandle safeHandle = 
              new SafeRegistryHandle(hkcuHandle, true))
    {
        using (RegistryKey tempUserHKCU = RegistryKey.FromHandle(safeHandle))
        {
            // Unum all sub keys under tempuser's HKCU
            String[] keys = tempUserHKCU.GetSubKeyNames();

            // Create a new sub key under tempuser's HKCU
            using (RegistryKey tempKeyByWayne = 
                   tempUserHKCU.CreateSubKey("TempKeyByWayne"))
            {
                // Ensure priviledge
                //RegistrySecurity registrySecurity = new RegistrySecurity();
                //RegistryAccessRule accessRule = 
                //   new RegistryAccessRule(Environment.MachineName + "\\" + userName,
                //       RegistryRights.TakeOwnership,
                //       InheritanceFlags.ContainerInherit,
                //       PropagationFlags.None,
                //       AccessControlType.Allow);
                //registrySecurity.SetAccessRule(accessRule);
                //tempKeyByWayne.SetAccessControl(registrySecurity);

                // Create a new String value under created TempKeyByWayne subkey
                tempKeyByWayne.SetValue("StrType", 
                     "TempContent", RegistryValueKind.String);

                // Read the value
                using (RegistryKey regKey = 
                         tempUserHKCU.OpenSubKey("TempKeyByWayne"))
                {
                    String valueContent = regKey.GetValue("StrType") as String;
                    Console.WriteLine(valueContent);
                }

                // Delete created TempKeyByWayne subkey
                tempUserHKCU.DeleteSubKey("TempKeyByWayne");
                tempKeyByWayne.Close();
            }
        }
    }
}

Impersonation result and verification

Temp.txt created

TempFolder

"TempKeyByWayne" created under HKCU

Registry

References

Happy coding Smile | :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Wayne Ye
Software Developer (Senior) SAP Labs Shanghai
China China
Wayne is a software developer, Tech Lead and also a geek. He has more than 6 years' experience in Web development(server: ASP.NET (MVC), Web Service, IIS; Client: HTML/CSS/JavaScript/jQuery/AJAX), Windows development (Winform, Windows Service, WPF/Silverlight, Win32 API and WMI) and SQL Server. Deep understanding of GOF Design Patterns, S.O.L.i.D principle, MVC, MVVM, Domain Driven Design, SOA, REST and AOP.
 
Wayne's Geek Life http://WayneYe.com
 
Infinite passion on programming!
Follow on   Twitter

Comments and Discussions

 
GeneralCan't access to different server location PinmemberAhsan Murshed21-Nov-11 19:16 
GeneralRe: Can't access to different server location PinmemberWayne Ye24-Nov-11 15:25 
GeneralBig problem PinmemberDjordjefasdf9-Mar-11 21:45 
Generalhave 5 PinmemberPranay Rana30-Dec-10 19:37 
GeneralMy vote of 5 Pinmemberjohannesnestler6-Nov-10 2:06 
GeneralRe: My vote of 5 PinmemberWayne Ye7-Nov-10 1:57 
GeneralRe: My vote of 5 Pinmembershakil03040037-Nov-10 3:24 
General[My vote of 4] Impersonator PinmemberKing_kLAx5-Nov-10 5:29 
GeneralRe: [My vote of 4] Impersonator PinmemberWayne Ye5-Nov-10 5:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141022.1 | Last Updated 5 Nov 2010
Article Copyright 2010 by Wayne Ye
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid