DLL Hijacking - a simple technique that makes an attack more stealthy; understand it

Written by: Alexandre Siviero

The recent REvil ransomware attack via the Kaseya infrastructure is the latest in a series of attacks that have incorporated a technique known as DLL Side-Loading. According to the MITRE ATT&CK framework, this is a technique for hijacking the execution flow of a process (which is why it is also called DLL hijacking). Its application is simple, even more so considering other DLL exploits such as reflected injection, and it gives the attack greater stealth. To understand it, you first need to understand what DLLs are and how they are loaded by a file.

Dynamic-Link Libraries

According to Microsoft's documentation, a DLL is a module that contains functions and data that can be used by other modules. To put it even more simply, it's an executable just like a PE, but it doesn't run on its own. Its purpose is to bring modular operation to applications, for example by offering native Windows functions through APIs so that programmers don't have to reinvent the wheel. If your program uses TCP/IP communication, for example, you don't need to program all that; just import the DLL that has the APIs you need (such as using sockets for network connections) and move on.

The term dynamic-link also suggests that there are other possible types of links for applications: static-link and runtime-link.

Static-link

It consists of including all the functions necessary for an application to work within its code. No importing is necessary during execution, but this results in compiled software that is larger in size and more difficult to update and adjust. Why? Because instead of fixing just one auxiliary module that your program uses, you have to go through the source code and recompile for any changes.

Dynamic-link

DLLs are imported into the memory of a process during its initialization. This means that the executable on disk lists all the corresponding modules and functions in an import table. During the operations that transfer an executable to memory and turn it into a process, Windows will read this table, look up the code for the functions requested in the associated DLLs and copy it into that process's memory. It is this import table that software that analyzes executables reads.

Runtime-link

An alternative to dynamic-linking, or importing DLLs and functions during process initialization, is to do it while the application is running (hence the term runtime). There are legitimate uses for this approach, such as not spending time or memory importing functions that will rarely be used by the process - think of it as an "on-demand" import model. Malware authors are very fond of this type of import for another reason: to obscure the functionality of their malicious software. When you know enough about system DLLs, reading the import table of an executable on disk gives you a lot of information about what that program will do when it runs. Nobody puts DLLs in for nothing, so if a piece of software imports something to interact with the Windows registry, it will interact with the Windows registry.

To prevent this kind of simple static analysis from showing what the malware is trying to do, the authors only import DLLs that are strictly necessary for the process to work via dynamic-link. All the really interesting functions will be enumerated and imported during execution.

DLL Hijacking

Okay, we've talked about importing countless times, but so far without explaining how it actually works. Earlier we mentioned strictly necessary DLLs; one of them is Kernel32.dll, which provides a series of APIs used to correctly initialize a process, among them LoadLibraryA and LoadLibraryEx. Both are used to import a DLL, both in dynamic-link cases and in runtime-link cases (the differences between the two APIs are small and irrelevant to the topic covered here).

Both APIs have lpLibFileName as a parameter, which can provide a PE (.exe) or a DLL (.dll) to be loaded into the process. The file name passed to this parameter may or may not contain its full path (location on disk). If it does, the target file will be read from the directory provided and mapped to memory. But what if you only provide the file name, without its location?

In these cases, Windows will search for the file on disk in a predetermined order, which is available in Microsoft's documentation. Safe Dll Search Mode is enabled by default and affects the order in which the executable to be imported will be searched. With it enabled, the order is:

  1. The directory from which the application was launched
  2. The system directory (C:\Windows\System32)
  3. The 16-bit version of the system directory
  4. The Windows directory (C:\Windows\)
  5. The current directory

With Safe Dll Search Mode disabled, the order changes to:

  1. The directory from which the application was launched
  2. The current directory
  3. The system directory (C:\Windows\System32)
  4. The 16-bit version of the system directory
  5. The Windows directory (C:\Windows\)

PS: I've omitted the 6th entry from the list for the sake of brevity and because it's irrelevant to the technique we're going to discuss, but the full list is available at the link to the documentation provided in the previous paragraph.

"By the difference between the two, I bet DLL hijacking uses the current directory!". Good guess, but no. In fact, for this technique, the state of the safe search is completely irrelevant. The exploit takes place in the very first place searched, the directory from which the application was launched.

In the specific case of REvil, Sophos has shown how the dropper of this attack saves an old (and legitimate) Microsoft Defender binary on disk and uses it as a hijacking DLL target. Below, note that normally the legitimate MpSvc DLL (selected) resides in the same directory as MsMpEng.exe, one of the Defender binaries:

 

So, following the search order for DLLs imported without a full disk path, it's in the first place Windows would look. Taking advantage of this, the group simply named their malicious DLL MpSvc.dll and saved it in the same directory as the old Defender binary.

Why do it this way instead of writing malicious software and running it as a process? Because Defender running on a machine attracts less attention.

Stealthy, but how much?

Any technique that deviates from the operating system's normal behavior will leave traces, even stealthy ones. You can be discreet on one front, but end up being noisy on another. In the case of REvil, the old version of Windows Defender (msmpeng.exe) used as a vehicle for side-loading a malicious DLL is saved in C:\windows\msmpeng.exe. This is the first clue that something isn't right: the Defender executable has always resided inside the "Program Files" directory, in the Windows Defender folder. In Windows 10, this directory migrated to ProgramData; although different, neither path had msmpeng.exe saved directly in the Windows directory.

Defender's parent process would also be cause for suspicion. The dropper used by the group was saved in a Kaseya folder (C:\KWORKING\AGENT.EXE), so the process tree would show that Defender was started by... a Kaseya executable? It doesn't make any sense.

Of course, there are many nuances to detecting such an attack in real time. A forensic analysis would trivially show suspicious behavior, but in the case of ransomware, analyzing what happened after the attack means that the damage has already been done. To add even more fuel to the fire, Kaseya itself recommended that its customers check their applications' directories against their machines' antivirus software. To be as clear as possible: DLL hijacking/side loading is easy to understand; it doesn't mean it's equally easy to detect and/or avoid.

Bibliography:

https://docs.microsoft.com/en-us/troubleshoot/windows-client/deployment/dynamic-link-library

https://news.sophos.com/en-us/2021/07/04/independence-day-revil-uses-supply-chain-exploit-to-attack-hundreds-of-businesses/

https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#standard-search-order-for-desktop-applications

https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa

https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya

https://github.com/sophoslabs/IoCs/blob/master/Ransomware-REvil-Kaseya.csv

https://attack.mitre.org/techniques/T1574/002/

https://news.thewindowsclub.com/new-path-windows-defender-installation-windows-10-91061/

https://www.ghacks.net/2017/12/18/microsoft-changes-windows-defender-path-on-windows-10/