Introduction
In this blog post, I’m going to show the most three common access token techniques.
- Steal Token
- Revert2Self
- Make Token
In upcoming posts I’m gonna talk about how to build a token vault to store your tokens.
Access Token Manipulation (Steal Token)
How many time did you use steal_token, Revert2Self, and Make_token modules in C2 like Cobalt Strike? Too much, right?!
In this section I will show you how to write your own Access Token Manipulation in C/C++.
Short Forward
Token Manipulation is used to impersonate another running process token.
So basically we can do that through OpenProcess as PROCESS_QUERY_LIMITED_INFORMATION.
Note
There is no need to use PROCESS_ALL_ACCESS.
Then you can pass this Opened handle of the target process to OpenProcessToken to open a handle to the Access Token of the process.
Then duplicate the token of the specified process through DuplicateTokenEx.
Note
Here is no need to use TOKEN_ALL_ACCESS. You can just pass MAXIMUM_ALLOWED to the second member of DuplicatedTokenEx.
Then you can execute whatever you want by passing the stolen handle to CreateProcessWithTokenW.
Weaponization
As we are trying to steal token of another process so we need to get that process id. So in the first few lines I just check if the user entered the process id of the target process or not.
Then initialize some variable to use them later.
So, first mission we need to do is to open handle of the target process and that could be done through OpenProcess
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
OpenProcess takes 3 arguments.
- dwDesiredAccess // The access to the process object. Here is a list of Access Right you could use Access Rights but in our case we can use PROCESS_ALL_ACCESS or PROCESS_QUERY_LIMITED_INFORMATION, but It’s recommended to use PROCESS_QUERY_LIMITED_INFORMATION for less suspicious activity.
- bInheritHandle // This process inherit the handle
- dwProcessId // Process Id of the target process
Then we need to check if the handle is opened successfully or not.
After opening the target process handle, we will need to open handle of the process handle and that could be done through OpenProcessToken
BOOL OpenProcessToken(
[in] HANDLE ProcessHandle,
[in] DWORD DesiredAccess,
[out] PHANDLE TokenHandle
);
OpenProcessToken takes 3 arguments.
- ProcessHandle // the returned value of OpenProcess.
- DesiredAccess // Access Rights of the Token Handle.
- TokenHandle // [OUT] Token Handle variable.
As always we need to check if the handle is opened successfully or not.
Then we will need to duplicate that token handle and that could be done through DuplicateTokenEx
BOOL DuplicateTokenEx(
[in] HANDLE hExistingToken,
[in] DWORD dwDesiredAccess,
[in, optional] LPSECURITY_ATTRIBUTES lpTokenAttributes,
[in] SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
[in] TOKEN_TYPE TokenType,
[out] PHANDLE phNewToken
);
DuplicateTokenEx takes 6 arguments
- hExistingToken // the returned value of OpenProcessToken
- dwDesiredAccess // Access Rights
- lpTokenAttributes // Attributes of the Token
- ImpersonationLevel // Impersonation Level of the token we are going to use SECURITY_IMPERSONATION_LEVEL
- TokenType // Token Type
- phNewToken // [OUT] New Token Handle variable.
So, now we have the duplicated token handle, so now we have two options to create a new process through the duplicated token.
- CreateProcessWithTokenW
- ImpersonateLoggedOnUser, and CreateProcessWithLogonW
First Way
CreateProcessWithTokenW can be used to new process through the stolen token.
CreateProcessWithTokenW takes 9 arguments
BOOL CreateProcessWithTokenW(
[in] HANDLE hToken,
[in] DWORD dwLogonFlags,
[in, optional] LPCWSTR lpApplicationName,
[in, out, optional] LPWSTR lpCommandLine,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCWSTR lpCurrentDirectory,
[in] LPSTARTUPINFOW lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
Second Way
ImpersonateLoggedOnUser can be used to impersonate the security context of a logged-on user through token.
BOOL ImpersonateLoggedOnUser(
[in] HANDLE hToken
);
ImpersonateLoggedOnUser just takes the token which the duplicated token.
Revert Impersonation Token (Revert2Self)
What if you need to return to your primary token, what do you need to do?
There is an API that’s called RevertToSelf
BOOL RevertToSelf();
You just need to call that function with no arguments to revert the token and if the returned value is nonzero so you successfully revert the token. And we have done : ) looks easy, right?
Make Token
As we are trying to make a new token for the user, we must have the credentials for that user.
To log in with the user credentials and return the token handle, we can do that through LogonUserA
BOOL LogonUserA(
[in] LPCSTR lpszUsername,
[in, optional] LPCSTR lpszDomain,
[in, optional] LPCSTR lpszPassword,
[in] DWORD dwLogonType,
[in] DWORD dwLogonProvider,
[out] PHANDLE phToken
);
LogonUserA takes 6 arguments
- lpszUsername // The username value. actually you can set UPN format but if you did that you must NULL the domain value.
- lpszDomain // Domain Name.
- lpszPassword // User’s Password.
- dwLogonType // The type of logon operation to perform
- dwLogonProvider // Specifies the logon provider
- phToken // [OUT] Token Handle variable.
After returning the user token handle you can use ImpersonateLoggendOnUser as we did in the Steal Token section.
You can use direct System calls to bypass any userland hooks. that could be done through SysWhispers3 or you can Implement your own Syscall. and if you re-write this in CSharp you can use DInvoke/DInvoke.DynamicInvoke at master . rasta-mouse/DInvoke
If you have feedback please go ahead and DM me on Twitter, See you in the next blogpost.
Peace out!✌️