You are currently viewing Writing your own Token Stealer

Writing your own Token Stealer

Hey Guyz! I am writing this blog with respect to my recent project TokenStealer, which can help to steal the access token of a user when an attacker has local Administrator privileges. In this blog, I will explain how I write the code for stealing the access token, what various Windows API which is being used in this project, etc. So, let’s begin!!

 

Behind the Scene

The first thing I do is to check whether the attacker has admin privileges (High Integrity ) or not. Before moving on to the code part , let’s understand what is “SeDebug Privilege”.

 

SeDebug Privilege

Suppose you want to get access to one of the processes opened by you, you can easily do it, but what if you want to get access to a process opened by some other user? In that scenario “SeDebug Privilege” is useful for us. This privilege is dangerous because if you let users debug processes owned by other users, then they can debug processes owned by System, at which point they can inject code into the process and perform the logical equivalent of net localgroup administrators anybody /add, thereby elevating themselves (or anybody else) to an administrator.Keep in mind that this privilege is being abused by the threat actors, so it might be possible that if it gets enabled by a process, then it might get flagged by AV engines or EDR.

 

In simple language, when you start the cmd.exe while clicking on it “Run As Administrator”, then you can easily get the SeDebug Privilege. Refer to the below screenshot of ProcessHacker where the first cmd.exe  is running in admin user context, but does not have the SeDebug Privilege whereas second process, cmd.exe has been started while clicking on the “Run As Administrator” option.

 

Now, let’s move on to the code part.

To get the information about my current process token, I will be using the GetTokenInformation API, which will return the Integrity Level (Medium Integrity or Higher Integrity,etc. ) of my current process token. The first parameter is GetCurrentProcessToken() which will return the handle to the current process token. The second parameter is the value from TOKEN_INFORMATION_CLASS which will specify what things we want to query from our current process token, which is TokenIntegrityLevel, then the third parameter is a pointer to a buffer where the information gets stored. If you look closely, I have defined the type of ptml parameter as TOKEN_MANDATORY_LABEL, because on getting the information from the function, it will return in this data type format only, refer here

The fourth parameter is Token Information Length which I have set to a random “1000” value, you can increase this if you get the error “Please Increase value of TokenInformationLength”. The fifth parameter is a pointer to DWORD, which will basically store the length of token information.

Now once I get the information, I will check whether the value returned by the above function is true or false, if it is true then we move ahead and compare the two variables, because the code will not work if TokenReturnLenght is greater than TokenInformationLength, as per Microsoft.

Do you remember when  I told you about the data type of pmt variable? It was TOKEN_MANDATORY_LABEL which is basically a structure and it contains only one member which is variable to another structure SID_AND_ATTRIBUTES. Refer below.

typedef struct _TOKEN_MANDATORY_LABEL {
SID_AND_ATTRIBUTES Label;
} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL;

Now, the “label”, as I said earlier, is a variable to SID_AND_ATTRIBUTES structure which contains a member that is a pointer to SID structure, which we need eventually.

 

typedef struct _SID_AND_ATTRIBUTES {
#if …
PISID Sid;
#else
PSID Sid;
#endif
DWORD Attributes;
} SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES;

 

We only need PSID variable which is a pointer to the SID that helps to uniquely identify the users and groups and hence helps in getting the integrity level of a user.

Now, we are assigning the value of sid into our new variable called as “sid”.” ptml” is a varible to TOKEN_MANDATORY_LABEL , so it will first access the member “label” which is a pointer to _SID_AND_ATTRIBUTES , then it will query for Sid member and assign it to “sid” variable. Once we get the sid value , we will use the function GetSidSubAuthority()  that allows you to retrieve the sub authorities of a SID, which are components that make up the SID. In the context of integrity levels, the last subauthority of a user’s SID often represents their integrity level.

That’s how we are querying the Integrity Level of a user. Now next part is to assign the “SeDebug Privilege” to the user token, which is basically an optional part, because we already checking if the user has a higher integrity level or not, if it has then it should have the SeDebug Privilege. I am applying the privileges just to be confident in executing the code. Let’s see how to assign/change a privilege of a token.

We are first using the function OpenProcessToken(), to open the current process token with TOKEN_ADJUST_PRIVILEGES and TOKEN_QUERY privileges, these privileges are necessary to assign any privilege in an access token. Then we are using LookupPrivilegeValueA() to retrieve the “locally unique identifier” (luid) which will help to identify privileges in a local system.

First parameter is NULL because we have to check privilege on the local system, the second parameter is the name of the privilege of which we want the LUID , which is SE_DEBUG_NAME(SeDebug Privilee) as per Microsoft documentation.The third parameter is a pointer where we want to store the returned LUID of the privilege.

We move on to the next function AdjustTokenPrivilege(), where the first parameter is the handle to access the token of which we want to enable the privilege, second parameter will be FALSE because we want to enable a privilege, not disable any privilege.The third parameter is “tp” which is a pointer  TOKEN_PRIVILEGE structure. According to Documentation, a pointer to a TOKEN_PRIVILEGES structure specifies an array of privileges and their attributes. So , basically we have the LUID of the “SeDebug” Privilege , so we will assign that using the tp variable and also if we want to enable this privilege then the attribute value will be SE_PRIVILEGE_ENABLED.

If look above in the code, the tp variable is used to assign those values in the TOKEN_PRIVILEGES structure, fourth parameter is just size of the TOKEN_PRIVILEGES structure and remaining two are NULL.

Overall, AdjustTokenPrivileges() function is trying to enable some privileges to the current process access token, and what are those some privileges, they are stored in the TOKEN_PRIVILEGES structure using tp variable. That’s how we enable the “SeDebug” Privilege. Now we will see, how to steal the token of another user.

 

First, we will find the process opened by the target user, in this case, it is “notepad.exe”. I am not going to explain how you can obtain a handle to a  process, you can easily google it.Now, if we have handle to target process, then we are going to use OpenPocessToken to get the handle to access token of that particular process, having TOKEN_DUPLICATE privilege, so that we can use/duplicate this token.If we get the token, then we will use DuplicateTokenEx() to get the token of that user. The first parameter to this function is handle to access token of the target process, then the second parameter is the Desired access rights we want for the new token,which is MAXIMUM_ALLOWED in this case.Third parameter is NULL, which is related to inheritance of token. Fourth parameter is Impersonation Level and we want “SecurityImpersonation” to duplicate the access token. Fifth parameter specifying that we want “Primary token”, and the last parameter is handle to newly obtained token.

 

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
);

If we get the Impersonated Token, then we want to create some process with this access token, I have specified to open “cmd.exe” using the obtained acces token, you can change it accordingly.The first parameter of CreateProcesswithTokenW() is handle to access token of target user, then the second parameter is LOGON_WITH_PROFILE, specified as per microsoft documentation. Third parameter is name of application which we want to open with the access token, Fourth parameter is the command with we want to trigger with application , I have specified this as NULL, because I Just want to open “cmd.exe” without any arguments. The fourth parameter is flags which control how the process is created, you can specify anything as per documentation. Fifth parameter will specify the pointer to the environment block of the process, if it is NULL it will create the environment block as per lpUserName, which I think will be in the context of the target user.

The next parameter will specify the directory of the process created, if it is NULL then it will use the same directory as calling process, after that we have a parameter which will specify the pointer to “STARTUPINFO” structure which will decide the appearance of the window for the new process. If you look in the code, I have specify only the “cb” value of the STARTUPINFO structure, which is the size of the structure, and then uses the “zeromemory” to fill the block of memeory with Zeros, which is much better than making the pointer to point at NULL.If the lpDesktop member is NULL or an empty string, the new process inherits the desktop and window station of its parent process.So, in this case newly created process inherits the desktop from its parent process, because we haven’t set the lpDesktop member of the structure. Last parameter is pointer to “PROCES_INFORAMTION” structure which will receive information related to newly created process.

That’s how we make a token Stealer 🙂 . If you like this please share this with security community. Hope you guyz like this!!!