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 inputs 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 a specific user sets an Sock5 proxy to access the internet, then your Windows Service needs to know the Socks proxy information so that it could access internet, you must impersonate this user and read the settings.
Impersonation Definition
Definition copied from http://msdn.microsoft.com/en-us/library/aa376391(VS.85).aspx
Impersonation is the ability of a thread to execute using 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.
I read many articles and blogs and wrote an ImpersonateHelper
class to do impersonation work. During the investigation, I noticed that very few articles/blogs refer to a complete impersonation process, so I decided to write one that refers to as many details as possible, and actually my code was a code snippet combination that came from 10+ sources.
Functionality
I create a local user: TempUser
which belongs to “Administrators
” (make sure to log on TempUser
at least once). I logged on as my own account and I am going to 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 impersonation is successful.
Notes: After setting TempUser
as the only owner, my current account cannot access this folder except privilege promotion (I disabled UAC, if UAC is enabled, a prompt window will pop up and as for Admin confirm).
In addition, I tried to access this folder programmatically under my account, UnauthorizedAccessException will be thrown!
- I will access
TempUser
’s HKEY_CURRENT_USER
and do registry key creation, reading and deleting.
Code Snippet
We need to invoke three very famous 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 HKCU under TempUser, code below, as I highlighted line 45 and 49, after invoking LoadUserProfile
, hProfile
will be set 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 was created.
“TempKeyByWayne” was created under HKCU.
References
Filed under: Win32 API, Windows Development, Windows Service
Tagged: .NET, C#, CodeProject, DuplicateToken, Impersonate, LoadUserProfile, LogonUser, PInvoke, Win32 API