Suspected Security Breach?

Lateral movement: A deep look into PsExec

PsExec was originally conceived as a sysadmin tool. It quickly turned into the de facto standard for network pivoting. This article will explain how it works and give you the background to understand under which conditions it can be used.

By Daniel Muñoz

Moving laterally during an engagement is a common practice which consists in accessing or controlling other systems on a network after compromising a machine. For this purpose, the ability to execute code on a remote host is really useful. There are many techniques available and the landscape keeps evolving as monitoring and defence mechanisms improve. Although you may be familiar with them or even used them quite often, there is a chance you are not entirely aware of the underlying mechanisms supporting it.

This is an in-depth analysis that will hopefully help having a clear picture of one of the best-known techniques, which has been around for long time now: PsExec.

What is PsExec?

As you probably know, PsExec is a tool included in the Sysinternals Suite created by Mark Russinovich. Originally, it was intended as a convenience tool for system administrators so they could perform maintenance tasks by running commands on remote hosts.

By providing the address of a target host, a valid user and a password, you can get control of a machine remotely.

What is happening under the hood? In a nutshell:

Uploads PSEXESVC.exe to the $ADMIN shared folder.

Remotely creates a service that will run PSEXESVC.exe.

Remotely starts the service.

The PSEXECSVC service acts as a wrapper. It runs the specified executable (cmd.exe in our example) on the remote system (note that whatever you want to run must exist already on the remote system) while it redirects the input/output of the process back and forth between the hosts via named pipes. In other words, the PsExec tool running on the sysadmin´s computer writes to \\client\pipe\PSEXESVC-stdin and reads from \\client\pipe\PSEXECSVC-stdout and \\client\pipe\PSEXECSVC-stderr, while the process running on the remote machine has its output redirected to \\client\pipe\PSEXECSVC-stdout / \\client\pipe\PSEXECSVC-stderr and gets its input from \\client\pipe\PSEXESVC-stdin.
Let us see the process in detail:

1. Open an SMB session using the supplied credentials to authenticate. It doesn´t matter here if it uses NTLM or Kerberos.

3. Open handle to \\client\pipe\svcctl to talk to the Service Control Manager (SCM), which gives us the ability to create and start/stop services remotely, among other things. It uses the SVCCTL protocol which goes on top of DCE/RPC calls sent to the svcctl pipe, i.e., DCE/RPC goes on top of SMB.

4. Call the CreateService function, using the recently uploaded PSEXESVC.exe as service binary.

5. Call the StartService function.

As you can see in the following Wireshark capture, it creates the named pipes to redirect stdin, stdout, stderr.

In total, there are 4 named pipes created, one for the service itself, and the pipes to redirect the process´ stdin/stdout/stderr.

What is pass-the-hash?

Pass-the-hash is a technique which takes advantage of the constant fight between security and usability. Windows implements single-sign-on for users’ convenience, without it, every time a user accessed, e.g., a network share, it would have to prompt him for the password. To be able to do this, the “key” must be stored in memory. At some point the designers must have thought “well, we don´t want to have cleartext passwords laying around in memory. Let´s just keep its hash”. The problem then is that applications only rely on the password´s hash to authenticate against remote services. As a result, many authentication protocols in the Windows ecosystem end up using the NTLM hash of the password as the “key”, instead of the password itself. The consequence is that the hash is often enough to authenticate.

In the first step described before, PsExec authenticates to SMB. Oddly enough, the authentication process itself only requires the user´s NTLM hash. If we are using NTLM authentication the hash will be used to encrypt the challenge or nonce. If it is Kerberos, we will be able to get a Service Ticket from the KDC only using the hash (pass-the-ticket). This means that remote code execution can be achieved without knowing the password itself.

Does PsExec pass the hash?

Sysinternal´s PsExec does NOT pass the hash. There are other tools, such as Metasploit´s PsExec module, which use the same technique and do pass the hash. Unfortunately, this has led to confusion and you´ll see misleading articles about this on the web.

If you do want to pass the hash using Sysinternal´s PsExec, from a windows box, you can do so with the aid of Mimikatz.

First, let´s lay out some facts:

To be able to access resources locally, the Windows kernel checks if the user SID contained in the Process´ AccessToken has permission to access such resource.

To access resources remotely, the credentials from the logonSession are used to authenticate with the remote system.

AccessTokens are linked to a logonSession, specified in the AccessToken´s logon SID field.

Mimikatz can create a forged logonSession using any user, domain and NTLM hash you provide it with.

Mimikatz can make a process´ AccessToken point to a forged logonSession.

Sysinternal´s PsExec will authenticate to a remote system using the credentials of the current process if no credentials are given as an argument.

Hence you can execute Sysinternal´s PsExec in such conditions that when it tries to authenticate, it will be using the stolen NTLM hash. In the picture below, you can see the steps taken:

Spawn cmd.exe with cooked logonSession using Mimikatz

Run PsExec within that cmd.exe to connect to the remote computer with the stolen hash

This is only one of many ways you could do this. If, instead of having GUI access to this pivoting machine, you only had a Meterpreter session you could use the Mimikatz module to spawn a hidden bogus process instead of cmd.exe and make your session impersonate its token using the Meterpreter´s steal_token command.

It’s worth noting the following:

In these examples it’s being assumed that you already have a valid NTLM hash. Typically, to obtain them, you would need to have compromised a host and have a privileged account to extract the hashes. This would not be possible at all in newer versions of Windows 10 and Windows Server 2016 with isolated lsass (see references for more information).

Again, even if you already had the NTLM hash, if you want to use Mimikatz to forge a logonSession to pass-the-hash, you’ll need admin privileges on the attacker machine.

Minimum requirements to use the PsExec method

These are the bare minimum requirements:

Port 139 or 445 open on the remote machine, i.e., SMB.

Password or NTLM hash of the password (*)

Write permissions to a network shared folder (**). It doesn´t matter which one (***).

*: Remember that NTLM ≠ NTLMv1/v2, so captured hashes over the network are not valid, unless you crack them or use an NTLM relay technique, but that´s another story.

**: Bear in mind that NTFS permissions ≠ Share permissions. It´s not enough to have permissions to write locally.

***: Note that if you are using Sysinternal´s PsExec, it will try to copy PSEXESVC.exe to ADMIN$ (alias for C:\Windows), so you need access to it, not to just any folder.

In most scenarios, your stolen account won´t be able to comply with requirements 4 & 5 unless it is a privileged account (read the FAQ for more information on what type of privileged accounts would work). Mainly because, even if you had the ability to create services (SC_MANAGER_CREATE_SERVICE), the default DACL template (Discretionary Access Control List) will be applied to the service you just created. As you can imagine, this template won´t allow your user SERVICE_QUERY_STATUS + SERVICE_START access unless it belongs to an administrators group. In short: with a normal account you won´t have the permissions to start the service you just created, provided you could create one in the first place.

As a proof of concept, we are going to follow the steps manually. We are going to use Alice´s hash again. For the sake of simplicity in this scenario she belongs to the local Administrators group.

Step 1: Impersonate user

Again, we impersonate the user.

Step 2: Upload the executable

First, create a simple Meterpreter executable. Don´t forget to build it with the exe-service format.

Within the cmd.exe running with the forged token, copy the executable to the remote share.

Step 3: Start the service

Start the service remotely

When the service starts, it launches a reverse shell back to our Kali box.

Differences with this approach:

Note that you need to either know the local path of the network share or use the UNC path (as seen in the screenshot).

The session is running as NTAUTHORITY\System. That is because, by default, all services run as SYSTEM, not as the user who created the service. Despite that, Sysinternals´PsExec runs as the user whose credentials you are using.

In this case, the executable is a Meterpreter payload, not PSEXECSVC.exe. Thus, communication happens over TCP session, instead of over named pipes.

FAQ about token filtering and local users

Q: Can I use this method against non-domain-joined hosts?

A: Yes, but be aware that local users will fail trying to do so, even if they belong to the local “Administrators” group. The reason for this is that their AccessToken on the victim´s system will be filtered to a medium-integrity level by UAC and, as such it won´t be able to write to disk or to control the SC Manager, as those require a high-integrity token. This is an intentional security measure. There is an unadvisable workaround to prevent this which consists in editing/creating the registry key HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy and set it to ‘1’.

On the other hand, this policy does not apply to the built-in local Administrator account (read, the one with RID 500), who is not enrolled in UAC by default. This account is disabled by default, but if you enable it you can test it yourself.

Q: What about domain-joined hosts?

A: Local users belonging to the Administrators group will only get a medium-integrity token as well. Nonetheless, domain users belonging to the local Administrators group will receive a full-fledged high-integrity token and will be able to perform this technique.

To wrap things up:

Remember that you need to write to network share and control the SC Manager and a service. You could write to the network share with a medium-integrity token, but to create and start a service, you do need to be running with a high-integrity level. Regardless of whether the victim is part of a domain or not, local users will be constrained to a medium-integrity level, domain users (if that applies) and the built-in Administrator account will not.