BlackArrow blog header

One shell to HANDLE them all


During a Red Team engagement, the exploitation of vulnerabilities in web apps usually offers a good chance of establishing a foothold into the target’s infrastructure or compromising an internal asset for lateral movement. From there on, a common approach is to upload a web shell and start looking for privilege escalation opportunities. When we talk about Windows environments, this privilege escalation from a web shell often relies in the abuse of the SeImpersonate and/or SeAssignPrimaryToken privileges using any of the various “Potato” exploits available in public repositories. 

However, since these are very well-known techniques, sometimes it may not be the best approach, especially when dealing with highly monitored environments. In these scenarios, as well as in general, it is important to have a wider range of alternatives at the time of modifying the initial security context to achieve privilege escalation or perform lateral movements. 

The main goal of this post is to propose an alternative approach to modify the initial security context from a web shell: the abuse of leaked user token handles. The reuse of open handles is not a new technique, and it has been explored and abused in diverse scenarios and for different purposes (e.g., dumping LSASS without opening a new process handle). Nevertheless, this technique applied to post exploitation activities after compromising a webapp has certain advantages:  

  • The user account used to run a web server on Windows almost always has impersonation privileges due to the software requirements. This allows to directly use token handles to spawn processes running on different security contexts. 
  • Web servers host a high variety of apps with different degrees of complexity and functionality which makes them more likely to end up leaking resources like handles. Sometimes this leakage will be provoked due to bad programming practices but in other cases it will appear simply because of the webapp design itself. 
  • IIS servers come with several built-in features that turn them into the perfect target for this technique: Windows authentication, user impersonation, delegation, remote virtual web directories and so on. Throughout this post we will see how these legitimate features can lead to the leakage of token handles, even when they are used properly. 

Next, we will explore several scenarios where user token handles can be leaked in an IIS server process due to different reasons, from software design requirements to programming errors. These are not exceptional or rare since they can be found in very well-known sites like StackOverflow (although nobody would copy source code from the Internet without investigating its security implication, right?) as well as in the Microsoft documentation itself. 


All the scenarios that we will comment on in this article come from an ASP.NET web app running on an IIS web server. But before that, we need a basic understanding of how these two technologies work and how it may lead to a leak of handles. 

Nowadays, a single IIS server can safely run several web apps using what is known as an Application Pool. An AP is a way to isolate different web apps that may be running on the same IIS server. Each AP has its own worker process (w3wp.exe) that is running in the security context of its own Application Pool Identity, which are special “virtual accounts”. Therefore, each worker process will be running in the security context of a different user account. 

This combination of separate processes with different security contexts provides a strong isolation between Application Pools. Before the implementation of this feature (i.e., before the release of IIS 7.5) all the worker processes run by default as the built-in identity Network Service, which allowed to jump between worker processes once one of them was compromised. 

This is relevant for the abuse of leaked token handles due to the isolation provided by the Application Pools. If we compromise a web app that is running in the Application Pool A, we will only be able to access the handles leaked by this app or by any other app running in the same AP A (a single AP can run any number of web apps). This means that, by default, if the same server is running a third web app that is running in the Application Pool B, we will not be able to reach any handle leaked by this app since it will be running in a separate process with a different security context. 

As for ASP.NET, the fact that it is a framework for web app development using .NET is probably the main reason that it generates so many situations where handles are leaked. Due to its managed nature, it is common for .NET developers to delegate in built-in features like the Garbage Collector the automatic management and release of the app resources. However, the problem arises when a .NET program is using unmanaged resources (like token handles) which cannot be released by the GB and require manual management, something that is not that common in these kinds of programming languages. 

To deal with this issue, Microsoft proposes the use of the interface IDisposable. Any class that implements this interface will be forced to have a method Dispose() in charge of releasing any unmanaged resource created by the class’s objects. According to the official documentation, the method Dispose should always be called before the object goes out of scope either by using an explicit try/finally block or through the using statement. 


Next, we will explore different scenarios where an ASP.NET app running on an IIS server could leak token handles. For that, we will use a simple classification of the scenarios depending on the characteristics of the leaked token and the level of access that their use will provide 

  • Security context switch: A token that allows to escape the IIS worker process security context is leaked but it is tied to a user session without cached credentials. This means that it will not allow us to access network resources. Based on our observations, this token will be elevated if the user belongs to the Administrators group.  
  • Access to network resources: The leaked token points to a user session with cached credentials, and therefore, it can be used to perform lateral movements and access network resources. In this case, we can find as well both medium and high integrity tokens depending on the user’s membership. 

There are many scenarios where we can find this behavior that we will not be exploring in this post. For example, the usage of HTTP Basic Authentication on an IIS server will leak by default token handles for each user that logs in to the app. Moreover, these tokens will provide access to network resources as well. 

At the end, the main goal of this article is to prove both theoretically and practically the different aspects and use cases of this technique and how we can abuse it during an engagement. 

Security context switch


When Windows authentication is enabled to access a web app, usually it is necessary to obtain the identity of the user who has logged in. The more common way of doing so from ASP.NET is using the class HttpContext. 

With this class it is possible to access the WindowsIdentity linked to an HTTP request. In this case, the WindowsIdentity represents the system user that has logged in to the web app, and according to the official documentation, this class implements the previously commented interface IDisposable. This means that we must call the method Dispose to release the unmanaged resources used by the object before it goes out of scope. 

WindowsIdentity Implementation

Despite an explicit warning in the docs about the need of calling the method Dispose, the code shown in the same page is not using it either explicitly or through the using statement, which can lead to confusion. 

WindowsIdentity remarks

In any case, let’s say that for any reason our web app requires access to the group membership of the logged in user. A quick search on Google returns several results from Microsoft’s forum, codeproject or csharpcorner among many others showing us how we can accomplish this task from ASP.NET. In general, the code proposed in all cases is as follows:  

ArrayList groups = new ArrayList(); 
foreach (System.Security.Principal.IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)

By calling System.Web.HttpContext.Current.Request.LogonUserIdentity we are creating a new instance of the class WindowsIdentity with information about the logged in user, generating under the hood a new handle to the user’s token. However, since we are not assigning the new WindowsIdentity instance to an object that we can manipulate at the end of the loop, it is impossible for us to call the method Dispose, preventing us from closing the new handle. This code effectively retrieves the user’s membership, but it also leaks the token handle which will remain open indefinitely in the context of the worker process and will allow an attacker to impersonate the user that has logged in previously: 

Handle properties

This Google search threw other alternatives to accomplish the same result, but all of them make the same mistake: they instantiate the class WindowsIdentity but they do not release the unmanaged resources by calling the method Dispose. Therefore, it doesn’t matter the alternative selected by the developer because unless they dive deep in the Microsoft’s documentation (you have a long way to reach WindowsIdentity from HttpConxtext and you will require a strong idea of what you are looking for) the web code will end up leaking token handles from the users that log in to the app. 

Impersonation with unhandled exception

Impersonation is a built-in feature of IIS servers that allow a webapp to run in the security context of a user other than the Application Pool Identity. This feature is mostly combined with Windows authentication, allowing the server to run the web code in the security context of the logged-on user. This is very handy at the time of implementing access control to the filesystem or other local services based on the logged-on user’s membership. 

To achieve this impersonation, the IIS itself manipulates the token of the user that has logged in and assigns it to a new thread, properly closing the handle once the execution has finished. Nonetheless, if for any reason an unhandled exception is thrown during this impersonation, the execution will end abruptly and the IIS will not be able to close the handle, which will remain open until the worker process ends: 

Unhandled exception

The combination of Windows authentication, user impersonation and unhandled exceptions could lead to a massive leak of token handles. 

Reflectively enabling user privileges 

Although the next technique is undocumented by Microsoft, it is included as an example of the many situations where unmanaged resources leakages can occur in .NET web applications. 

A very unconventional way to enable a privilege in a user token without requiring the use of P/Invoke is to use reflection over the internal class System.Security.AccessControl.Privilege 

Type privilegeType = Type.GetType("System.Security.AccessControl.Privilege");
object privilege = Activator.CreateInstance(privilegeType, "SeDebugPrivilege");
privilegeType.GetMethod("Enable").Invoke(privilege, null);

This code is quite simple and very effective from the point of view of the developer but, as in the previous examples, the token handle is leaked and once again there is nothing we can do to prevent it. Here we can see why this leak is happening (the thread’s token is manipulated but the handle is not closed at the end of the function), although this cannot be considered a programming error since the method Enable is probably not meant to be called directly using reflection.  

If this piece of code is used in an IIS server that is impersonation the users that log in, the leaked tokens will point to those users’ sessions, allowing a malicious actor to switch the initial security context from a web shell. 

Access to network resources

Interaction with Windows API 

Despite the enormous abstraction that .NET provides to interact with the operating system, there are many situations where it is necessary to call Windows API functions directly to execute a specific action. In most cases, this interaction is performed through P/Invoke, which allows to call unmanaged code from .NET. 

In addition to increasing the code complexity, one of the main disadvantages (if we can call it like that) of using P/Invoke is that it generates similar situations to the ones we have described before when we talked about IDisposable objects: it creates new references to unmanaged resources that the Garbage Collector cannot release by itself. 

It is not surprising that the manual release of resources could be an extra issue to deal with for .NET developers, who are used to rely on the managed nature of the framework that allows them to ignore this kind of task. 

Bringing this to the topic at hand, there are countless cases where using P/Invoke results in the creation of new token handles, which means that it is required a call to CloseHandle for each one of them to prevent the leakage.   

One of the most straightforward examples is the function LogonUser. A successful call to this function will create a new session and it will return to the caller a new token handle that represents the logged-on user. This token can be used then to impersonate the specified user to perform any task on their behalf: 

public string LeakToken()
    IntPtr hToken = IntPtr.Zero;
    string username = "mdiaz.adm";
    string password = "SuperStrongPassw0rd!";
    string domain = "BLACKARROW";
    var result = LogonUser(username, domain, password, 8, 0, ref hToken);

    WindowsIdentity id = new WindowsIdentity(hToken);
    WindowsImpersonationContext impersonatedUser = id.Impersonate();
    var directoryPath = @"\\\C$\RT";
    var localPatt = @"C:\RT\file.txt";
    directoryPath = directoryPath + @"\file.txt";
    File.Copy(directoryPath, localPatt);

    return "Leaked!";

In general, this token will be valid to access network resources on behalf of the logged-on user (unless the logon type has been set to LOGON32_LOGON_NETWORK) and will be elevated if the user belongs to the Administrators group. 

Once the requested ASP.NET code has finished its execution and considering that the call to CloseHandle has not been made, the result will be the leakage of a handle of a potentially elevated token that provides access to network resources: 

Leakage of a handle

In our experience, it is uncommon to find C or C++ pieces of code where the corresponding calls to CloseHandle to release each handle are not made. However, this happens quite a lot when we examine .NET code available in the wild, perhaps due to the way that the use of .NET is taught or because the direct interaction with Windows API is less common in managed programming languages. 

Anyway, we think that the interaction with Windows API from ASP.NET may result in a valuable source of leaked token handles. 

Web directories 

When a new website is deployed on an IIS server, one of the settings that must be configured is the physical path where the root web directory is located. This setting allows the use of UNC paths, which means that it is possible to set a remote path as the root web directory: 

Use of UNC paths

The website deployment wizard also allows you to insert an alternative identity that the IIS should use to access the root web directory. By default, the IIS will use its Application Pool Identity to access this directory and if the physical path is a remote directory the computer’s user account will be used instead.   

If an alternative set of credentials are needed to access the root web directory, the deployment wizard comes with the option “Connect as…” that will open a new dialog asking for username and password: 

Using alternative set of credentials

This alternative set of credentials will not change the security context of the worker process, instead, they will be used to access the UNC path set as physical path for that specific website each time that a new file is required to properly answer an HTTP request. 

What is interesting about this setting is the way that the IIS handles the access to the web directory. Since we have indicated that the web directory must be accessed using an alternative user account (in the example below, we use the identity of the domain user blackarrow\mdiaz.adm) the IIS will perform under the hood a call to LogonUser to create a new user session. Then, this new session can be used through its token to access the web directory under the identity of the supplied user. 

This is exactly what the advance settings UI shows, also allowing us to set the logon type that we want to use:  

Setting logon type

Depending on the specified logon type, the tokens generated will vary slightly (it is considered that the user belongs to the local Administrators group for the classification below): 

  • Interactive: It will be created both an unelevated and an elevated token. In both cases, these tokens will grant network access. 
  • Network: Elevated token without network access. We will not see this kind of logon type when the specified root web directory is in a remote host 
  • Batch and ClearText (default): It will generate a token with full administrator privileges and its use will grant network access as well. 

Once the worker process is started, a new session for the user mdiaz.adm is created and several handles for the user token are generated, that will remain open and available from the process’s context indefinitely 

Handles remain open indefinitely

The image above shows that since mdiaz.adm belongs to the local Administrators group and the default logon type is ClearText, the resulting tokens have full administrator privileges. Also, these tokens will be tied to a session with cached credentials, meaning that their use will grant access to network resources 

We are calling this a handle leak because, although this is by design and it seems the desired behavior, the compromise of a website with this setting will automatically allow us to escape the Application Pool Identity security context and perform lateral movements (unless, as we have said before, the logon type has been set to Network, in which case we will only be able to switch the security context).  

Moreover, a single website can have multiple virtual web directories, each one of them with its own alternative access credentials, meaning that this setting may offer an attacker several choices to modify the initial security context.  

Proof of concept

After all this theory it is time to put all the pieces together to build up a web shell that allows us to list all the token handles available for the current worker process and use them to modify our security context at will. To do so, our first approach was the following: 

  1. Retrieve all the handles available in the current process by calling NtQueryInformationProcess. Once obtained a handles list, we iterate over each one of them applying the steps 2 and 3. 
  2. Call NtQueryObject to find out if it is a token handle. 
  3. Retrieve information about the owner of the token by calling GetTokenInformation and LookupAccountSid. 
  4. Once the operator decides which one of the available token handles wants to use for the impersonation, a call to CreateProcessWithTokenW is performed to spawn a new process running in the new security context. 

Although this approach worked fairly well, we soon realized that the call to NtQueryInformationProcess was pretty unreliable, and in a lot of cases it was not returning all the handles available in the current worker process. This forced the operator to refresh the web shell several times before the desired handle appeared, what we thought was not good enough. 

For that reason, we decided to slightly modify the web shell’s behavior. But before going into that, it is important to understand that a handle in user space is just an index that references an entry in the process’s handles table stored in the kernel, and therefore, it is an integer. These indexes are assigned consecutively, and the kernel tends to fill the gaps between indexes instead of assigning a higher number for each new handle.  

What we can do then is replace the call to NtQueryInformationProcess with a loop that iterates over all the numbers in the range [0,1000000], creating a new IntPtr variable (this is one of the ways that we can encapsulate a handle in C#) for each one of those indexes. This range has been selected based on our tests, since we consider that a process with more than 1 million open handles is rare and, even though the range may seem huge, the web shell keeps responding fast enough. 

At this point, it is obvious that not all the indexes in the range will correspond to valid handles (actually, most of them will not), a problem that is solved by calling NtQueryObject. If an index does not correspond to a valid handle, this call will return with a STATUS_INVALID_HANDLE (C0000008) NTSTATUS value, in which case the execution will continue with the next index. 

This way, we obtain a web shell that allows us to abuse the leakage of token handles to escape the restrictions of the Application Pool Identity security context. 

The last thing before considering this a functional PoC is to retrieve the token’s integrity level and figure out whether it grants access to network resources. This is simply accomplished with two consecutives calls to GetTokenInformation. 

In particular, to figure out if the token’s session has cached credentials that we can use to perform lateral movements, we call GetTokenInformation asking for the TokenOrigin value. According to the documentation, if a token has been generated using explicit credentials the returned TokenOrigin will contain the session id, and otherwise it will be zero: 

TokenOrigin description

With few exceptions, we have checked that in this scenario if the TokenOrigin is different from zero the session pointed by the token is storing cached credentials. Therefore, even though it is not bulletproof, it is a decent method to know if we can use the token to access network resources without requiring administrator privileges. 

Webshell to abuse handles

This proof of concept is already available in our repository. As a suggestion to make this PoC fully functional, we propose at least the following improvements: 

  • Implement the abuse of the privilege SeAssignPrimaryToken to be able to call CreateProcessAsUser as well. 
  • Reflective loading of .NET assemblies to take advantage of the impersonation without the need of spawning a new process each time. 


Throughout this post we have explored several scenarios where token handles are leaked in the context of an IIS worker process running an ASP.NET app. However, we think that this is just the beginning and if we go deeper, we may find many apps and web services where this situation is currently happening.  

As an example of scenarios that we have not detailed in this post, we can comment once again about the usage of HTTP Basic Authentication on an IIS server or the AD CS web services, which keep an open token handle for all users that log in to them. 

As our final thought, considering the huge amount and diversity of web apps run by any company, this technique may be a valuable approach at the time of performing privileges escalation both locally and in an Active Directory environment after compromising a web app during an engagement.