Revisited: DLL Search Order
Recently I have been researching DLL Injection where the LoadLibrary API is utilized in order to load a DLL into the memory address space of a target process and eventually get executed. The tool I wrote  accepts the name of the process that is being targeted and the path of the DLL to be injected into this process.
My goal in this post is to demonstrate and describe the Dynamic Link Library search order as defined by Microsoft  using, this time, a random application for which we’ll monitor the backgroung.
Setting the scene and a step forward
Beginning from the tool I wrote, I pivoted to the LoadLibrary and the input this function accepts. Users can provide either an absolute path (during my tests I used the path: C:\Users\test\Source\Repos\calc_poc\x64\calc_poc.dll) or a plain filename (let’s say malware.dll). And there the action begins.
In order to see what’s going on in the background I downloaded a random x64 portable application and used it to inject a proof of concept DLL into. Slightly same procedure as the one I discuss in the DLL injection post. This randomly chosen application is Sumatra PDF version 3.2 .
Let’s inspect the procmon.exe ouput when we attempt to load a DLL with an absolute path:
Now see what happens if we provide a plain DLL filename (malware.dll) that actully doesn’t exist on the system (I have added numbers in red color next to each path so we can follow the article by Microsoft):
We observe that the resource is being looked up in a few paths. And this is where search order is being applied.
Windows Search Order
The documentation provided by Microsoft states that the DLL is looked up in locations with the following order when the SafeDllSearchMode option is enabled (enabled by default starting with Windows XP SP2):
- The directory from which the application loaded
- The system directory
- The 16-bit system directory
- The Windows directory
- The current directory
- The directories that are listed in the PATH environment variable
- the App Paths registry key
Search Order into practice
Next step is to break down this list and associate the list items with the actual path they correspond to and the colored numbers.
\1. The directory from which the application loaded -> C:\Users\test\Desktop\SumatraPDF-3.2-65\malware.dll, number in red 1 \2. The system directory -> C:\Windows\System32\malware.dll, number in red 2 \3. The 16-bit system directory -> C:\Windows\System\malware.dll, number in red 3 \4. The Windows directory -> C:\Windows\malware.dll, number in red 4
So far, so good. Number 5 is the current directory for the process. The current directory of a process is set up for example by the variable lpCurrentDirectory when using the CreateProcessA API. In this case, the current directory of the application seems to be the System directory.
\5. The current directory (?) -> C:\Windows\System32\malware.dll, number in red 5
On number 6 in the list we have the directories that are listed in the PATH environment variable. There are two environment variables on each system. One for the system profile and one for the user profile.
The following screenshot shows the system PATH:
Correlating the environment PATH with the Process Monitor output we observe that from number (in red) 6 to 13, the paths are queried.
The next screenshot shows the user PATH:
Again, correlating the two screenshots (from Process Explorer and PATH) we observe that numbers 14 and 15 are queried next.
What’s the take away here? That the system environment variable is checked first and then goes the environment variable for the user.
And by reaching to number 15, we reach the end of this topic.
In this post we observed the behavior LoadLibrary when it does and when it doesn’t get an absolute path as input. In cases in which LoadLibrary doesn’t get an absolute path, this is the prelude of a class of vulnerabilities called DLL Search Order Hijacking. In this type of vulnerabilities, attackers take advantage of the search order by replacing the queried DLL at an earlier stage of the query chain it gets executed instead of the legit one. The technique is documented in the MITRE ATT&CK framework as T1038.