Straight Outta Script Kiddie Zone : Deep dive on how to get a SYSTEM shell on Windows
Gather around, neighbors! It is time for a short pentest adventure story.
In a recent engagement, we found ourselves having issues to get a command prompt under
nt authority\system context, even if we had an administrator access to our target (what a shame😅).
Instead of drowning in our failure and accept our lack of skills, I decided to throw away my script kiddie side and take a few hours to think about the situation.
The first question that crossed my mind then was : Why we are failing at this ? Well the answer was clear : We did not know at the time what we were doing, we were running tools like PSExec, WMIExec left and right without really understanding how things works. And to be honest when I think about calling PSExec in order to get an
nt authority\system shell, I realize how lack of understanding lead us to use a Swiss Army knife as a screwdriver (that being said using Psexec will give you a system shell … in most cases 😶).
Local Administrator Account VS NT AUTHORITY\SYSTEM
As you may know, having local administrator privileges does not mean you can do what ever you want on the system. So for example using Mimikatz’s
lsadump::sam or call
LsaRegisterLogonProcess()at this stage was not possible for us unless we manage to have
nt authority\system context. And this all made sense when I started learning about
Access Tokens. After all we could have run
lsadump::sam to get it all done, but as I said we had no idea on what we are doing and how the tools are working 👎 👎 But let’s go back to the
Access Tokens thing.
According to the Windows documentation, An access token is an object that describes the security context of a process or thread. The information in a token includes the identity and privileges of the user account associated with the process or thread. When a user logs on, the system verifies the user’s password by comparing it with information stored in a security database. If the password is authenticated, the system produces an access token. Every process executed on behalf of this user has a copy of this access token.
The security context we have using the Local Administrator account with UAC consent looks like this :
And in the other hand the access token under
nt authority\system is more like this :
As we can see there is some Privileges that are specific to the
nt authority\system account. Which means : in order to get an access similar to ROOT in Linux we need to have a SYSTEM context. In reality this is what is happening behind the scenes when we are calling
Why the hell can’t we do it ourselves ?
At this stage I was quiet curious on how can we have a shell as
nt authority\system , and I did not want to rely on another tool again. So I decided to dive a little deeper in the beautiful world of Windows
Access Tokens .
By reading the Windows documentation I learned that every process has a primary token that describes the security context of the user account associated with the process. But more interesting the Windows API offers a whole set of Functions to manipulate
Access Tokens, such as enable/disable the privileges in an access token, create a new primary token that duplicates an existing one, and so on … With all of these functions, it is possible for a thread to impersonate a client account and get a copy of it’s primary token, we call this process Impersonation.
Impersonation is the ability of a thread to execute using different security information than the process that owns the thread. So basically we can use the Windows API Calls to get a copy of a process primary token and use it to start a new program that has the same exact context. And of course in this case the end goal is to target an existing process running as
SYSTEM and use the copy of its token to start a new shell which will hopefully be running with
nt authority\system context 🙏
Nothing new so far … but let’s do this !
Of course nothing of this is new to the Offsec community but now that I had a clear idea on what to do, and how many OSTs (Long live the OSTs ❤) are abusing Impersonation, I felt confident enough to write my own implementation of the process and use it to get our
nt authority\system command prompt😃. And to make the journey more fun I decided to use C# as I never wrote anything in this language, and I thought it would be such a great introduction.
The first step in this process was to identify the PID of a process running as
nt authority\system . This can be done with the following Powershell command:
Enabling SeDebugPrivilege with AdjustTokenPrivileges function
In order to obtain a handle to the primary token of the
nt authority\system process we are targeting, we need to have a specific Privilege which allows use to debug programs
SeDebugPrivilege . And if you remember the
Access Token we got using a Local Administrator (see image above ☝️), the
SeDebugPrivilege was available. But it was marked as Disabled. As we will be using this specific privilege in the process, it is mandatory to enable it before proceeding. This is where the function
AdjustTokenPrivileges can be useful. Since this is a part of the unmanaged code of a Windows API we need to declare and import all the signatures/structures we need to successfully call
AdjustTokenPrivileges . So the following C++ signature given in the windows documentation :
Can be declared in C# like this : ( All credits to pinvoke.net for the amazing Wiki )
Now that we have a function that can enable a specific privilege we can use it to enable the
SeDebugPrivilege for the current process. We just need to use a few other API calls :
GetCurrentProcess to get a handle for the current process,
OpenProcessToken to get a handle for the
Access Token of the current process,
LookupPrivilegeValue to retrieve a unique identifier of the privilege we are looking for and finally the
AdjustTokenPrivilege call to change the state of that privilege. After a few more definitions :
We can now use the following code to enable
SeDebugPrivilege we can now access the primary token for a system process and get a copy of it. For this we will use
OpenProcess to get a handle to the targeted process ( the PID is obtained using the command earlier ☝️). Then we call
OpenProcessToken one more time to get a handle to its token. According to the windows documentation the
OpenProcess C++ signature is like :
Which translates in C# to :
And the code block that allow us to get a handle to the primary token which we will be storing at
tokenHandle variable :
This the part when the magics finally happens, with a handle to the token we have so far, we can duplicate it and use the duplicated token to create a new process which will use the duplicated token as a primary token. The new process we will be creating is nothing else than
cmd.exe . As we will use 2 new API calls we still have some more definitions and structure to declare :
At this stage we should have all the prerequisites to achieve our goal. The following *ugly* C# code does the trick :
And by the end of the day, we build and run the project feeding it with the PID of a non protected SYSTEM process like Winlogon :
That’s it, we finally got our meterpreter’s
getsystem alternative. This concludes our little adventure. As I said we did nothing revolutionary but it feels great to understand how things work.