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:
- 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.
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).
In addition, I try to access this folder programmatically under my account, and an UnauthorizedAccessException
is thrown!
- 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);
[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
{
public int dwSize;
public int dwFlags;
public string lpUserName;
public string lpProfilePath;
public string lpDefaultPath;
public string lpServerName;
public string lpPolicyPath;
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
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);
AccessFileSystem();
AccessHkcuRegistry(profileInfo.hProfile);
UnloadUserProfile(tokenDuplicate, profileInfo.hProfile);
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()
{
String appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
File.AppendAllText("C:\\TempFolder\\Temp.txt", "some text...");
}
AccessHkcuRegistry method:
private static void AccessHkcuRegistry(IntPtr hkcuHandle)
{
using (SafeRegistryHandle safeHandle =
new SafeRegistryHandle(hkcuHandle, true))
{
using (RegistryKey tempUserHKCU = RegistryKey.FromHandle(safeHandle))
{
String[] keys = tempUserHKCU.GetSubKeyNames();
using (RegistryKey tempKeyByWayne =
tempUserHKCU.CreateSubKey("TempKeyByWayne"))
{
tempKeyByWayne.SetValue("StrType",
"TempContent", RegistryValueKind.String);
using (RegistryKey regKey =
tempUserHKCU.OpenSubKey("TempKeyByWayne"))
{
String valueContent = regKey.GetValue("StrType") as String;
Console.WriteLine(valueContent);
}
tempUserHKCU.DeleteSubKey("TempKeyByWayne");
tempKeyByWayne.Close();
}
}
}
}
Impersonation result and verification
Temp.txt created
"TempKeyByWayne" created under HKCU
References
Happy coding :)