Tokenvator Release 3 is a long overdue update that includes a major overhaul to the tool. From the user interface, it will be mostly familiar with some command line tweaks. Under the surface, large portions of the code base have been reworked, and parts of the base have had some updates. In this series, we will go over some of the changes and new features added. Teaser Alert: Adding Privileges & Creating Tokens
First and foremost, the user interface. Historically, every action had a series of positional arguments that were clunky and generally difficult to remember. They were also not very flexible, and as the commands started to have more, and additional optional arguments, they became completely unwieldy. These have been replaced with flags that will auto complete.
For instance, to list and enable privileges:
This also works in the non-interactive mode (though it won’t tab complete – sorry, it’s Windows):
Additionally, the scroll back function was improved and numerous bugs were resolved. For instance, now when you press up you will always go to the last command issued. A printable command history has also been added if you want to copy and paste instead or keep a log of your actions.
The info functionality was improved again, removing many bugs and adding additional information, such as impersonation contexts:
(Tokens) > whoami [*] Operating as NT AUTHORITYSYSTEM (Tokens) > info [*] Primary Token [+] User: S-1-5-21-258464558-1780981397-2849438727-1001 DESKTOP-J5KC1AR xbadjuju [*] Impersonation Tokens [*] Primary Token Groups [+] Enumerated 15 Groups: S-1-5-21-258464558-1780981397-2849438727-513 DESKTOP-J5KC1ARNone S-1-1-0 Everyone S-1-5-114 NT AUTHORITYLocal account and member of Administrators group S-1-5-32-544 BUILTINAdministrators S-1-5-32-559 BUILTINPerformance Log Users S-1-5-32-545 BUILTINUsers S-1-5-4 NT AUTHORITYINTERACTIVE S-1-2-1 CONSOLE LOGON S-1-5-11 NT AUTHORITYAuthenticated Users S-1-5-15 NT AUTHORITYThis Organization S-1-5-113 NT AUTHORITYLocal account S-1-5-5-0-870189 Some or all identity references could not be translated. S-1-2-0 LOCAL S-1-5-64-10 NT AUTHORITYNTLM Authentication S-1-16-12288 Some or all identity references could not be translated.
Now, you have the option to get additional information by using the /all flag.
(Tokens) > info /all Option Value ------ ----- all [*] Primary Token [+] User: S-1-5-21-258464558-1780981397-2849438727-1001 DESKTOP-J5KC1AR xbadjuju [*] Impersonation Tokens [*] Thread ID: 5820 [+] User: S-1-5-18 NT AUTHORITYSYSTEM [*] Thread ID: 1120 [*] Thread ID: 7108 [*] Thread ID: 9180 [*] Thread ID: 1152 [*] Thread ID: 8592 [*] Thread ID: 8076 [*] Primary Token Groups [+] Enumerated 15 Groups: S-1-5-21-258464558-1780981397-2849438727-513 DESKTOP-J5KC1ARNone S-1-1-0 Everyone S-1-5-114 NT AUTHORITYLocal account and member of Administrators group S-1-5-32-544 BUILTINAdministrators S-1-5-32-559 BUILTINPerformance Log Users S-1-5-32-545 BUILTINUsers S-1-5-4 NT AUTHORITYINTERACTIVE S-1-2-1 CONSOLE LOGON S-1-5-11 NT AUTHORITYAuthenticated Users S-1-5-15 NT AUTHORITYThis Organization S-1-5-113 NT AUTHORITYLocal account S-1-5-5-0-870189 Some or all identity references could not be translated. S-1-2-0 LOCAL S-1-5-64-10 NT AUTHORITYNTLM Authentication S-1-16-12288 Some or all identity references could not be translated. [+] Source: User32 [*] Enumerating Token Privileges [*] GetTokenInformation - Pass 1 [*] GetTokenInformation - Pass 2 [+] Enumerated 24 Privileges Privilege Name Enabled -------------- ------- SeIncreaseQuotaPrivilege False SeSecurityPrivilege False SeTakeOwnershipPrivilege False SeLoadDriverPrivilege False SeSystemProfilePrivilege False SeSystemtimePrivilege False SeProfileSingleProcessPrivilege False SeIncreaseBasePriorityPrivilege False SeCreatePagefilePrivilege False SeBackupPrivilege False SeRestorePrivilege False SeShutdownPrivilege False SeDebugPrivilege True SeSystemEnvironmentPrivilege False SeChangeNotifyPrivilege True SeRemoteShutdownPrivilege False SeUndockPrivilege False SeManageVolumePrivilege False SeImpersonatePrivilege True SeCreateGlobalPrivilege True SeIncreaseWorkingSetPrivilege False SeTimeZonePrivilege False SeCreateSymbolicLinkPrivilege False SeDelegateSessionUserImpersonatePrivilege False [+] Owner: S-1-5-32-544 BUILTINAdministrators [+] Primary Group: S-1-5-21-258464558-1780981397-2849438727-513 DESKTOP-J5KC1ARNone [+] ACL Count: 572 [+] Primary Token [+] TokenElevationTypeFull [*] Token: Split [+] ProcessIntegrity: High
Previously, I had glossed over Impersonation (Thread) Tokens in the Tokenvator tool. When you impersonate a token, it doesn’t replace your primary token in your process. What it does is place the token in the calling thread. In this tool, this is typically the primary thread. Going forward, I will use Thread Token and Impersonation Token interchangeably.
In the following example, we show the privileges on our primary token (List_Privileges), impersonate the SYSTEM account (GetSystem), list the privileges on our primary token again to show it hasn’t been altered (List_Privileges), and finally list the privileges for the SYSTEM token we are impersonating (List_Privileges /Impersonation).
In this second, more complex example, we show:
- The privileges on our primary token (List_Privileges)
- Impersonate the SYSTEM account (GetSystem)
- List the privileges for the SYSTEM token we are impersonating (List_Privileges /Impersonation)
- Disable the SeAssignPrimaryTokenPrivilege on the Thread Token for SYSTEM (Disable_Privilege /Privilege:SeAssignPrimaryTokenPrivilege /Impersonation)
- List the thread token privileges again (List_Privileges /Impersonation)
- Re-enable the privilege on the token (Enable_Privilege /Privilege:SeAssignPrimaryTokenPrivilege /Impersonation)
- List the privileges one last time (List_Privileges /Impersonation) to show that it has been reenabled
This could all be done against a remote process as well by passing /ProcessID:<ID> flag.
Similarly, Thread Tokens can be impersonated with the Steal_Token command by specifying the /Thread Flag.
Now for the Cool Stuff
The number one request I’ve gotten has been, “Can I add a privilege with this tool?” Until now, that issue has been open on GitHub. I can happily say, I can finally close this issue.
Like Morpheus said, some of these rules can be bent others can be broken. To change the privileges on the token, I’ve historically used advapi32!AdjustTokenPrivileges. This allows for privileges to be enabled, disabled, or removed. As far as I’ve been able to tell, this does not allow for adding privileges onto a token. But because that doesn’t work, it doesn’t mean that there are no other options. It’s time to enter the world of the kernel.
Exploring the Kernel
To look at the Windows kernel, you will probably need WinDbg. It involves installing several development kits from Microsoft (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg–kernel-mode-). A local instance of the debugger can be started from an elevated command prompt:
- “C:Program Files (x86)Windows Kits10Debuggersx64kd.exe” -kl
From here we can start exploring.
As expansive as Microsoft Developer Network (MSDN) is, it is not all-encompassing. There are things that are intentionally not documented as they are not intended to be used. Among these is the EPROCESS structure.
Part of this is that this structure can change without notice from Microsoft. I can personally confirm that this happened. While it may be difficult to track down the layout of this structure online, we can easily view it with WinDbg. Using the dt command in WinDbg we can view each instance of it.
Exploring with windbg
Let’s look at the process structure of an elevated Tokenvator instance:
It’s running with process ID of 8140, converted to hex it is 1FCC.
Examining that process shows:
The line we are looking for is:
- PROCESS ffff9a890b963080
The EPROCESS structure can be found at is at address ffff9a890b963080. Querying for the EPROCESS structure:
It’s mostly truncated here, but it’s big. Really big. Excluding the KPROCESS structure which takes up the address 0x000 – 0x438 it has 238 entries.
dt nt!_EPROCESS ffff9a890b963080 +0x000 Pcb : _KPROCESS … +0x460 PrimaryTokenFrozen : Pos 15, 1 Bit … +0x4b8 Token : _EX_FAST_REF … +0xa10 DynamicEnforcedCetCompatibleRanges : _PS_DYNAMIC_ENFORCED_ADDRESS_RANGES
But in this structure, there is a field called Token which references the _EX_FAST_REF structure.
Querying the _EX_FAST_REF structure for the Token field shows the following.
If we were query that address for a _TOKEN structure it wouldn’t work but looking the verbose process information with the command.
I didn’t immediately realize why the addresses were different. Fortunately, the ired.team was able to provide a useful insight:. they realized a bitwise AND (&) would correct the address.
Evaluate expression: -65372684652448 = ffffc48b`3c5a7060
Querying that address for the token we see a structure where the privileges are stored.
Querying that field reveals a structure that contains the present field where a bitwise or for each privilege can detect if it is present.
AND’ing that field can allow us to put privileges back on the token.
Pulling it All Together
We’ve found what needs to be changed in the kernel, so how do we do that? Well, that involves creating a kernel mode driver that can interact with kernel memory. Introducing: the KernelTokens driver.
This introduces several additional commands:
First, we need to install the driver. The default name is TokenDriver.
Now, let’s look at the existing privileges on the Tokens:
In this instance we see at the start there are 24 privileges. Let’s add two privileges SeTcbPrivilege and SeCreateTokenPrivilege. First, we run the command Add_Privilege /Privilege:SeTcbPrivilege. This connects to the driver and updates the bitfield in memory. Running List_Privileges again we see SeTcbPrivilege is now on the token. Running the Add_Privilege /Privilege:SeCreateTokenPrivilege command again allows us to add this privilege as well. As can be seen during the final List_Privileges command, 26 privileges are now present on the token including SeTcbPrivilege and SeCreateTokenPrivilege.
Causing Some Shenanigans
Critical Processes are a fun flag that can be added to a process to indicate that it is critical to the system functionality. This can be used to force a system to blue screen, or in some cases prevented the process from being killed.
When it does, well…
Becoming Someone Else:
One of the interesting things that I always wanted to add was the ability to become another user on the system without having to steal their token from a running process. There are several ways to accomplish this with increasing levels of difficulty:
Each one of these methods calls a different API.
The RunAs is almost identical to the RunAs /netonly command. Under the surface this is just calling CreateProcessWithLogonW.
Logon_User is a little more complex, depending on the options provided it is either calling LogonUser or LogonUserExExW (no, not a typo) and then uses the newly created token to call CreateProcessWithTokenW.
Using this we can become any user we have credentials for as well as local service accounts such as Network Service or Local Service.
Lastly, the final technique for this part of the post is create_token. Under the surface this calls ntdll!CreateToken – this is a bit of a bear. This manually crafts the token from scratch and then calls CreateProcessWithTokenW.
As can be seen above, with this we can become disabled users and ephemerally add them to groups by adding the group onto the token at creation time.
This release can now be accessed on GitHub. To download and learn more about Tokenvator visit: https://github.com/0xbadjuju/Tokenvator.