DLL hijacking is an attack that exploits the Windows search and load algorithm, allowing an attacker to inject code into an application through disk manipulation. In other words, simply putting a DLL file in the right place causes a vulnerable application to load that malicious DLL. This attack method has been in use since the beginning of Windows 2000 and is still alive and kicking.
Most of the time, an attacker uses DLL hijacking in order to gain code injection into a digitally signed application. Many endpoint security products are based on whitelisting signed applications, making attackers’ lives difficult when they try to run unsigned code. Traditional code injection methods often include opening a handle to a remote process, which is a noisy activity that could cause an alert. DLL hijacking, on the other hand, can be extremely useful in those cases, since the bad guy only needs to write malicious payload into a specific path for the vulnerable (whitelisted) application to happily load their code.
So, is there a cure for DLL hijacking? There are multiple tools that claim to detect applications vulnerable to DLL hijacking.
In our opinion, however, those tools are not sufficient. The majority of them seem to detect only first leg exploitation – writing a DLL file to an unprotected folder. However, this is not enough, partially because they lack the ability to detect missing DLLs. These are non-existent DLLs that still try to be loaded into applications. The detection of missing DLLs is critical because, if a privileged application tries to load a DLL that is missing, it might allow an attacker to escalate his privileges by supplying the missing DLL. More on that shortly.
As a result, we created a tool named DLLSpy that counters almost all DLL hijacking opportunities. The thing that distinguishes it from previous tools is the fact that it has three different ways of detecting DLL hijacking: dynamic, static and recursive.
DLLSpy is able to recursively follow all referenced DLLs by checking their binaries. In doing so, it finds a bunch of more indirectly used DLLs, thus widening the search for DLL hijacking.
Overview of DLLSpy
Let’s describe the three engines DLLSpy has under its belt.
Dynamic – First, DLLSpy scans the loaded modules by iterating the process loaded module list. Then it checks if any of those modules could be hijacked by trying to write to their file location on disk and then checking if they could be overwritten. This happens after the duplication of the access token for explorer.exe, which is a weak token. We do that in order to test whether we have write permission to the DLL location and the DLL itself as a regular user.
Static – DLLSpy Locates all strings that contain a DLL name or DLL Path in the binary files of running processes.
Recursive – DLLSpy statically scan all the DLLs of the processes previously examined. The goal is to find more DLLs loaded by the first set of DLLs and see if they are vulnerable to hijacking.
Initial Testing – Dynamic Scan
When we first tested DLLSpy, it only had a dynamic engine and, even then, the test results were quite shocking. We discovered that multiple third-party applications and around 10 Microsoft applications were vulnerable to DLL hijacking. Among them were:
- Microsoft Office Suite
Most of the programs are vulnerable because of one particular DLL named FileSyncShell.dll and its 64 bit version, FileSyncShell64.dll. This DLL file is located at C:\Users\user\AppData\Local\Microsoft\OneDrive\version-number\, which is an odd location, especially for DLLs used globally. Basically, any attacker could inject code into a bunch of programs by hijacking the FileSyncShell.dll.
At the moment, we have only verified that this exploitation works on Windows 10 (all versions) on a fresh install. We succeeded in hijacking this particular DLL, because OneDrive is installed by default.
For example, FileSyncShell.dll is always loaded right from startup by Explorer.exe (Figure 1). An attacker who successfully hijacks this DLL gains code injection and persistency. There is no need to perform a more complicated noisy injection technique, which has a higher probability of being detected, only to hijack a DLL that is not even critical to the applications who uses it.
A short list of several other third-party applications that load FileSyncShell.dll:
- Google Chrome
- Ida Pro
- Notepad ++
- Tortoise SVN
In addition, it seems that almost all major software products are vulnerable to DLL hijacking including:
- Google Chrome
- Spotify client
- Slack client
- WhatsApp client
Specifically, it seems that almost all the DLLs used by OneDrive.exe (Figure 2) and Teams.exe are vulnerable to hijacking. Windows doesn’t check if they are digitally signed, even though they are.
After the relative success of the first engine we thought to ourselves, “All those hijacking opportunities are nice, but can we get more? We want to be able to elevate our privileges.” This is where the static engine comes into play, which provides a next level of detection.
We ran a static scan with DLLSpy and found references to DLL strings in the binary from the time Visual Studio’s debugger was activated. The output was interesting. It showed us that the process Microsoft.VsHub.Server.HttpHostx64.exe tries to load a DLL named Microsoft.VisualStudio.CodeMarkers.dll that doesn’t exist on its DLL search and loading order.
Microsoft.VsHub.Server.HttpHostx64.exe is an executable that is executed by VsHub.exe whenever you debug an application. This executable supports multi-tool communication across the VS family of apps.
Back to DLL hijacking. We verified that Microsoft.VsHub.Server.HttpHostx64.exe tries to load Microsoft.VisualStudio.CodeMarkers.dll by using Procmon. This executable does not try to securely load the DLL using a suitable API or by providing full path to the LoadLibrary API. So, we managed to hijack the DLL pretty easily.
Then, we got this:
For all of you developers, you’ve probably seen this type of error at least once. This error is invoked by the WerFault.exe process. WerFault.exe’s purpose is to report errors and crashes in applications.
In this case, the WerFault.exe process spawned to report an error in Microsoft.VsHub.Server.HttpHostx64.exe, the process whose DLL we hijacked. The crash (Figure 3) happened because we tried to write a file into the System32 folder in our malicious DLL.
Wait a moment. Why am I talking about WerFault.exe? It is perfectly normal for it to spawn because of my badly crafted DLL. We asked ourselves: should we do another test with DLLSpy’s static scan to see if WerFault.exe is vulnerable to DLL hijacking? So, we did. We discovered that WerFault.exe tries to load a DLL named ext.dll, which, if you understand what this post is all about, doesn’t exist. Mabey we could put it to a good use (Figure 4).
So, what is so special about WerFault.exe? Hijacking its DLL leads to UAC bypass. The error reporting process sometimes executes as a privileged process. It mostly happens when a privileged application – installers, services, critical processes – crashes. Now, whenever WerFault.exe is executed with admin privileges, and in the context of a privileged account, WerFault tries to load several DLLs named: ext.dll, uext.dll and exts.dll.
This is a new way to bypass UAC that adds to the ever growing number of ways to bypass the technology that protects an admin from auto-elevating its applications. While Microsoft does not consider UAC to be a boundary, it’s is still a hurdle that attackers often need to cross. We can see that the WerFault.exe process is elevated (Figure 5), which allows us to execute code inside the WerFault.exe process in the context of a privileged application.
We were able to discover a new way to bypass UAC. Still, we wanted to push it further and achieve privileges escalation. Hence, we thought to continue with the idea of detecting missing DLLs.
Our experience has shown that multiple binaries are vulnerable to DLL hijacking. Is it possible that loaded DLLs are loading additional DLLs that, in turn, could be hijacked? It is possible that developers pay less attention to those second in line DLLs, because they are not constantly tested.
The recursive engine extends the search of DLL hijacking by looking at modules that loaded by modules. In other words, process A’ loads a DLL called X. The module X loads a DLL called Y. This continues on and on until a specified level in the recursion is reached. In other words, a user can specify the depth of the recursion.
Let’s put it in practice. We executed a recursive scan with a level number of 3. We discovered a process named PoaService.exe, which loads a DLL named ati.dll. ati.dll tries to load a DLL named atiadlxx.dll. Unfortunately, this DLL doesn’t exist, which allows an attacker to escalate his privileges to local system by simply restarting the machine.
POAService.exe only present in Dell they patched it last year. Cisco discovered privilege escalation exploit CVE-2017-2802. Although we didn’t discover this particular CVE, it shows how effective DLLSpy is. By just doing some scans, we managed to find privilege escalation.
As far as I am concerned, on my Dell Precision 5510 (straight out the box) there aren’t any automatic Dell updates for this vulnerability. This means that you need to go directly to the Dell site and install an update manually, which is totally not cool. I highly suggest everyone who owns a Dell update manually.
After several executions of DLLSpy across multiple machines, we came upon several security holes of a similar nature, which we find quite confusing, because it is very easy to prevent these holes in the first place.
Windows API provides the ability to check if a DLL is digitally signed. In addition, you can check if a DLL’s location is in a protected folder. The fact that Microsoft doesn’t sanitize its own code – by checking for signed modules and not trying to load missing DLL – is troublesome indeed.
Software companies should protect their products better than this. All they have to do is sign self-made DLLs and check for a valid signature before loading DLLs. In addition, with suitable permission levels, you can specify a full path for the DLL and putt it in a protected folder.
You can find the DLLSpy GitHub repository here.
Microsoft Responses –
1. Regarding the vulnerability that allows a regular user to inject code into multiple applications, including Microsoft’s products and third-party software, by hijacking dll, Microsoft responded: “Microsoft has decided that it will not be fixing this vulnerability and we are closing this case. “
Microsoft does not think this vulnerability is worthy of a fix even though because of their bad design, the shell extension FileSyncShell.dll – which is located in an unprotected folder — could cause software that stores sensitive information to be vulnerable to undetected code injection.
- Nov 27,2018: Reported to Microsoft.
- Nov 29,2018: Microsoft opened a case.
- Dec 3, 2018: Microsoft decided to close the case.
2. Regarding the vulnerability that allows a regular user to hijack VsHub.Server.HttpHostx64.exe, which is activated whenever you debug in visual studio, Microsoft responded: “Thanks again for your report and the opportunity to review your findings. After further analysis, the team has decided that this isn’t something we will be fixing as part of our normal monthly security update release”.
- Dec 2, 2018: Reported to Microsoft.
- Dec 6, 2018: Microsoft opened a case.
- Dec 31, 2018: Microsoft acknowledged the vulnerability, but decided to close the case.
3. Regarding the vulnerability in WerFault.exe that allowed us to escalate our privileges, Microsoft initially said: “We haven’t determined a release date for the fix yet. I will let you know once I have that information”.
One week later, we received another email from Microsoft, which said: “We have determined that the report you sent us is something we treat as a ‘won’t fix,’ and we have said that publicly.”
Microsoft quoted a sentence from their blog: “The directories that are in the PATH environment variable are always admin ACLed and a normal user can’t modify contents of these directories.”
- Dec 12, 2018: Reported to Microsoft.
- Dec 17, 2018: Microsoft opened a case.
- Dec, 2018: Received a notification from Microsoft stating they managed to reproduce the bug.
Jan 9, 2019: Microsoft closed the case.All the applications mentioned above are still vulnerable to DLL hijacking on Windows 10’s latest build – 17763.253