If you’re ever running any programs on an account with administrative privileges, it might be tempting to think “It’s alright; at least if this account is compromised, the attacker won’t be able to obtain NT AUTHORITY\SYSTEM (“SYSTEM” for simplicity) level permissions.”
Unfortunately, this doesn’t bear out in reality.
And just to be clear, I’m not talking about right-click, “Run as administrator” to escalate privileges, I’m talking about a user running my Word macro or malicious MSI application and giving me a SYSTEM shell.
Now you might be wondering, how is this true?
Well, there are essentially two boundaries that must be crossed for a process that was created by an administrator account to achieve SYSTEM privileges. To understand these boundaries, we first need to understand three basic concepts of Windows security.
An access token is an object that describes the security context of a process or thread. The information in a token includes the identity and privileges of the user account associated with the process or thread.
A privilege is the right of an account, such as a user or group account, to perform various system-related operations on the local computer, such as shutting down the system, loading device drivers, or changing the system time.
The integrity level is a representation of the trustworthiness of running application processes. When a process runs under User Account Control (UAC), some privileges are completely stripped out of the process token. This prevents the process from adjusting its own privileges to elevate itself. It also helps enforce the principle of least privilege.
The first boundary to bypass is the User Account Control (UAC). When UAC is enabled in the default configuration, all applications executed by an Authenticated User are placed into the medium integrity category. This integrity level removes the Builtin\Administrators (“Administrator” for simplicity) group privileges from the access token. To run with these privileges requires prompting for consent. We want to bypass the prompt while still obtaining the Administrator privileges.
Microsoft does not consider UAC to be a security boundary, and it can very easily by bypassed by any one of the 16 (at the time of writing) techniques available here https://github.com/hfiref0x/UACME. These UAC bypass methods allow a process to obtain the Administrator access token without prompting for consent.
You might be wondering how these attacks bypass the UAC prompt. Windows contains certain programs that are configured to automatically elevate privileges, to prevent the user from getting spammed with UAC prompts when, for example, an update is installing. These auto-elevate programs are digitally signed by Microsoft, and thus trusted by UAC to execute without prompting the user. If attackers can manipulate these trusted programs into executing their own code, they will have successfully bypassed the UAC prompt.
An example is the trusted fodhelper.exe, which looks for additional commands to execute from the registry. The registry locations are not protected from user modification. This allows users to modify the registry keys and gain code execution in a high integrity process.
Escalating an Administrator Token
Now that we have a high integrity process with Administrator privileges, there are various methods to elevate an Administrator token to the next privilege level and gain a SYSTEM token. The boundary between an Administrator token and SYSTEM token is again not considered to be a security boundary. Administrators often need to be able to perform actions that require SYSTEM permissions, including installing services, drivers or software, debugging processes, scheduling tasks, etc.
So, there is no reason to prevent administrators obtaining a SYSTEM token, however, there is also no explicit Windows API to do so. This becomes a two-part process. First we must create a process running under the SYSTEM context, then steal of token of this process.
The common methods of creating a SYSTEM process include:
Each service executes in the security context of a account. A process holding the “Create Service” access right can create a service to execute under the SYSTEM account. This allows the execution of an arbitrary binary as a service with SYSTEM permissions.
When creating a scheduled task, the task can be configured to run under the SYSTEM account. This allows the creation of a task that will execute arbitrary code under the SYSTEM account.
If debug privileges are available, a DLL can be injected into a SYSTEM process to steal the security token. The methods for stealing the token are described below.
An MSI installer can contain custom actions to be executed during the installation process. These actions would generally be executed under the context of the installing user. However, by configuring an impersonation flag, the custom actions get executed by the SYSTEM account. The end result is an MSI installer that will execute arbitrary commands under the SYSTEM account.
This technique relies on the fact that a process inherits attributes from its parent. Using debug privileges, an attacker can create a process and manually configure the parent to be a SYSTEM process. This causes the newly spawned process to inherent a SYSTEM token.
Any of these techniques could be used to create a process under the SYSTEM context. The low level details are outside the scope of this blog but can be found by inspecting the Meterpreter source code, or reading this blog by Adam.
In many cases this is enough to gain code execution under the SYSTEM context. However, from the perspective of an attacker, I don’t always want to create a new process running as SYSTEM, I want my process to have SYSTEM privileges. It should be noted that once a process has started running, the token is locked and can no longer be modified. We can change the privileges of the token but cannot assign a new token.
The solution is for the process to obtain the handle to a SYSTEM token and use that token for privileged actions. Two methods are commonly used to pass the token from a SYSTEM process to a lower integrity process. These are:
Named Pipe Impersonation
A named pipe is a communication channel between a pipe server and one or more pipe clients. The method works by having the lower-privileged process create a pipe server, and the high privileged process connected to the pipe server.
A pipe server can use the ImpersonateNamedPipeClient API to change the thread to start impersonating the security context of the last message read from the pipe. Once impersonated, the thread can use OpenThreadToken and DuplicateTokenEx to create itself a primary token to use for privileged actions.
The SetThreadToken function can be used to assign an impersonation token to a thread. The high-privileged process can create an impersonation token using DuplicateTokenEx, and then assign this token to the lower-privileged thread. Once the token has been assigned, the low-privileged thread can use the OpenThreadToken and DuplicateTokenEx APIs as described above to create itself a primary token.
Proof of Concept Video
This covers how an application can gain SYSTEM permissions without ever prompting the user. The issue can be remediated by operating Windows using a standard user account. Without administrator privileges, an attacker will have to find a method to escalate their privileges, or steal credentials, before they can even begin the process of bypassing UAC. The boundary between Administrator privileges and SYSTEM could be hardened through group policy, but this would only delay an attacker. All Administrator accounts should be considered as having SYSTEM permissions.