DLL Sideloading: Making Legitimate Software Do Our Bidding

Nathan Smith
June 24, 2023

Intro

So, you have decided to click on this article to better understand what exactly DLL Sideloading is. Welcome! This article is meant to include more technical details than a standard blogpost or general synopsis. If you wish to have a more general overview, I included a TL;DR below.

In this article, I will explain to you what is the art that is DLL Sideloading. I will use other techniques in the same family of DLL Hijacking to help you understand the impact and alternative uses for this attack. This specific attack does target Windows systems and for this post, a Windows 11 Professional instance. I included a relatively small background section on some concepts that are important to understand prior to diving into the article. If you already are familiar with these concepts, feel free to move past the background section.

Thank you for reading and enjoy.

Background

The concepts in this section will be important to better understand the concepts in this article. If you do not already have a firm grasp on the concepts, it is recommended to at least read through this section to better understand some of the small intricacies that occur.

Dynamic Link Library (DLL)

Main Usage for DLLs

Many code bases will not always include all the functionality for the main portion of the code in the “main” file itself. For managed and non-managed code such as .NET and C++ respectively, a developer may off-load some of that functionality to a separate file called a library. When the library is compiled in Linux, the code gets compiled down to a Shared Object (SO) file, but for Windows, it is compiled down to a DLL file.

At a high level, DLLs serve as a separate place to reference when the main program needs to call a specific function. This makes code much more portable when there are multiple uses for the same code; just reference the DLL file and boom, you got your function. DLLs support load-time dynamic linking and run-time dynamic linking. More simply put:

  • Load-time Dynamic Linking: Main program is linked with the DLL on compile time. Calls to functions in DLL are called just like calling any other function normally. Ex: int x = superCoolFunction(foo, bar);
  • Run-time Dynamic Linking: Main program makes use of library loading functions such as LoadLibrary or LoadLibraryEx to load in the DLL and uses a handle to reference the DLL. Ex: HINSTANCE meme = LoadLibrary(“MyMemes.dll”);.

There are various reasons why to do one or the other, but the main point is to understand, the code being executed is in a separate file! It is important to note, Run-Time dynamic linking will not immediately cause your program to fail to start whereas Load-Time linking will cause the program fail to start if the DLL was to fail when loading.

For Run-Time and Load-Time dynamic linking, the executable knows what the path is to the specific DLL but sometimes there are assumptions made. Such assumptions as:

  • The DLL is always in the same directory as my executable.
  • The DLL will only exist when developing, otherwise I will just continue execution and not worry about loading it.

These will be important to us later when understanding why these assumptions are dangerous.

What Are Exports?

In order to understand what functions a DLL has for a program to use, DLLs have this cool hip table called the Exports table. This contains the name of every function that a program could possibly link against or call externally into the DLL file. External executables are pretty much blind to all other functions within the DLL. Think of it similar to private versus public classing in Object Oriented Programming.

DLL Entry Points

DLLs are normally just “dumb” files that hold functions. They are quite mild in comparison to the chad executable file. In order to make the DLL much cooler, you can also add a DLL Entry Point. This is an optional function which can be put into the DLL in the event the developer wants more control over what happens when a process or thread attaches/detaches themselves to the DLL. This is key because we can fire off a set of events to occur on the loading of a DLL without requiring a thread or process to call a malicious function (or any function in general).

Key Points

  • DLLs are usually linked in at load-time or run-time.
  • People make assumptions about the path to a specific DLL.
  • Entry-points are cool if you want things to happen on DLL loading.
  • The DLL will use the stack of the calling thread or process. Meaning if the parent process dies, anything created process wise in the DLL will die as well.

These TL;DR explanations are explained here: https://learn.microsoft.com/en-us/troubleshoot/windows-client/deployment/dynamic-link-library . This reference also has a well-documented section on the general structure of the DLL entry point. I would recommend this to understand the different calling points within a DLL.

DLL Search Order

This is separate from DLLs only because it is key to most DLL vulnerabilities that are being found today.

DLL Search order simply put, is the order of directories to search for a specific DLL resource when the absolute path for the library is not immediately known. Let’s walk through the loading of a DLL. For this I will assume SafeDLLSearchMode is enabled, though a lot of the time it is not (more on that in a second):

  1. Check for DLLs already loaded in memory (if the process is already running). If it is not running, just use that DLL instead.
  2. Check if the DLL is in the Known DLLs registry key (from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs)
  3. Search in the directory from which the application is loaded.
  4. Check the C:\Windows\System32 directory
  5. Check the C:\Windows\System directory.
  6. Check the C:\Windows directory.
  7. Check the current directory of the user calling the executable.
  8. Check the directories that are in the PATH environment variable.

When SafeDLLSearchMode is not enabled, it instead first checks the directory from which the application was loaded from then the current directory of the person calling the executable. This is super important for understanding the whole premise behind DLL Sideloading and DLL Hijacking.

Hijacking Execution Flow Using DLLs

Whoa!?! I thought we were talking about DLL Sideloading?

We are.

DLL Sideloading is one of a few different process-flow hijacking techniques using DLLs. It comes second to the good ol’ DLL Search Order Hijacking. To better understand how DLL sideloading works, I will first explain what DLL Search Order Hijacking is and to get a grasp of where it comes from, then dive into the meat of DLL Sideloading.

DLL Search Order Hijacking (SOH)

Remember that quirky thing I mentioned before called “DLL Search Order”? While protections such as SafeDLLSearchMode and DLL hashing exist, some developers may not know how to properly load their DLLs and sometimes suffer from not properly cleaning up development DLL references. The DLL search order is as follows (assuming the program is not already running):

  1. Check if the DLL is in the KnownDLLs registry key.
  2. Check if the DLL is in the directory the executable was started from.
  3. Check if the DLL is in the current working directory of the user calling the executable.
  4. Check the C:\Windows\System32 directory.
  5. Check the C:\Windows\System directory.
  6. Check the C:\Windows\ directory
  7. Check all the directories in the PATH environment variable.

It goes top down, searching for the DLL. If it does not exist, it fails to load the DLL. If it does exist, it will attempt to link the DLL to the program and upon success, the calling thread or process can use functions from the DLL. This order assumes that SafeDLLSearchMode is not enabled. Reference the former for when it is enabled and notice a similar process.

How Can Search Order be Abused?

If the developer who created a specific installer writes files critical to the execution of the program into a directory without proper permissions, an attacker can abuse the way an executable searches for DLLs. A few causes for DLL Search Order Hijacking include:

  • Improper write permissions on installation directory.
  • No verification of DLL loaded by program.
  • Dangling DLL references not cleaned up allowing DLL to be loaded from current executing directory of callee.

I would also like to add, it is not always because a developer improperly set permissions for an installation directory. Sometimes after a system has been compromised, an attacker might implant a malicious version of the DLL in one of the paths mentioned in the DLL search order. Meaning if an attacker were to replace a DLL inside the installation folder for a program, that executable will gladly load the DLL since it resides in the same directory as the executable. If an attacker were to put the DLL instead inside C:\Windows\System or C:\Windows\System32, then any executable that references the DLL will eventually search in one of those folders and notice it exists. Once it sees it, it loads it, then boom! Instant malicious DLL executable loaded into memory. This is more dangerous at times due to the amount of processes which use DLLs in the system folders may also run as the NT SYSTEM user. A.K.A, root in the Windows world.

Here are a couple of examples of where DLL SOH has been performed due to issues with permissions or proper loading of DLLs.

Notepad++ CVE-2022–32168

In September of 2022, a vulnerability was reported for the popular text editor Notepad++ that you could replace the vulnerable DLL UxTheme.dll with your own DLL and run arbitrary code in the context of Notepad++. The steps to reproduce were as follows:

  1. Compile a custom DLL source code file.
  2. Rename the compiled DLL file to UxTheme.dll and copy both notepad++.exe and UxTheme.dll files to a new folder.
  3. Run notepad++.exe and see the code execute.

Note: The executable, notepad++.exe will load the DLL from the directory the executable is ran from. In the search order, this ends up being the second step. This enables us to load a malicious DLL without needing to modify the original installation directory. This is not the original DLL Search Order Hijacking we discussed but more resembles the DLL Sideloading concept we are talking more so in a couple sections.

Read more on the original GitHub issue here: https://github.com/notepad-plus-plus/notepad-plus-plus/issues/12113 .

Microsoft Teams and Microsoft Onedrive (2021)

Back in late 2021, Microsoft Teams and Microsoft OneDrive had a SOH vulnerability. These applications had been installed to a user’s AppData\Local folder. Inside the folder held the Microsoft Teams and OneDrive folders where permissions on these folders were not set correctly to restrict average users from modifying the contents in the folder. This meant any ol’ user could place a malicious DLL in the directory where OneDrive or Teams was run from and those applications would load the DLL with the intent of using it.

Read more about this here: https://besteffortteam.it/onedrive-and-teams-dll-hijacking/ .

So…Sideloading Is Just DLL SOH?

Yeah, basically. It’s just a subset of SOH if you want to think of it that way. The Notepad++ CVE mentioned above is a great example of this specific subset of DLL SOH. To more plainly put it all together:

  • DLL Hijacking is a class of vulnerabilities where program execution flow is hijacked using a DLL.
  • DLL Search Order Hijacking (SOH) is a subset of DLL Hijacking which abuses an executable that relies on the search order path to give it the location of the proper DLL to load, thus allowing an attacker to place a DLL somewhere in that path and make that executable load their malicious code.
  • DLL Sideloading is taking advantage of the subset of DLL SOH which targets executables that attempt to load DLLs from the directory the executable was invoked from. This abuses an executable that assumes the DLL it will need will always be where it is executed from to load a malicious DLL.

Sideloading vulnerabilities allow an attacker to move the binary to a user writable location where their malicious DLL is also located and execute it. The copied-over executable ends up loading the malicious DLL. This is due to the search order failing us and the developer failing to realize the unprotected search order of DLLs is insecure by default.

How is a DLL Sideloading Attack Performed

Okay, so enough with the theoreticals, let’s discuss how DLL Sideloading is performed. It is not super complicated which is what makes this technique super slick.

Picking a Target

With a system as complex as Windows, there are many, and I mean many, executables to choose from. Some of which already exist in your system without having to install extra software to take advantage of DLL sideloading. With that being said, let’s go through the process of identifying if an executable is vulnerable to DLL sideloading. For this demo, I am going to select ARP.exe from the C:\Windows\System32 folder. This is nothing special, just one I had lying around in that folder (and you likely do too). If we end up determining it is not vulnerable, we can always just move to another executable we find. Remember, for this technique, I am assuming we have a writable location in the system where we can place this executable as opposed to testing for any opportunities in the installation directory.

Sideloading ARP.exe

For this section, I am going to make use of ol’ tried and true, Process Monitor.

For those of you who have never heard of Process Monitor, worry not. There are a lot of bells and whistles of this tool but we will only use it for a subset of those capabilities. All the information that is pouring out on the screen before you are various logs that Process Monitor has logged for different events which occur in your system. An event can be defined by the different operations a process may perform such as file reading, file writing, Registry key querying, and file locking to name a few.

We want to make use of process monitor to help us identify when there is potential to misuse the way an executable loads a DLL. Process monitor can help us identify when a DLL is being loaded from the current executing directory (CED) of the process. Remember, we want to find an executable that will attempt loading from the CED at some point since this allows for DLL sideloading when the malicious DLL is in the same folder as the executable itself. If we isolate a target executable into its own folder, we can use Process Monitor to target the process spawned from the executable and identify if it fails to load a DLL from its current executing directory. If we see this event, we know it is possible to perform DLL sideloading.

1. Setting up Our Target and Process Monitor

First, you need to copy over the ARP.exe file from the C:\Windows\System32 folder into a user writable folder. I have made a folder called test_folder and inside I have copied over the ARP.exe file:

Before we execute the binary, let’s start up Process Monitor. The program will show a lot of events:

Don’t panic, this is normal. Process Monitor lists all events from all processes that are performing different operations on your machine. It shows first the time that the event happened, the process which performed that action (and its PID), the operation which was performed, the path to the item the process was attempting to read, write, query, lock, etc., the result of the operation, then the details of the operation performed. The most useful thing on your screen is the filter button (it may look different in older versions of ProcMon):

The filter in Process Monitor is the most helpful for weeding out all these unnecessary events we don’t care about. It is pretty simple to add a filter, first select from the drop-down which field you would like to filter on, then the next field is a Boolean comparison to check what you enter in the next text box. Then at the end, a decision is made to keep or to discard the event. We can already see some events in Process Monitor which are so it will filter itself out and some other system processes and files that are negligible.

In Process Monitor, when a file a process is looking for, whether to read, write, or use as a DLL, it comes up as the result of NAME NOT FOUND. We can also use the fact a DLL always should end in .dll if you are in Windows land. With these two facts, we can make two filter rules to find at least processes who cannot find their DLL files. That looks like this:

Which we then end up seeing something like this:

This gives us a lot of interesting findings. The screenshot only shows powershell.exe but the list goes on in the events found. Are they all DLL sideloadable? Maybe. This may give more insight into what potential executables we could perform DLL hijacking on if those paths are not secure.

There is still one issue

We need to be able to find our specific event when we launch it. So let’s add one more event in that allows us to filter on the process name:

You may see some previous logs from something else that Process Monitor caught, but we can clear the logs it has collected so far to allow us to start off on a fresh new plate of logs. To do this, just perform CTRL + x or click the trashcan icon at the top next to the filter button.

2. Executing Our Process and Watching

Once we execute the ARP.exe file from the folder, we will notice two things:

  • ARP.exe did not successfully come up with any text on our terminal (when executing it from powershell within the test_folder I created)
  • There are two CreateFile events which populate on the PATH field of Procmon!

Both seem to include the path I had ran the executable from. Doing some quick testing to see if maybe it is based on the current executing directory will lead us to similar results. With that being said, we can assume at some level that these two DLLs are

  1. Both used by ARP
  2. We can impersonate one of the DLLs and place a fake version of the DLL into the folder to check and see if it is correctly invoked.

3. Testing With Fake DLL

For compilation, using Visual Studio is cool but is a pain if you don’t already have it setup. For me, I like to use Linux so that is what we are going to do! #ubuntu4lyfe Here is some simple test DLL code I will be using:

In Linux, you should be able to compile this using the Mingw cross compiler

Then to compile, it is pretty simple

Note, the DLL we are compiling needs to be the same Instruction Set Architecture ISA as the DLL we are impersonating as well as the executable. For example, if you have a 32-bit binary, the DLL must also be 32-bit, or in more proper phrasing, x86 instead of x86_64 (that is the 64-bit version of the x86 ISA).

After copying the DLL over to our Windows machine, it is as simple as dropping it into the same folder as ARP.exe , renaming it to one of the DLLs and hitting execute! Let’s see what happens…

This seemed to fail at trying to find a specific function entry point within the snpapi.dll. Remember, DLLs are not just carriers for your payload, they serve a legitimate purpose of offering functions that executables can call. Those functions are known via Exporting (see the section above on DLLs). Our DLL code does not export anything. That means if the application is going to attempt to call a function in the library as opposed to just linking the library, we will need to also export some functions for our executable to metaphorically “munch” on.

3.1 Fixing Our Fake DLL

We could use this error prompt to help us figure out which functions the application is using and slowly adding them to our DLL, but that would take too long. Others who have gone digging for DLL hijacking opportunities have faced similar issues and have instead made use DLL Export Viewer . This is a super neat tool and is free to use.

While this is a neat tool, I also prefer to use a similar technique I used in my sideloadr tool. The same approach can be found in cocomelonc’s DLL hijacking article. This technique is making use of Python and the pefile library to identify the exports of a DLL. I find this a bit simpler and the fact that it is also more convenient since we can work the results somewhere else in the Python script versus outputting to a file then re-reading them into a file.

Pulling from cocomelonc’s article and from my sideloadr tool, we can create a small script to just show all the exports of a DLL file using python:

Which should give output something like:

Note that each function is now redefined to take no arguments. In reality, if the program were to attempt to call functions from this library, it would either silently fail or just kill the process if it did not return what it was expecting. For now, this is acceptable as we just want to see if this darn executable is DLL sideloadable. Make sure to then copy all these extern directives and paste them into the bottom of your DLL code. It would be something like this:

3.2 Perform The Sideload

Since C++ is cool with us doing inline functions, we are good to go. Otherwise we would just need to cleanup the formatting which also would not be too hard. Compiling is pretty simple, do the same as before:

Then after copying over and renaming the DLL, we can run the test again. To reiterate, I am running this via commandline and via just clicking on the binary in the same folder as the DLL we created. Let’s see what it does this time…

Spectacular! The message box was displayed which means we have successfully sideloaded a DLL for ARP.exe on Windows 11 Professional. Not a huge feat but still super cool nonetheless.

Notes on Sideloading

Here are some various notes to think about when performing sideloading or some DLL hijacking techniques in general. I have ran into a couple issues along the way.

You May Want Your Target to Stay Alive

If it was not obvious, we stayed in the main thread of the executable when it was loading up the DLL. All actions we performed were in the DLLMain function and the message box that popped up was a blocking operation. The window for ARP.exe did not close behind it when I ran the executable. In an ideal world, I may try to make a separate thread to handle the execution of my malicious code. Unfortunately, if the parent process dies, so does the thread that spawned the executable (unless you were able to jump to another process in that time).

Metasploit Meterpreter Needs to be Threaded

Why the above is important is due to an issue I ran into with Meterpreter. The Meterpreter shellcode MUST be in its own thread. This goes for if it is in the DLLMain thread, or the main thread of a process. That being said, in order to get a DLL to fire off a Meterpreter shellcode in memory, I need to run the code in a separate thread. This becomes an issue though if the parent process such as ARP.exe does not stay alive that long. Due to this, I need to select a different target that will stay alive on the machine while my DLL is firing off Meterpreter shells so I have enough time to pivot to another process before the parent thread dies.

Looking at the DLL code I have in sideloadr, it is pretty simple to make the DLL call your shellcode as a thread:

This DLL code will spawn a thread using the CreateThread() to execute the function meme() in the code above. Meme() will then get a handle to the current process using GetCurrentProcess() so it can first allocate a memory region in the parent process which is writable, executable, and readable. The shellcode is then written to this location and promptly executed from this thread we have created, calling the shellcode that now exists within the main process’s memory from within the DLL. This is quite similar if someone was to perform DLL injection however, this is being done from the DLL loaded by a process as opposed to another process attempting to inject into another process’s memory.

You do Not Need to Explicitly Define Exports in The C++ File

Exporting functions of a DLL are quite easy; writing multiple functions to define the exports we want to use in a crafted DLL is a little gross. Thankfully we can make use of a DEF file to define our exports that we wish to hint a DLL is supposed to be exporting. This avoids us needing to define multiple “extern C” directives in the C++ file. sideloadr and cocomelonc’s DLL hijacking article are a couple examples making use of this technique.

What this looks like is a file with all the exports listed out like so:

This then gives us the capability to put all our exports into a def file then just include it when we compile the application later with Mingw:

It is nothing super crazy extra but it keeps the exports nice and clean when you are making your code (if doing it by hand) instead of getting bombarded in your C++ file by 200 exports.

Analyzing and Detecting The Sideload

Performing the technique is super cool, but let’s take a small look at some of the ways we can notice the DLL sideloading is being performed.

Using Process Hacker

I am going to make use of Process Hacker which is like a cracked version of Process Explorer. You could use either and get similar results.

First I am going to filter for the process name ARP.exe

Note two things:

  • It is running under the user who executed it (meaning if the SYSTEM user started it, the process and DLLs would be launched under that same user).
  • We can see the executing path is not the same as the original ARP.exe executable (very important when threat hunting)

Then, viewing the properties of the process and viewing modules we notice the snmpapi.dll DLL is loaded however, when inspecting the DLL, we notice it is unverified:

Comparing this to the legitimate ARP.exe when executed (it’s PID 16524 in the picture below; it is purple because I had to suspend it).

We can see the DLL for the OG loaded ARP.exe for snmpapi.dll is verified by Microsoft Windows while ours was not. The DLL count in the modules between the two is different only because I did not let the real ARP.exe execute enough to resolve the runtime-linked DLLs therefore they are not linked yet.

Furthermore, Process Hacker allows us to also find where the location of this Unverified DLL is

Without manually keeping track of DLL hashes in your system, this technique will be one of the most effective ways for identifying potentially malicious DLLs loaded into memory. Some initial automation of finding the ugly duckling in a sea of DLLs for a process is just using Process Hacker or Process Explorer to self-identify all DLLs which are signature verified or unverified

This already is a big step so we do not need to step through each DLL to see whether or not it is a trusted DLL. If you are trying to move through the sea of different processes then for each process verify each of its DLLs, you may find yourself clicking around a lot in Process Explorer to do that more manually. I made a small Powershell script here to enumerate all processes currently running, then for each process enumerate their DLLs and attempt to verify their authenticode signature. This will allow us to see if a particular processes DLLs are signed.

An unsigned DLL is not uncommon as some developers do not invest in a verification signature for their application as it costs anywhere from $359 USD for an EV Code Signing set to $410 USD for a GlobalSign EV Code Signing. That being said, an unverified or Not trusted verification does not mean DLL sideloading has been performed however, this does help us clue in on applications such as ARP.exe which should have only signed DLLs and neighboring resources from Microsoft when bad, unsigned DLLs start being loaded into the application.

Upon executing the script we already see our “malicious” DLL has been picked up as being loaded into the ARP.exe process and it reveals it via showing us the path to the DLL as well:

Showing of the path will hopefully clue us in on whether or not it makes sense for this DLL to be where it is.

Using Sysmon to View Executed Programs

I keep mentioning the path being a very important key within DLL sideloading detection. Since we can forward what processes were executed on a system from Sysmon to a SIEM, we can make use of this logging utility and the power of a SIEM such as Splunk to perform small statistics for what processes were started and how often do they get started. What is important here is we do not just do the statistics based on process name but instead we do it based on the file location using the absolute path for the process.

This technique and a few more using Powershell + Sysmon to combad DLL Sideloading on Windows systems can be found here https://github.com/TactiKoolSec/SideLoadHunter with an accompanying article here going over the different techniques used to detect the events that signify DLL sideloading.

What pairs well with this technique is also having a list or understanding of what applications you have running on your system, where are they normally located, and when that list differs from the running processes and DLLs, you should alert on it. Keeping a sort of documented software bill of materials (SBOM) of your system(s) will help the most when trying to mitigate the attack surface of DLL Sideloading attacks.

Automated DLL Sideloading / Hijacking Projects

Here is a list of awesome tools that in varying capacities will allow you to automate DLL Sideloading and Hijacking vulnerabilities (whether discovery or actual performing the task of sideloading):

  • Sideloadr — Small Python tool to do DLL Sideloading (and consequently, other DLL attacks). By Pascal-0x90
  • DLLHijackTest — DLL and PowerShell script to assist with finding DLL hijacks. By slyd0g
  • HijackLibs — Project for tracking publicly disclosed DLL Hijacking opportunities. By Wietze (Would also like to shout out this writeup on his research related to this project here)
  • windows-dll-hijacking — Project for identifying executables and DLLs vulnerable to relative path DLL hijacking. By Wietze
  • DLLHSC — DLL Hijack SCanner a tool to assist with the discovery of suitable candidates for DLL Hijacking. By ctxis
  • DLLSpy — DLLSpy is a that detects DLL hijacking in running processes, services and in their binaries. By CyberArk
  • DLLirant — DLLirant is a tool to automatize the DLL Hijacking researches on a specified binary. By Sh0ckFR
  • WFH — Windows Feature Hunter is a PoC python script using friday to identify DLL Sideloading and COM hijacking Opportunities at scale. By ConsciousHacker

All serve their own purpose and have different reasons why you may want to use one solution vs another. A point to get across is the technique of fully evading EDR and other AV solutions is still somewhat on the person performing the DLL sideloading. This technique does not stop an EDR from detecting a malicious DLL though it will get around restrictions on what executables can be ran.

Preventing DLL Sideloading

For Developers

We could put a whole bunch here for tips and tricks, but Microsoft has done a swell job with many recommendations for developers who are looking to mitigate the DLL hijacking and sideloading attacks against their applications.

https://support.microsoft.com/en-us/topic/secure-loading-of-libraries-to-prevent-dll-preloading-attacks-d41303ec-0748-9211-f317-2edc819682e1

Some additional things to note:

  • .NET developers can use manifest to store the Hashes of their DLLs
  • Windows dev can use loadlibrary/getprocaddress combination so the specific address is hardcoded and the malicious DLL cannot be properly loaded.
  • General hashing of functions you want to use.

I would also like to link the remediation seen for the Notepad ++ vulnerability in our case studies above here with the changes implemented here. Notice the only difference in their code was one additional line which said “don’t look in any other directory but System32”. This constrains the DLLs which are loaded by a folder which can only be touched if the user is Administrator or SYSTEM.

For End Users

The methods for mitigation on the end user side is a little less verbose but with proper logging, a system SBOM or catalog of application installation locations, and a way to aggregate all this information will prove quite powerful. Some easy steps are:

  • Identify processes or DLLs which are not in C:\Windows\System, C:\Windows\System32, or the installation directory for a given executable. Microsoft has some good resources for what tracking this looks like here: https://msrc-blog.microsoft.com/2018/04/04/triaging-a-dll-planting-vulnerability/ .
  • Perform DLL Whitelisting. Track the hashes of known DLLs used on systems so you can identify when a malicious one has been placed.
  • Enabling SafeDLLSearchMode in your registry to force all applications to use the safe search order for DLLs.
  • Validate loaded DLLs. Identify which ones do not have valid certificates and determine if they are malicious or not. White list the ones you deem benign so when a malicious DLL gets loaded, it can be easily identified.
  • Look for applications which are taking up higher than normal cycles of CPU usage, performing call outs to remote systems when it should not be (why is Notepad.exe calling out to AWS??), and spawning multiple children processes (this is just a general threat hunting red flag)
  • Using AppLocker to block DLLs from being executed from the current executing directory or a custom directory outside of a Secure search path. Also using allow-lists to prevent unsigned DLLs from loading with AppLocker

Conclusion

In the world of Windows systems there is so many different vectors for attack. In this article, we have addressed one of those attack vectors to help slowly reduce the attack surface you and your company may face on a day to day basis. DLL Hijacking attacks are no laughing matter and are consistently used by adversaries to help pose as persistence methods or ways to avert the eyes of EDR and antivirus software as well as the eyes of the active analyst.

As we draw to a close, remember, these threats can be detected and it matters that we as analysts are more vigilant than ever on threats which pose as legitimate programs we use every day. This topic itself is not new nor is it anything particularly revolutionary, though this article is a piece of the puzzle for reaching anyone who sees it so they are aware of this issue and can defend against. Here are some additional and amazing articles by fellow cybersecurity researchers out in the field on their takes of the DLL hijacking story and its subsidiaries such as DLL sideloading:

There are probably some more I missed but these resources are all quite helpful. Thank you for reading this article and please leave feedback if you have comments on the article both good or bad.

Subscribe to our blog

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Nathan Smith
January 21, 2024

Check out our other posts