What is DLL hijacking?
DLL hijacking is an adversarial technique for exploiting trusted applications in order to load malicious code. There are many more advanced techniques than what I will display here such as stack walking, export table cloning, and run time table reconstruction. To learn more about those, check out this blog. This technique is the quickest I've found for getting a payload included into a trusted application for use at run time.
Checking what DLL's an application uses
For this we can use Procmon - a tool from Windows. It's perfect for our use case.
We want to filter for only DLLs that aren't being loaded from C:\Windows or C:\Program Files and that are from user-land accessible folders, and filter out everything else.
Once we have this setup and our results filtered, we can look for two things. An application not able to find a DLL, or an application successfully loading a DLL out of its own folder. I find the latter to be the easier method, as with the former you have to ensure that replacing the DLL with your own doesn't interrupt application flow, which is hard.
Hijacking the search order flow
When we find an application to target, we can attack the DLL search implementation. Windows searches for DLLs in a specific order, which is as follows:
If the module is not already loaded or on the list of known DLLs, the system searches these locations in this order:
- The package dependency graph of the process. This is the application's package plus any dependencies specified as
<Dependencies>section of the application's package manifest. Dependencies are searched in the order they appear in the manifest.
- The directory the calling process was loaded from.
- The system directory (%SystemRoot%\system32).
Knowing this helps us narrow down the potential applications to target. We want a DLL not already loaded into memory and one that doesn't appear in the package dependency graph. With this in mind, we begin searching. Below is a good example of NP++ not able to find a file it's looking for. It's searching the local folder to not avail.
DLL Side-loading is a lot less interrupting to the application in question. In the screenshot below, it can be seen that NP++ is loading a legitimate DLL it needs for updating. So let's mess with it and see what it takes to break.
I found the easiest way to do this was with a tool called DLLsideloader. This tool allows us to not interrupt the normal flow of the application, letting it continue working as intended while also loading our payload. To use it, there's two commands:
Invoke-DLLSideLoad legit.dll payload.dll
Copying the DLL that we found, libcurl.dll, into the same folder as DLLSideloader, we launch the script and let it run. This then outputs two files, a new "legit.dll" file and a temp file. In my case, these are libcurl.dll and tmpD1F3.dll.
You'll notice the new libcurl.dll is smaller than the real version, libcurl.dll.bak, and that's because this file contains a pointer to our payload as well as a pointer to the temp file created. When libcurl.dll is ran, the payload is executed and then the flow passes onto the legitimate dll which has been renamed to the temp file. This way, there should be no service interruption. We now want to copy all three files above over to the folder where we took the original DLL out of. Once done, if we look back at Procmon and close and reopen NP++, we can see that the file is being read and functionality hasn't been impacted.