Privilege Escalation on web01
In this section, we’ll attempt to escalate our privileges to SYSTEM
so we can obtain the NTLM hash of the machine account.
Since we compromised an IIS server and
gained code execution as the IIS DefaultAppPool,
we should have impersonation privileges.
We can quickly check this with whoami.
The output indicates that we have impersonation privileges.
Since this is a Windows Server 2019 machine,
we can either use RoguePotato or PrintSpoofer
to attempt to escalate our privileges.
Given that RoguePotato requires access to a second machine
and is generally more complex to execute, we’ll instead use PrintSpoofer.
To use PrintSpoofer, we can download the Visual Studio project and compile it.
Unfortunately, if we upload and execute PrintSpoofer,
even to just display the help menu, Windows Defender flags it.
Since PrintSpoofer is written in C++, we can use the
Invoke-ReflectivePEInjection PowerShell script
to bypass antivirus as long as we disable AMSI.
However, invoking an executable through reflection can be tricky,
especially when it requires arguments.
As an alternative,
we can use our custom-coded PrintSpooferNet implementation
that we developed in a previous module.
Since this is custom-coded, it will likely evade detection.
One caveat of this procedure is that PrintSpoofer bundles
both the pipe server and the printer bug in a single application,
while our custom implementation requires SpoolSample.
Luckily, Windows Defender does not detect SpoolSample.
Before we start the attack, we must discuss another issue.
When we previously developed the PrintSpooferNet code,
we executed it from an interactive logon session.
In this case, we must execute it in the context of the service account
that has not logged in.
Because of the non-interactive logon session,
when we invoke CreateProcessWithTokenW
after having impersonated the SYSTEM account,
the new process will immediately terminate.
To solve this, we have to modify the code,
revisit some of the API arguments,
and introduce a number of concepts.
First, one of the arguments for CreateProcessWithTokenW,
is an environment block array
that contains metadata related to the user
and a startup directory.
In our previous attacks, we used NULL for this argument,
as the newly created process sed an environment
created from the profile of the logged on user.
In this case, since the service account did not perform an interactive logon,
we have to provide an environment block.
Second, the lpCurrentDirectory argument,
which specifies the initial drive and working directory for our shell,
also needs to be specified for the same reasons as explained earlier.
We can generate the values for these parameters with the
CreateEnvironmentBlock
and GetSystemDirectory APIs, respectively.
CreateEnvironmentBlock accepts three parameters.
The first is an output pointer to the created environment block.
The second argument is the user token.
In our case, this is the SYSTEM token after the successful privilege escalation.
The third is a flag
to signal whether to inherit from the current process’ environment.
As the current process does not have an environment,
we must set it to false.
GetSystemDirectory accepts a string buffer
that will be populated with the system directory,
along with the maximum allowed size of the string.
We’ll add the code for the DllImport statements taken from pinvoke.net,
along with their implementation in the code.
We will also use the WindowsIdentity.GetCurrent() C# method
to print the name of the impersonated account,
and finally, we call the Win32 RevertToSelf API
to revert back from the impersonated SYSTEM token.
In previous examples with CreateProcessWithTokenW,
we have not called RevertToSelf first.
This means CreateProcessWithTokenW
has been called while impersonating \the SYSTEM token,
which works most of the time because SYSTEM generally
has the SeImpersonatePrivilege as well.
However, for some processes running in SYSTEM context,
this privilege has been removed,
so to ensure that our attack succeeds, we can revert back
to IIS DefaultAppPool and use its impersonation privilege.
We need to modifiy two additional arguments for
CreateProcessWithTokenW to get our code working.
These are dwLogonFlags, and dwCreationFlags,
which we previously left as NULL.
First, we must specify the LOGON_WITH_PROFILE logon flag,
otherwise some of the registry usage will fail during process execution.
In addition, the environment block we created uses Unicode
so we must also specify the CREATE_UNICODE_ENVIRONMENT creation flag.
These two flags are specified through enums,
which we must manually implement in our code.
Next, we must consider desktops, or logical display surfaces.
Interestingly, all processes must have a designated desktop
even if their windows are hidden.
When a logon session is created,
CreateProcessWithTokenW will automatically use the desktop of that session.
However, in our case we must explicitly specify it
in the lpDesktop field of the STARTUPINFO structure
passed to the API as the eighth argument.
Here, we set the lpDesktop property to the name of the default desktop.
We’ll update the call to CreateProcessWithTokenW
to reflect our changes.
The final step is to choose the application
to use with CreateProcessWithTokenW.
We could use PowerShell to start an in-memory PowerShell shellcode runner,
but we must take AMSI into account.
In more complicated attacks like this one,
it is often better to take a simple approach
and place the executable on disk.
This means we must make sure the executable evades AV detection.
To generate the executable, we’ll reuse our AV bypass C# shellcode runner
that we used for our web shell, since it was successful.
Now that the met.exe shellcode runner is created
along with the modified PrintSpooferNet application,
we’ll upload them along with the SpoolSample executable
to the upload folder on web01.
Now we’re ready to launch the attack.
First, we’ll ensure that our listener is running in the background
on port 443 to catch the SYSTEM shell.
Then we’ll return to our initial session,
and run PrintSpooferNet from that shell.
We’ll then background the initial shell channel,
launch a new shell,
and run SpoolSample with web01 as the target.
Once SpoolSample completes, we’ll switch back to the original channel,
where the print spooler connects to the named pipe.
Immediately following the print spooler connection,
a new Meterpreter session is created.
After we background the PowerShell prompt and the current Meterpreter session,
we can interact with the new session,
where we find our SYSTEM shell.
Excellent!
In this section, we managed to elevate our privileges
from the IIS DefaultAppPool account
to that of local SYSTEM
through impersonation while bypassing antivirus.
This allows us to continue our attack in the next section
by obtaining the NTLM hash of the computer account.