Written by: Itamar Medyoni & Eran Yosef
How Dynamic-Link Libraries (DLLs) and the way the Windows API is instructed to use them may be utilized as an interface for arbitrary code execution and assist malicious actors to achieve their goal.
Dynamic Link Libraries are the products of Microsoft’s implementation of Shared Libraries.
These libraries usually have the file extension DLL, and they are also PE files, exactly like exe files.
DLLs may contain any type of content a PE file can contain, which may vary from code, resources, or data in any combination.
DLLs are mainly used to share this content between applications and processes on the system, in order to provide programmers with high flexibility when creating applications for Windows.
DLLs are executed in the memory of the calling process, with the same access permissions. This means that there is no protection for the calling EXE if the DLL contains any anomalies. Malicious attackers may exploit this fact by using methods such as DLL Hijacking or DLL Proxying to execute their malicious code.
In this article we provide elaborate information regarding the methods in which an attacker may exploit DLLs and the way that Windows loads them in order to damage a target.
Throughout our daily work, we load a vast amount of processes into our systems. When using Windows operating systems, a critical step of the process loading algorithm consists of loading Dynamic-Link Libraries (DLLs) into the memory to utilize their functions and fulfill the dependencies between their processes and the DLL. This operation occurs whenever a process is started.
Windows operating systems may contain a vast number of versions to the same DLL. Due to the fact that one system may host many processes that may require the same DLL, a system is in place to ensure that the required DLL is loaded from the correct path, while making sure that the most relevant version of that DLL is located.
The action of loading a specific library is utilized by application makers by using the LoadLibraryExA or LoadLibraryA functions. These functions receive a path argument which leads to the requested DLL and returns a handle to the module to the calling process.
Figure 1 – LoadLibraryA function in C++ (MSDN)
When declaring these function calls, the operating system starts looking for the requested DLL by reviewing a well-known set of locations in which the DLL could be found in:
DLLs contain code to be executed by the loading process, which creates a situation where a missing DLL or a DLL which is implemented in an unsecure method can be utilized in order to trick the running system to execute a malicious payload, and in doing so, it exploits the native DLL search order. This technique may be used by a malicious actor in order to load his own DLL, which may contain any type of code.
The following Registry keys change the way the loading order works, and as such may raise obstacles with whoever attempts to run this exploit:
• KnownDLLs – HKEY_LOCAL_MACHINE\SYSTEM \CurrentControlSet\Control\Session Manager \KnownDLLs
As the name implies, these are ‘known’ DLLs , and as such, each action, which aims to load a known DLL is redirected to the known path of the DLL and then skips the additional locations mentioned previously.
• Safe DLL Search – HKEY_LOCAL_MACHINE \System\CurrentControlSet\Control\Session Manager\SafeDLLSearchMode
SafeDLLSearchMode moves the current directory to the end of the search process to reduce the chances of this exploitation.
Having established that a process searches for a DLL in a certain search order, and that a process with a missing DLL/wrongly implemented DLL is vulnerable to such an exploit, the next step is to identify a process that contains these prerequisites.
First, we set up ProcMon from Sysinternals to filter for any operation in which a path that ends with DLL has not been found:
Figure 2 – Procmon filters to find vulnerable paths
When running several other executables on the host, “Bginfo64.exe” stands out with the amount of DLLs it did not find.
Figure 3 – A list of failed DLL loading attempts done by Bginfo64.exe
When investigating the purposes of these DLLs, the conclusion is that Riched32.DLL is a non-native DLL, and as such, there was no default search path for that DLL in the registry. Even with SafeDLLSearchMode on, the system will eventually load it if we configure it correctly.
All that is now required in order to demonstrate the vulnerability, is to create that DLL in the requested path:
Figure 4 – The “payload”
This DLL opens a Message Box with the prompt “Hello World”
Figure 5 – “Hello World”
The purpose of this DLL, is to open a message box containing the message “Hello World”.
Natively, the process “Bginfo64.exe” does not open such a message box, although when re-opening the process after the changes, the message box appears, and we can observe the loading of the DLL by the process:
Figure 6 – Message Box Loaded by Bginfo64.exe
Figure 7 – View of the loaded Modules within Bginfo64.exe which shows the previously not loaded “Riced32.dll”is now loaded.
The code contained in the DLL, has the potential to be any malicious payload!
Having identified the process, and the vulnerable path, all that is missing is to create the requested payload we wish to execute.
The Goal: Replace the DLL with a malicious one, and execute the process By using the tool made by J3wker called “DLLicious” (https://github.com/J3wker/DLLicous-MaliciousDLL), we are able to quickly compile a reverse shell script contained within a DLL:
Figure 8 – Reverse Shell Script by using PowerShell and NetCat contained within DLL source code
This code downloads an instance of NetCat, and attempts to contact the listener on the attacking machine and then open a shell Using PowerShell.
Once the hijacked process is executed, the DLL is loaded and the shell opens:
Figure 9 – Left to right – Bginfo64.exe loaded with the malicious Payload Riched32.dll, the acquired shell
We have demonstrated how the Windows Search order may be manipulated in order to load any DLL that you require.
As technology advances and big companies are aware of such vulnerabilities, processes that are vulnerable to such exploits still require validation of the required functions in order to fully load the DLLs.
Fortunately, or unfortunately, depends on whom you ask, this brick wall can also be bypassed using a technique called DLL Proxying, which in combination with File Dumping techniques may be a perfect set up for code execution and by that, privilege escalation.
We now introduce you to the basics of DLL Proxying and then we shall do our best to include all of these concepts in one attack in order to simulate a real-world scenario.
DLL Proxying is a technique in which an attacker loads a DLL, which masquerades as the DLL that is meant to be loaded by a process, and at the same time provides access to the functions from the original DLL when required, and hence the name “proxy”.
This Masqueraded DLL contains all the actions that the attacker wants to perform, while directing the running process to the original DLL in order to preserve the correct functionality of the process and the DLL.
The attacker in a scenario like this one is considered as the “Man in the Middle” between the application and the DLL.
The attack happens in the following order:
1. Analysis of the original DLL
2. Implementation of the linkage between the victim and the malicious DLL
3. Implementation of the attack/code the attacker wants to execute
4. Placing the DLL in a location that will be searched for by the process.
A malicious actor has been probing at a company environment for some time.
The actor has already gained initial access, was able to enumerate the running process, but encountered a dead end and did not access privileges which would have enabled him to proceed.
In an effort to gain access and harm the company, the attacker tries to hijack a DLL by utilizing the DLL search order and Proxying on Python.
Since the attacker failed to gain the required privileges, he then attempts to perform an Email Phishing campaign on the company, sending a malicious Microsoft Word document, weaponized with malicious macros, in order to perform this attack.
To learn more about Malicious Macros in office documents, refer to this article:
In order to abuse the existence of Python, we can launch a standard version of Python and monitor it with the previously shown ProcMon filters.
Figure 10 – Python failed to find VERSION.dll in the Python Directory
The following result is clear:
After choosing the victim DLL (VERSION.dll), we can use the “Get-Exports” module by FuzzySecurity/PowerShell-Suite (https://github.com/FuzzySecurity/PowerShell-Suite) in order to receive the full list of exports in C++ format, to link the malicious DLL to the victim DLL:
Figure 11- Using the GetExport Module in order to receive a list of exports in a linking comment in C++ format
Now, we implement this output in our DLL project:
Figure 12 – A C++ program, when compiled, acts as the Masqueraded DLL, which will proxy VERSION.dll and prompt the user with the message “Cynet is the Best!”
The above DLL proxies the original VERSION.dll which resides in System32 and launches a simple Message Box.
The content of the DLL has been translated to BASE64 and is saved on a PASTEBIN.
The purpose of that is to host the file and later download it through a string, using PowerShell.
Figure 13 – The DLL content, in Base64 and its translation
In order to download the content of this PASTEBIN, and save it as the DLL that we proxy, the following PowerShell script is created:
Figure 14 – This Script checks if Python exists, and if so, downloads the base64 encoded DLL and saves it to the Python directory.
This script has been implemented in this Proof of Concept, which is a VBA weaponized office document:
Figure 15 – The front content of the document, Social Engineering
First, the document applies a social engineering technique in order to trick the user to enable the macros.
The macros are:
Figure 16 – The Trojan code that executes the process
The code operates as follows:
Figure 17 – This diagram shows the full attack chain as performed by the Word document
After the user clicks the ENABLE MACROS button,
Figure 18 – The button that the user must click to run the macros
the following process tree is launched:
Figure 19 – The process tree of the attack
Figure 20 – The original way Python loads the VERSION.dll and the proxying performed by the attacker.
This is how the activity goes IRL:
Figure 21 – Video Link
When checking the process, you may observe that both the victim and the malicious DLLs have been loaded, we can therefore conclude that the attack has been successful and the victim DLL has been proxied:
Figure 22 – Loaded modules on the attacked process illustrate that the attack has succeeded
In order to defend your organization from these vulnerabilities, the following steps can be taken:
DLLs are very powerful. They are part of our daily life when using Windows, and they enable processes in our systems to be flexible and more lightweight.
DLL searching and loading mechanisms are vulnerable in the sense that a user may load whatever type of content a malicious adversary may attempt to load.
Furthermore, we have demonstrated how such an attack may occur, what steps are required to achieve Search Order Hijacking and DLL Proxying.
Additionally, we have demonstrated how an attacker may implement these methods in a more sophisticated type of attack and the other techniques he may incorporate to achieve his goal.
Finally, we have presented ways in which we may protect ourselves from such threats.
Malicious actors will always use any vulnerability they can find to achieve their goals and, as such, our beloved DLLs should always be considered as suspects!