Monday, December 16, 2019

Chasing polar bears: part one


Edit: sample bug now patched: https://www.vmware.com/security/advisories/VMSA-2020-0002.html

(note: click on images to view larger version)

Index

I. Using Procmon
II. Writing a PoC
III. Directory junctions

I. Using Process Monitor


Disclaimer: I am currently, as of publishing this, not working for any company.

a. Introduction


Download: https://docs.microsoft.com/en-us/sysinternals/downloads/procmon

This will be your main tool for tracking down filesystem race conditions.
Process Monitor is a tool to record all file operations that occur in the filesystem, thus making it easy to find possible timing windows that can be exploited.

b. Finding a bug


Lets see how we find "timing windows" in Process Monitor.
A timing window is nothing more then a duration of time where-in a bug can be exploited.
A lot of filesystem bugs are based on exploiting timing windows.. a.k.a winning a race (condition).

A few years ago I saw the following folder:

c:\windows\installer (hidden by default)

For whatever reason, some of the .msi files in there will auto-elevate when you run them. They do not prompt, even on non-admin.
This means that we can run installer files, doing file operations at higher privileges, no prompts required.
The limitation being that this only applies to .msi files inside of c:\windows\installer (which are already installed programs and features).

To reproduce this bug, you will need a windows 10 vm with vmware tools installed.
You can find many other bugs in other third-party .msi files and perhaps even Microsoft ones (assuming they are already installed by an admin and are under c:\windows\installer)

Go to c:\windows\installer and if vmware tools is installed, locate the vmware tools .msi file (from an non-admin account), mine is about 44mb big, just sort by size, you can confirm you got the right one by right clicking, selecting "Properties" and going into the "Details" tab. The names of the .msi files are randomized, so it won't be the same on your system:



Launch process monitor and apply the following filters (press control + L):


-We only want to see installer related processes.



 -We do not want to see file operations in c:\windows, same for c:\program files.


 -We also only want the installer process running at system, discard any running at medium.



-We also only want CreateFile operations, otherwise there will be way to much clutter that doesn't really matter.
Once we find an interesting file handle being opened we can disable this filter again to see what happens with it.

There is one more thing we need to do, if you double click on a CreateFile operation in Process Monitor, sometimes you will see that it is "impersonating":



This means that it will impersonate someone else, in this case the local user, so this CreateFile call will only have the same access rights that the user has (because it pretends to be the user!).
This isn't useful for us, so we discard those too, but make sure to only discard when it impersonates the user, in a few cases it might actually impersonate itself (nt authority/system):


In my case the user is part of the DESKTOP-L7TMINM group (which is the computer name), hence the filter "contains Impersonating: DESKTOP", but it may be different for you.
Just type "whoami" in a command prompt to see your computer name and user.

Hotkeys for Process Monitor:

-Start/stop recording: control + E
-Clear the screen: control + X

Start recording and enter the following command (where 368c0.msi is the vmware tools .msi file, different name on your system):



This is telling the installer to "repair" the vmware tools installation.
Make sure to press "no" when it asks for a reboot at the end.
After it completes we will see that it does a lot of file operations in c:\ProgramData.
The ProgramData folder is interesting, because sometimes folders might be write-able by a user.

This one looks interesting for example:
C:\ProgramData\VMware\VMware CAF\pme\scripts\stop-listener.bat

If we go to this folder and check the permissions:



We see it being given write permissions to non-admin users.
We can confirm this by creating a file in the scripts folder:



It works!
However we cannot modify any of the existing files, which is what we are really after.
These can only be changed by an admin user.
So lets dig deeper.

Add a filter for C:\ProgramData\VMware\VMware CAF\pme\scripts\stop-listener.bat:



Resulting in:



Looking at this, at the bottom, we see "NAME NOT FOUND" twice, and right before that a handle being opened with delete permission.
If we remove the "Operation is CreateFile" filter we can see more details:


It is moving the file somewhere else (which is what SetRenameInformation does)!
So for a short time window we can actually drop a file there with the same name, but we have to do it before it gets created again.

 Lets write a PoC for this and see what happens!

II. Writing a PoC


We know that we have a timing window, right after the file stop-listener.bat gets deleted and before it gets created again.
That is why it is called a timing window, it is literally a window in time!

We could use oplocks to determine our timing window, but I think the much easier route would be spamming CreateFile until it succeeds and creates a file right after the original one is deleted.

We can also increase the thread priority to speed everything up and make sure we hit our timing window.
However, when increasing thread priority, make sure you are running more then one cpu.. as it might freeze your VM otherwise.

Lets try something like this:

HANDLE thisthread = GetCurrentThread();
SetThreadPriority(thisthread, THREAD_PRIORITY_TIME_CRITICAL);

HANDLE testhandle = NULL;
do {
CloseHandle(testhandle);
testhandle = CreateFile(L"C:<\\ProgramData\\VMware\\VMware CAF\\pme\\scripts\\stop-listener.bat>", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);           
} while (testhandle == INVALID_HANDLE_VALUE);
CloseHandle(testhandle);

Full poc code:
https://github.com/SandboxEscaper/chasingpolarbears/tree/master/vmwarebug
(also has headers for junctions and oplocks)

Run polarbear.exe (compile above git project to get the exe) and then run our .msi file again using the repair flag.

Note: You can also pass the /quiet and /norestart flag to hide any UI as shown in the video.
Video takes a while since to finish up.
In this particular case I couldn't get the /norestart flag working for whatever reason.. but maybe you can find a way around it.
Atleast the UI is gone!



Before:



After:



Using this bug we have basically gained write access to a file we would otherwise not have write access to.
Ofcourse exploitability is another thing.
But just look at all the privileged file operations going on, I wouldn't be surprised if atleast one of those is exploitable. And some of the other files in c:\ProgramData\VMware suffer from the exact same bug.
I personally just care about bugs in Microsoft code, so I'm going to stop right here.
Also, I just wanted to demonstrate a filesystem bug that was unpatched, this bug is likely not exploitable, unless any of the files that you can take control over get executed by a higher privileged process, but I have not investigated this.
You can find tons of other third party installers that will start doing weird file operations when passing the repair flag to them (and I definitely recommend messing around with different flags too, you never know).
Also if it starts removing files without impersonation.. that's a good candidate for arbitrary deletes!

You could just find some bugs in third party .msi files and then build a tool that scans the c:\windows\installer directory for any vulnerable ones on the target where you want to achieve privilege escalation (the randomized names won't be a problem, since you can pull all the info you need from the file details programmatically).

I vaguely remember an .msi file belonging to an old Microsoft component creating files and folders directly in c:\ when running it with the repair flag, which really isn't good. (not c:\config.msi, that I will talk about in part two). So there is definitely more bugs out there.

This bug didn't use junctions, but with junctions it is the same routine, you identify a timing window and then figure out the tools you have to use.

III. Directory junctions


a. The code


The easiest way to exploit most filesystem race conditions are directory junctions.
In this chapter I will explain how they work.

Code to create directory junctions (credits go to @tiraniddo):

https://github.com/SandboxEscaper/chasingpolarbears/tree/master/createjunctions
(you can just copy this project, also has the headers for oplocks in it)

ReparsePoint::CreateMountPoint(L"c:\\bear", L"\\??\\c:\\bear2", L"bear");

The first argument is the junction, the second one the target, the third one is the name but you can put there whatever you want, it doesn't matter at all.

In the second argument you may notice the file path starting with \\??\\, this is actually pointing to the global root.
It may be a good habit to precede your target file-path with this, as this could bypass some obscure checks. (found a bug where this was the case in the past)

b. What is a directory junction?


A directory junction is a special folder that points to another folder.



Bear is a junction, bear2 is not, you can see the icon is different.
It is basically like a shortcut, but unlike a shortcut, code trying to do file operations with  junctions won't notice that it isn't a regular folder. Which is why junctions are so powerful.

For example, you can turn folder "C:\bear" into a junction that points to "C:\bear2".
Now if a program tries to write to "C:\bear" it will follow the junction and actually write to "c:\bear2".

This is useful, because when a program tries to write to a folder, you can plant a junction there, and redirect the write to somewhere else.

Programs are not aware of junctions and will blindly follow them unless the "FILE_FLAG_OPEN_REPARSE_POINT" flag is passed. When this is the case it will not follow the junction.
However, this flag only applies to the folder being opened. So if "C:\thisisajunction\blah" is being opened, it will only check blah for a junction and is blind to the fact that "C:\thisisajunction" is actually a junction.
This is how you would bypass these types of checks.

You can see in Process Monitor when a junction is being followed:



When we run "mkdir c:\bear\blah", where c:\bear is a junction pointing to c:\bear2, you will see the folder being created in c:\bear2 instead in Process Monitor.
Notice the first one, where the result says "REPARSE", this means a redirection happened, because we wrote into a junction.

c. conclusion


What you need to take away from this is that you can create a folder, that points to another folder, which will redirect file operations.
See it like a wormhole!

This is nothing but a tool.

Know that you can create wormholes in the filesystem to redirect file/folder operations (creating files and folders, reading/writing files, and so on..).
Forget all the rest, forget the technical side of it, because that's how people get lost, especially when starting out, simplify things in your head... you have the power to create wormholes in the filesystem, that's all.

In part two I will show you how to use Junctions when you can't just win a race condition by spamming CreateFile!