Follow the Link: Exploiting Symbolic Links with Ease
This blog is part one of a continuing series describing research I performed between April and July 2019.
In the recent years, the most common way to find vulnerabilities has been fuzzing. Since I’m interested in innovation and finding weird, new ways to do things, I tried to develop a complementary approach to fuzzing. This approach helped us to find many vulnerabilities – around 40 CVEs across major software vendors using only two types of attacks. By showcasing our new approach and the most interesting vulnerabilities we discovered, we hope to increase awareness among software vendors and security researchers alike.
In the first part, we will explore the attack vector for abusing privileged file operations bugs along with how to fix those bugs. To start, we will walk through CVE-2019-1161, a vulnerability in Windows Defender that can be exploited to achieve Escalation of Privileges (EoP), which Microsoft released a patch for it in August patch Tuesday.
Hundreds of millions of Windows machines –- any machine running Windows 7 and above – are vulnerable to the arbitrary delete vulnerability. A malicious user can abuse Windows Defender to delete any file he wants with NT AUTHORITY\SYSTEM privileges. The vulnerability lies in a process named MpSigStub.exe, which is executed by Windows Defender with high privileges. This process suffers from an impersonation issue that could lead to EoP using Object Manager symlinks.
Let’s dig in.
Around four years ago, James Forshaw showed that the Object Manager symbolic links can be used by attackers to escalate their privileges. Even though some time has passed, abusing symlinks to get EoP is still very relevant today. In this blog entry, we will see how symbolic links work under the hood and investigate an easy-to-exploit vulnerability in Windows Defender that ends with the arbitrary delete bug.
Those who disabled the automatic updates\disabled Windows Defender are recommended to download the latest update to mitigate this vulnerability.
Not all things are as they seem. The C: directory, for instance, is not a real directory on disk. Instead, the C: directory is a symbolic link object in the object manager namespace, which is isolated from the regular file system.
The object manager namespace is organized in a hierarchy tree. Each level is an object of the type of OBJECT_DIRECTORY. Each OBJECT_DIRECTORY has a name and can have multiple nested object directories within. For example, the \Device object directory contains the named device objects created by drivers. In each object directory, there are a bunch of objects of many types: Symbolic Link, Mutex, Object Directory, Event, ALPC ports, etc.
Let’s go back our example of the C: directory. If we look for the C: directory physical path, we can see it is located in the \Global?? object directory.
We learn that the C: directory is an object of the Symbolic Link type. This object holds the value of the physical path on the file system, the real device. In our case, C: is reparsed to \Device\HarddiskVolume4. The kernel calls the ObpParseSymbolicLink function to resolve the symlink path. For instance, when you access a path that contains the C: name, such as when creating a file, it parses the name into the physical path on the device \Device\HarddiskVolume4. The path value varies depending on the number of hard drives you have. To create a symbolic link, you do not need admin rights, a fact that makes it useful from an EoP perspective.
It is impotent to note that a regular user is limited in the creation and deletion of symbolic links in the object manager. He can’t create or delete new symlinks under most object directories, such as the \Driver or \Global?? directories. If he could had the ability to do that, the operating system would be unreliable from a security point of view. Imagine changing the \symlink value of drivers under the \Driver object directory. Instead, a non-admin user can create a symlink at \RPC Control object directory.
Symbolic links can be created by any user under some object directories, but by themselves are not sufficient to successfully escalate privileges. However, combining them with a different object, such as NTFS junction point, just might do the trick.
A junction is an object that maps a directory into a different directory. There are two requirements for creating a junction:
- Have write-permission (delete is not needed) over the desired directory.
- Have the ability to delete all files inside the directory.
Still, you can’t just create a file in the junction folder and expect it to be created in a different directory without a privilege check. Otherwise, any user would be able to create a junction into the System32 directory and then drop a file there, which would cause a major security issue.
Now for the fun part. How do we combine junctions and symlinks to get EoP?
First, we create a directory junction. This is possible if we have to write permission over a directory. The second part consists of creating an Object Manager symlink on, let’s say, a log file in that directory that would point to a file in a protected directory. Here we will likely run into an issue of insufficient permissions. If the user can’t perform the file operation on the original file because he lacks the privileges, then surely he won’t be able to reparse on the other end of the symlink, right?
Not necessarily. If we find a privilege application that performs file operations (write, delete) on a log file in a directory we control, then it can perform that same operation on the file that the symlink points to. Let’s say we find a privileged application that tries to delete a file with NT AUTHORITY\SYSTEM privileges, then we can reparse the delete operation for any other file that we have access to. Unfortunately, using this technique to delete AV’s executables\DLLs isn’t reliable in most cases, due to the fact that they have a mini FS filter driver that protects their files. In other words, we abuse the fact that a privileged code performs file operations to non-protected locations in the file system.
Windows Defender <MpSigStub.exe>
The Windows anti-malware solution, Windows Defender, which has been installed on every version of Windows since Windows 7, is responsible for protecting the system from malware. Windows Defender runs in the context of NT AUTHORITY\SYSTEM and spawns a process named MpSigStub.exe, both of which are used by the Windows update service.
When you use Automatic Windows Updates or install a stand-alone installer, the update package is automatically extracted into a temporary directory. This operation is performed by the MpSigStub.exe installer (local system privileges). After the update is extracted in the temp folder, MpSigStub.exe will perform various checkups to determine if the extracted files are ready to be applied. Finally, it will delete the file.
In our case, we identified that the MpSigStub service tries to write and delete a file in the %localAppData% directory: C:\Users\user\AppData\Local\Temp\MPTelemetrySubmit\client_menifest.txt. Microsoft states that this process of updating and deleting happens three times a day. However, on my machine, it occurred around five times a day.
Not a Speculative Attack
If you followed closely, you can probably guess how to exploit the fact that MpSigStub.exe performs a write\delete operation as a privileged application – by using symlinks, of course. Many directories in local AppData are not admin-protected, including the directory C:\Users\user\AppData\Local\Temp\MPTelemetrySubmit. Therefore, we can take advantage of the fact that MpSigStub.exe deletes a file. Having arbitrary delete primitive is nicer than having the ability to create a file in arbitrary location. This is correct because, in most cases, we can’t alter the content of the as-yet-to-be-created file due to the fact that it inherits the ACLs of the target directory. Still, we can use arbitrary create to cause systemwide DoS., just create a file in C:\Windows\System32\en\ named Microsoft.Windows.Common-Controls.DLL. Then boot up your machine. Please only do this if you’re running in a VM, because otherwise it would make your machine unbootable.
The first step in a successful symlink attack is creating a junction. In this case, we create a junction on C:\Users\user\AppData\Local\Temp\MPTelemetrySubmit that points to a protected directory. In our case, we point to \RPC Control.
The next step is just creating an object manager symlink (you can use James’s toolkit for simplicity) from the “txt” file client_menifest.txt to any desired location on the disk. For a quick and easy DoS, we can delete the pci.sys driver from C:\Windows\System32 or you can use the vulnerability to delete critical files from security products and disable various security mechanisms.
Alternatively, escalating to administrator with this primitive alone would prove to be difficult on vanilla Windows We might be able to use this to replace a DLL\delete and mess up DLL loading for some privileged applications. This could probably be exploited by third-party vendors. We could also try deleting temp files that can be replaced by a regular user or replacing a binary file of a service in a directory that has misconfigured permissions.
As with many EoP vulnerabilities, such as CVE-2019-1142, which was patched in the last patch Tuesday, the MpSigStub.exe process doesn’t correctly impersonate the local user. If you do file operations on Windows on unprotected directories, you must do it with an impersonated token or else you risk a file manipulation attacks. Creating client_menifest.txt in the %localAppData% is by no means a dangerous thing, but I strongly urge developers to impersonate the local user instead of using the primary token. This will help avoid many bugs like this one.
We weren’t able to get full write permissions on the newly created file. Therefore, we can’t use any DLL Hijacking methods like putting a malicious DLL in System32 to escalate our privileges to the admin level. Nevertheless, we can either create a file in an arbitrary location or delete any desired file that might lead to full privilege escalation in certain cases.
Our suggested solution was to change the ACLs of the MPTelemetrySubmit directory and the files underneath. By doing so, a malicious user won’t be able to delete the files inside the directory. Therefore, he will not be able to change the MPTelemetrySubmit directory into a junction, which prevents the exploit entirely. Alternatively, we can amitigate vulnerability by impersonating the local user correctly. .
Symlink attacks are easy to execute and have the potential to cause some serious damage, as we have seen. In this case, we were able to execute a denial of service attack without any difficulty, which is nice, but could we do better? In short…yes. Getting arbitrary write is possible on some occasions.
In my next blog, we’ll discuss this and the question of how we might prevent symlink creation from a non-privileged file to a protected path.
June 19th, 2019: Vulnerability identified and reported to Microsoft.
June 20th, 2019: Microsoft received the report and decided to open case 52643.
July 8th, 2019: Microsoft reproduced the issue and decided to fix the vulnerability.
Aug 13th, 2019: Microsoft released a patch and issued CVE-2019-1161.