Version 7 / September 29, 2011
We're maintaining a list of guidelines for developers who want to avoid introducing binary planting vulnerabilities into their products.
If you're a developer, please come back regularly or follow us on Twitter to stay updated on these best practices.
- Use absolute paths to libraries when calling LoadLibrary*. If the DLL is supposed to be in one of your application's folders, concatenate that folder's path with the DLL name. If you're loading a system DLL, e.g., dwmapi.dll, use functions GetWindowsDirectory and GetSystemDirectory to locate the appropriate directory and then pass a full path to the library to LoadLibrary. Alternatively, expand the string "%SystemRoot%\System32\dwmapi.dll" with function/method ExpandEnvironmentStrings and call LoadLibrary with the resulting string. This will all take more time but remember: falsely assuming that some DLL will always be found in a system folder when LoadLibrary traverses the search path will result in a binary planting vulnerability.
- Don't make "let's see if it's there" LoadLibrary* calls with relative paths. If you chose to ignore the previous guideline, at least don't just call LoadLibrary("somefeature.dll") to test whether some feature is available on the user's system. This will make your application binary planting-positive on all systems where said DLL is not present. You can safely check for a DLL this way if you use an absolute path to it.
- Use absolute paths to executables when calling CreateProcess*, ShellExecute*, WinExec, LoadModule, _spawn*p*, _exec*p* or other functions for loading libraries or launching executables. If the executable is supposed to be in one of your application's folders, concatenate that folder's path with the EXE name. If you're loading a system EXE, e.g., explorer.exe, use functions GetWindowsDirectory and GetSystemDirectory to locate the appropriate directory and then launch the executable with full path. Alternatively, expand the string "%SystemRoot%\explorer.exe" with function/method ExpandEnvironmentStrings and call CreateProcess (or another process-launching function/method) with the resulting string. This will all take more time but remember: Search paths for launching executables are even worse than the search path for loading DLLs and even if the executable you're trying to launch exists on the system, not providing a full path to it can easily result in a binary planting vulnerability. (See this blog post for details.)
- Don't plan on your application finding a DLL/EXE in CWD. It is sometimes easy to set the current working directory to a specific directory where your DLLs are so neatly stored, and let the search paths of LoadLibrary* or other functions make their magic. Even if this does not introduce a binary planting vulnerability, it will create a "CWD-addicted" application that will break when CWD is eventually removed from search paths.
- Don't plan on your application finding a DLL/EXE in PATH. Locations specified in the PATH variable are always searched for after the current working directory, therefore such implementation will always be vulnerable to binary planting. Instead, load DLLs and launch EXEs with absolute path - if you don't know where the DLL/EXE you want is, you're playing with fire. If you're setting user, system or application PATH in order for your application to find your binaries, you're most likely developing a binary planting-positive application and have to rethink your approach to finding your binaries.
- Set CWD to a safe location at startup. Since the current working directory is an important component of binary planting attacks, your application must set it to a safe location (we recommend %TEMP%, not your application's folder, since the latter can tempt you to try loading EXEs and DLLs from CWD, which you should not do!)
- Don't let "file browse" dialogs change CWD. When using Open and Save As dialogs, set the OFN_NOCHANGEDIR flag to prevent these dialogs from setting the current working directory to an unsafe location. If needed, set the initial location with lpstrInitialDir. Thankfully, default Common Item Dialogs have their FOS_NOCHANGEDIR flag set, but be careful to set it yourself if you override the defaults.
- Call SetDllDirectory("") at startup. Call SetDllDirectory("") as soon as you can in your application. If this breaks anything, whatever breaks is either vulnerable to binary planting or will break anyway when CWD is removed from search paths, so fix it there. Note, however, that there is a bug in Windows that sometimes leaves environment variables unresolved in user or system PATH, thereby effectively anulling the power of SetDllDirectory. Also, if some other application adds a relative location to user or system PATH, your DLLs may be loaded from that location, i.e. from current working directory, even if you have previously called SetDllDirectory. This function should therefore be used as basic hygiene and not as a silver bullet. (Update: SetDllDirectory seems to affect the way child processes load their implicitly linked DLLs; thorough testing is thus advised.)
- Consider using functions SetDefaultDllDirectories, AddDllDirectory and RemoveDllDirectory. On Windows 7, Windows Server 2008 R2, Windows Vista, and Windows Server 2008 computers with KB2533623 installed, your application can use functions SetDefaultDllDirectories, AddDllDirectory and RemoveDllDirectory to fine-tune the way DLLs are being searched for. These functions can arbitrarily limit the set of folders included in a DLL search.
- Don't use SearchPath for locating a DLL. Just don't.
- Check your assumptions if your DLL will be loaded by someone else. If you're developing a DLL (or any component, for that matter) that will or could be loaded by another application, be aware that this can change the initial CWD as well as the "home" directory (number one in the search path) and thus affect the way your DLL-loading and EXE-launching calls work.
- Expand environment variables when processing externally-obtained DLL/EXE paths. Whenever you get a path to a DLL/EXE from an external source (e.g., registry or configuration file), expect that it may contain environment strings. Put such paths through ExpandEnvironmentStrings (it should cause no problems even if there aren't any environment strings in it) before using them in DLL/EXE loading functions.
- Do not register in-process COM servers with relative paths. While side-by-side COM components may seem useful for some situations, it is a bad idea to register an in-process COM server with a relative path. The reasons are described in this blog post.
- Be careful when developing for different OSs, languages. Don't assume that some DLL/EXE you need will be present on all users' computers, especially if your application will be installed on different OS versions in different parts of the world. Every missing DLL/EXE can provide a binary planting opportunity.
- Check your product's behavior with Process Monitor or some other monitoring tool. Use Process Monitor or a similar tool for observing what your application is doing in the current working directory. If you see it trying to open a DLL or EXE there, find the code that does that and fix it.
- Test your application with Microsoft's CWDIllegalInDllSearch hotfix set to "max". Install the CWDIllegalInDllSearch hotfix and set the global CWDIllegalInDllSearch registry value to 0xFFFFFFFF. This will completely remove CWD from the search path and will break your application if it relies on loading libraries from CWD. If your application breaks because of it, fix the code that loads the DLL from current working directory. If the broken code is in a 3rd-party library, contact its authors and refer them to these guidelines, then ask them to fix their code.
- Do all of the above for all modules of your product.
- Read Microsoft's guidelines. Read Secure loading of libraries to prevent DLL preloading attacks.
- Be careful when building MFC applications. A binary planting bug was present in the MFC (Microsoft Foundation Classes) libraries, resulting in all MFC applications automatically becoming binary planting-positive as soon as you built them (the bug has since been fixed). If you have dynamically linked MFC libraries with your application, the bug would be fixed on your users' systems once they have installed Microsoft's patch for Visual C++ Redistributable Packages. If, however, you were statically linking MFC libraries, you need to update your development environment (e.g., Visual Studio), then re-build your product and redistribute it to your users. Note: A vulnerable MFC application may be difficult to exploit if it's not associated with any file name extensions.
- Have your critical products profesionally analyzed for binary planting. If you're developing a security-critical product - e.g., for enterprises, finance, military or government - have it analyzed for binary planting issues by qualified experts with lots of experience. Don't expect free public tools like DllHijackAuditor or Process Monitor to adequately find all binary planting issues. The extensive binary planting research project conducted by ACROS Security makes us undoubtedly the world's most qualified experts on this subject.
Guidelines for Administrators
...to get immediate updates as we reveal our research.