Secure loading of libraries to prevent DLL preloading attacks Guidance for software developers ©2010 Microsoft Corporation switech@microsoft.com The information contained herein is provided "as is" without warranty of any kind. Microsoft disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Executive summary When an application dynamically loads a dynamic link library (DLL) without specifying a fully qualified path name, Windows tries to locate the DLL by searching a well-defined set of directories. If an attacker gains control of one of the directories, they can force the application to load a malicious copy of the DLL instead of the DLL that it was expecting. These attacks are referred to as “DLL preloading attacks” and are common to all operating systems which support dynamically loading shared DLL libraries. The impact of such attacks could be that an attacker has the ability to execute code in the context of the user running the application. When the application is being run as Administrator, this might lead to a local elevation of privilege. Microsoft has been made aware of renewed interest in these attacks. In order to limit the impact this issue has on our mutual customers, Microsoft is releasing this document to the developer community to ensure that they are aware of this issue and have the necessary tools to address the issue in their applications. Description of DLL preloading attacks LoadLibrary Based Attacks When an application dynamically loads a DLL without specifying a fully qualified path name, Windows tries to locate this DLL by linearly searching through a well-defined set of directories, known as DLL Search Order. If Windows locates the DLL within the DLL Search Order it will load that DLL. However, if Windows does not find the DLL in any of the directories in the DLL Search Order, it will return a failure to the DLL load operation. The DLL Search Order for the LoadLibrary and LoadLibraryEx functions which are used to dynamically load DLLs is: 1. 2. 3. 4. 5. 6. The directory from which the application loaded The system directory The 16-bit system directory The Windows directory The current working directory (CWD) The directories that are listed in the PATH environment variable Now imagine the following set of events: An application loads a DLL without specifying a fully qualified path that it expects to find in the CWD of the application The application is fully prepared to handle the case when it doesn’t find the DLL The attacker knows this information about the application and controls the CWD The attacker copies their own specially crafted version of the DLL in the CWD (assuming the attacker has permission to do so) Windows searches through the directories in the DLL Search Order and finds the DLL in the CWD of the application At this point, the specially crafted DLL will run within the application and gain the privileges of the current user Recommendation To prevent this attack, applications can remove the current working directory (CWD) from the DLL search path by calling the SetDllDirectory API with an empty string (“”). If an application depends on loading a DLL from the current directory, please obtain the current working directory and use that to pass in a fully qualified path to LoadLibrary. Microsoft is also aware that some developers use LoadLibrary to validate whether a specific DLL is present, in order to ascertain which version of Windows is being run by the user. It is important to note that this could render the application vulnerable. If the affected library indeed does not exist on the Windows release the application is executed on, an attacker could introduce a library with that same name into CWD. Microsoft recommends strongly against using this technique. Instead, use the recommended techniques described in MSDN article, Getting the System Version. An application that loads third-party plugins and cannot force the plugins to use a qualified path for its LoadLibrary calls should call SetDllDirectory(“”) to remove CWD and then call SetDllDirectory(“plugin install location”) to add the plugin install directory to the DLL search path. SearchPath Based Attacks A similar attack exists when an application uses the SearchPath API to locate a DLL and dynamically load the path returned by SearchPath. The default search order for the SearchPath API is: The directory from which the application loaded The current working directory (CWD) The system directory The 16-bit system directory The Windows directory The directories that are listed in the PATH environment variable This is not a secure pattern and is not recommended by Microsoft. The SearchPath function is not recommended as a method of locating a .dll file if the intended use of the output is in a call to the LoadLibrary function. This can result in locating the wrong .dll file because the search order of the SearchPath function differs from the search order used by the LoadLibrary function. If you need to locate and load a .dll file, use the LoadLibrary function. ShellExecute and CreateProcess Variations of these issues can also exist when developers call similar functions such as ShellExecute and CreateProcess to load external executables. Microsoft recommends that developers be careful when loading binaries and specify the fully qualified path name. This should pose less complexity when loading a binary as opposed to a library. Recommended steps for software developers Microsoft recommends that developers: Validate their applications for instances of insecure library loads (examples of each are given later in this paper), including: o The use of SearchPath to identify the location of a library or component; o The use of LoadLibrary to identify the version of the operating system. Use fully qualified paths for all calls to LoadLibrary, CreateProcess, and ShellExecute, where possible. Implement calls to SetDllDirectory with an empty string (“”) to remove the current working directory from the default DLL search order where appropriate. Note that SetDllDirectory affects the entire process. Hence, this should be done once early in process initialization, not before and after calls to LoadLibrary. Because SetDllDirectory affects the entire process, multiple threads calling SetDllDirectory with different values could cause undefined behavior. In addition, if the process is designed to load 3rd party DLLs, then testing will be needed to determine if making a process-wide setting will cause incompatibilities. A known issue here is that when an application depends on Visual Basic for Applications, making a process-wide setting may cause incompatibilities. Use SetSearchPathMode to enable safe process search mode for the process. This moves the current working directory to the last place in the SearchPath search list for the duration of the lifetime of the process. Using SearchPath to check for the existence of a DLL without specifying a fully qualified path is not recommended even if safe search mode is enabled, as it can still lead to DLL Preloading attacks. Guidance on identifying insecure library loads In source code, the following are examples of insecure library loads: DWORD retval = SearchPath(NULL, "schannel", ".dll", err, result, NULL); HMODULE handle = LoadLibrary(result); In the above code snippet, the application will search for “schannel.dll” using the least secure search path. If an attacker has the ability to place schannel.dll in CWD, it will load even before the application searches the Windows directories for the appropriate library. HMODULE handle = LoadLibrary("schannel.dll"); In this code snippet, the application will attempt to load the library from the various application and operating system locations described in the beginning of this document for the LoadLibrary() call. If there is any risk that the file is not present, the application may attempt to load the file from the current working directory. This scenario is slightly less dangerous than the one above, but still exposes the application user to risk if the environment is not completely predictable. The following are examples of better, more secure library loads: HMODULE handle = LoadLibrary("c:\\windows\\system32\\schannel.dll"); In this snippet, the library is loaded directly using a fully qualified path. There is no risk of the attacker introducing malicious code unless he already has write permissions to the application’s target directory. For information on the best ways to determine the system directory, please see the following resources: 1. GetSystemDirectory 2. SHGetKnownFolderPath - SetDllDirectory (""); HMODULE handle = LoadLibrary("schannel.dll"); In this snippet, the current working directory is removed from the search path prior to calling LoadLibrary. This reduces the risk significantly, as the attacker would need to control either the application directory, the windows directory, or any directories specified in the user’s path in order to leverage a DLL preloading attack. SetSearchPathMode (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT ); HMODULE handle = LoadLibrary("schannel.dll"); On all systems that have installed security update MS09-014, this would permanently move CWD to the very last spot in the search order. Any subsequent calls to the SetSearchPathMode function from within that process that attempt to change the search mode will fail. Using Process Monitor to dynamically detect insecure loads Microsoft publishes a tool called Process Monitor, which allows developers and administrators to closely track the behavior of a running process. Process Monitor can be used to dynamically detect whether one of your applications may be vulnerable to this type of issue. Download Process Monitor from the following location: http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx Attempt to start your application with CWD set to a specific directory, for instance by double-clicking a file with an extension whose file handler is assigned to your application. Set up Process Monitor with the following filters: In the event of a vulnerable path being hit, you’ll see something similar to the following: The call to the remote file share to load a DLL indicates that this is a vulnerable program. Additional resources Dynamic Link Library Search Order http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx MSDN documentation on the SearchPath function http://msdn.microsoft.com/en-us/library/aa365527(VS.85).aspx MSDN documentation on the LoadLibrary function http://msdn.microsoft.com/en-us/library/ms684175(VS.85).aspx MSDN documentation on the SetDllDirectory function http://msdn.microsoft.com/en-us/library/ms686203(VS.85).aspx MSDN documentation on the SetSearchPathMode function http://msdn.microsoft.com/en-us/library/dd266735(VS.85).aspx Blog post by David Leblanc, Principal Security Engineer with Microsoft Office http://blogs.msdn.com/b/david_leblanc/archive/2008/02/20/dll-preloading-attacks.aspx Blog post by Andrew Roths, MSRC Engineering team on DLL preloading attacks http://blogs.technet.com/b/srd/archive/2009/04/14/ms09-014-addressing-the-safari-carpet-bombvulnerability.aspx