Windows has a rich security model that is worth understanding to operate effectively on a red team or pentest.
There are many great resources that discuss this topic. We will reference them and attempt to distill the foundational concepts and the operational points you should know. Specifically, we will focus on the underlying ntdll and kernel32 user space API calls to manipulate tokens, and how tools like CobaltStrike use these for offensive functionality. We will also be exploring things like the Mimikatz source code to shine light on Windows internals.
For a full deep dive on access tokens, see: https://docs.microsoft.com/en-us/windows/desktop/secauthz/access-tokens. Otherwise, here are the cliff notes:
Access tokens describe the access rights of the running code. Tokens are used by the kernel to perform access checks when that process interacts with securable objects https://docs.microsoft.com/en-us/windows/desktop/secauthz/securable-objects. Also, like most things in Windows, tokens are themselves a securable object and have their own DACLs.
Tokens come in two main forms: they can be a Primary Token associated with a process, or an impersonation token associated with a thread. Why stop at just two token types? -Someone at Microsoft. Impersonation tokens can have three sub-types as well.
Impersonation tokens are designed to support a client server model where the server can impersonate the client's access rights and perform its activity as that client. This eases the burden of the server side code from doing its own access checks, and instead just making a thread impersonate the client and letting Windows handle access control.
While subprocesses and threads inherit the process token, thread/impersonation tokens do not get inherited. That means if you have a thread running under an impersonation token, any sub processes or threads spawned by that thread will inherit the process token. This was apparent for example in versions of Cobalt Strike prior to 3.13, where any tasks spawned in a sub-process would not run with the impersonated thread token.
Creating Impersonation Tokens
Windows provides several APIs to manage tokens, including spawning new processes or threads with tokens, or applying a token to an existing process or thread.
The ImpersonateLoggedOnUser API call is designed to apply an existing token to your thread. https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-impersonateloggedonuser.
ImpersonateLoggedOnUser takes a single argument, a token handle, and applies that token to your current thread. This token handle can be acquired from an other process, allowing your thread to impersonate a process running as another user. This existing token can be be acquired via OpenThreadToken or OpenProcessToken, and duplicated if necessary with DuplicateTokenEx to set the proper access permissions.
These impersonation APIs can be great tools for malware writers, and are largely integrated into attack frameworks such as MetaSploit and Cobalt Strike.
This is how Cobalt Strike, at least prior to 3.13, runs a pass the hash attack. It uses Mimikatz to spawn a process run with the passed hash, and then acquires the token handle for that spawned process and applies it to the Cobalt Strike process with ImpersonateLoggedOnUser. See this Cobalt Strike Blog Post and attached videos for more details: https://blog.cobaltstrike.com/2015/12/16/windows-access-tokens-and-alternate-credentials/
Cobalt Strike PTH Sample
run mimikatz's sekurlsa::pth /user:Administrator /domain:BAD_SERVER3 /ntlm:8118cb8789b3a147c790db402b016a08 /run:"%COMSPEC% /c echo 617a91287 > \\.\pipe\123456"
Access tokens reference a LogonSession which is handled by LSA and contains a wealth of authentication information. This LogonSession is used by Windows for Single Sign On functionality when accessing network services. If you ever have used Mimikatz, you've likely already been interacting with LogonSessions to dump their credential content or inject a hash into them for a pass the hash attack.
These are referenced by a LUID, a locally unique identifier. For more details on LogonSessions refer to: https://www.microsoftpressstore.com/articles/article.aspx?p=2224373&seqNum=7
Mimikatz pass the hash works by manipulating the LogonSession to inject the passed hash. Lets take a look what happens when you use mimikatz to pth and open a cmd.exe as that passed hash.
run mimikatz's sekurlsa::pth /user:Administrator /domain:BAD_SERVER3 /ntlm:8118cb8789b3a147c790db402b016a08 /run:"%COMSPEC%
Mimikatz will first run your program and suspend it, using the CreateProcessWithLogonW API call: https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithlogonw. From the linked documentation: "Creates a new process and its primary thread. Then the new process runs the specified executable file in the security context of the specified credentials (user, domain, and password). It can optionally load the user profile for a specified user.
This function is similar to the CreateProcessAsUser and CreateProcessWithTokenW functions, except that the caller does not need to call the LogonUser function to authenticate the user and get a token."
Mimikatz uses the username and domain provided with a dummy (empty) password.
Once this subprocess has been opened, Mimikatz then aqcuires a handle to that process's token with OpenProcessToken and gets that process's token information with GetTokenInformation. With the token information, and thus the LogonSession LUID for that target acquired, Mimikatz begins the pass the hash step.
At this point, Mimikatz opens a handle to the LSA process and injects the NTLM hash credential provided into the LogonSession referenced by the target process's (in this case cmd.exe) token's LogonSession LUID. This is the least OpSec safe step, as handles to LSA are highly suspect. This activity will be flagged as suspicious or malicious by most modern endpoint protection software.
Mimikatz then resumes the process, leaving you running your chosen application with a passed NTLM hash in its LogonSession.
Know Your Toolset
All this token manipulation and process spawning looks very suspicious to defenders, with many of these actions now easily caught out of the box by EDR solutions. In many cases, it can be a better option to tunnel network traffic through a SOCKS Proxy Pivot (such as that offered by Cobalt Strike https://www.cobaltstrike.com/help-socks-proxy-pivoting) and perform the pass the hash attack over this network tunnel using a tool like CrackMapExec. This shifts the indicators of attack from the compromised host's local activities to suspicious network traffic.
Each scenario is different, and it is important to adapt your approach to a defender's strengths and weaknesses. If a defender is very strong at network traffic detection, it may be better to stick with local commands, whereas if they have considerable endpoint detections/controls, it is likely better to be noisier on the network than on the host.