Simple question: What is dll hijacking?
I read a lot about which applications are vulnerable, but not a lot of depth as to why.
Answers appreciated.
The basics are simple. Windows has a search path for DLLs, much the same way it has a $PATH for finding executables. If you can figure out what DLLs an app requests without an absolute path (triggering this search process), you can then place your hostile DLL somewhere higher up the search path so it'll be found before the real version is, and Windows will happilly feed your attack code to the application.
So, let's pretend your system's DLL search path looks something like this:
a) . <--current working directory of the application, highest priority, first check
b) \windows
c) \windows\system32
d) \windows\syswow64 <-- lowest priority, last check
and some application Foo.exe requests "bar.dll", which happens to live in the syswow64 (d) subdir. This gives you the opportunity to place your malicious version in a), b), or c) and it will be loaded into the app automatically whenever the app requests bar.dll. And now your foo is well and trully bar'd.
As stated before, even an absolute full path can't protect against this, if you can replace the DLL with your own version.
And of course, this isn't really limited to Windows either. Any OS which allows for dynamic linking of external libraries is theoretically vulnerable to this.
DLL Hijacking is really a simple concept.
Applications load external code via DLLs (Dynamic Link Libraries). DLL Highjacking is a process by which malicious code is injected into an application via a malicious DLL with the same name as a DLL used by the application.
An application is vulnerable to DLL hijacking depending on how they reference their DLLs. One example is using relative paths instead of the absolute path to the DLL. Another is loading DLLs using environment variables that may not be set properly in which case the directory defaults to a relative path of the executing application.
Related
Often one wants to LoadLibrary a system DLL like Kernel32.dll, handily LoadLibraryEx has a dwFlags parameter that allows you to specify LOAD_LIBRARY_SEARCH_SYSTEM32.
For security reasons, this can be very important as malicious attackers can subvert the default search paths that LoadLibrary looks through.
However Windows XP, which I must still support, cannot use LOAD_LIBRARY_SEARCH_SYSTEM32 as it didn't exist eons ago. This leads me to the following conclusion: I need to come up with a fully qualified path and pass that to LoadLibrary.
However, a robust implementation of this is tricky due to WoW64 and file-system redirection.
I have to worry about this possibility because the code in question lives in a library and can be called in strange environments.
My current thoughts are something like the following pseudo-code:
string Path;
BOOL Wow64Process;
if (IsWow64Process(CurrentProcess(), &Wow64Process) && Wow64Process &&
WoW64FSRedirectionIsDisabled)
Path = GetSystemWow64Directory();
else
Path = GetSystemDirectory();
Path += "\Kernel32.dll";
LoadLibrary(Path);
I have considered, and rejected using Wow64DisableWow64FsRedirection and Wow64RevertWow64FsRedirection but this is problematic as it will leave FS redirection disabled in the library's DLLMain.
Given the above, what's the best way to go about what I am trying to do?
Thanks!
In terms of loading a DLL without dependencies, you are over thinking this. If the user has disabled file system redirection in a WOW64 process then LoadLibraryEx will attempt to load the DLLs from the 64 bit system32 folder. So you can simply do exactly the same and remove all that WOW64 code.
However, the documentation says (emphasis mine):
If this value is used, %windows%\system32 is searched for the DLL and its dependencies.
So, your approach cannot replicate LOAD_LIBRARY_SEARCH_SYSTEM32 because you can only supply the full path for the DLL you load, but not for its dependencies.
The only way to hope to replicate this is to force system32 to be at the head of the DLL search path. Specifying a full path won't help you.
Finally, are you sure that LOAD_LIBRARY_SEARCH_SYSTEM32 is not available on XP64?
As an alternative to the other answers use SetDlldirectory.
The advantage ist that the current Directory isn't checked for a DLL-load.
Still open is the search via PATH... but this is in the last Position, so your DLL located in the SYSTEM32 directoryies should be loaded first using SettDllDirectory.
Also it is supported since XP SP1
My code is using a library which is a static/implicitly linked DLL (let's call it DLLB) but on runtime it can't find it.
This is despite locating DLLB in the same directory as the code that calls it. (The calling code is itself a DLL, DLLA which is called from python, which is called from arcpy. I'm not quite sure why the python finds DLLA fine but DLLA doesn't find DLLB, despite them being in the same directory).
If I put the library DLL somewhere on the system path, everything works just fine.
But what's the best approach for deployment? Add an entry to the system path on the client machine, at install time? Modify the system path at runtime, from python, before loading the DLL? Something else?
Python must be specifying the full path to the DLL in the LoadLibrary call. This is recommended practice. If you specify only a module name, there is a risk of loading the wrong DLL, possibly introducing a binary planting vulnerability.
Note that although the default search path includes the directory the executable was loaded from, it does not include the directory other DLLs were loaded from. So the behaviour you're seeing is as expected.
If you can use dynamic loading, you can look up the path to DLLA and use it to construct the path to DLLB.
To get a module handle for DLLA, call GetModuleHandleEx. To get the full path to DLLA from the module handle, call GetModuleFileName. Check that the last element is DLLA.dll and replace it with DLLB.dll. You can then call LoadLibrary.
I have a program which stores plugins in multiple directories, like so:
root/
core/bin/
app.exe
core.dll
plugin.dll
support.dll
a/bin/
a.dll
a_support.dll
In this example, a.dll imports core.dll, support.dll, and a_support.dll (they are in that order in the import table). a_support.dll imports support.dll. I can change all but the support modules, those are redists of a third-party library.
My code calls LoadLibraryEx(name, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) to load each plugin. For core.dll and plugin.dll, this works fine.
When I try to load a.dll, it fails saying a_support.dll was not found. No errors about core.dll or support.dll, perhaps because they're already in memory.
My suspicion is that when a_support.dll is loaded, support.dll cannot be found, but this seems unusual as a.dll appears to import support.dll before a_support.dll.
Is this layout of modules even possible to use? Will the system be able to use the already-loaded support DLLs, or will it go searching for them and fail? Would there be a way to handle this through manifests? Is there a way to make this work, or will I have to relocate all modules into a single directory?
Edit: At Adrian McCarthy's suggestion, I ran the loading sequence with Process Monitor tracking, and it seems that when I call LoadLibrary("root/a/bin/a.dll", ...), it starts by searching the root directory, then system directories, then down through the path. For some reason, it never searches a/bin/, which it very much should.
I double-checked the paths, and noticed that my calls to load plugin.dll where using the wrong path (root, instead of root/core/bin). Either way, core.dll was loading correctly. After fixing that, I tried it again and this time a.dll does find a_support.dll and seems to load. However, that makes absolutely no sense, unless the loader is successfully using support.dll from... somewhere. The procmon log doesn't show it even attempting to load support.dll again, so I'm not entirely sure at this point if there actually is a problem (besides behavior from the loader that makes no sense).
I suggest using Process Monitor to see what's really happening. You'll see if it's looking in the right place, whether a_support.dll is opened but not loadable because something else is missing, etc.
Certainly one solution would be to place all DLLs in the same directory, the same directory as the .exe. If you can bring yourself to do that it would be the simplest approach for sure.
If not then you will have a bit more work on your hands. I guess you are expecting that the loader will search in the directory where the DLL lives. Sadly it doesn't. Instead the loader will look first in the executable file's directory, and then the rest of the DLL search order. This is why a_support.dll fails to load, because it is not in the same directory as the executable.
The fact that modules are already in memory is beside the point. The loader goes looking for the file. When it finds the file that it wants it then checks to see if it is already loaded. If so then it simply bumps the reference count to that module. Otherwise it loads it into the process.
You could switch to using LoadLibrary for all DLL loads and always being explicit about the path. That's probably inconvenient.
You could use side-by-side assemblies but that doesn't sound very compatible with a plugin architecture.
So I think the main remaining option is SetDllDirectory. Call this just before you load the plugin. You only need to add a/bin to the search path since the rest of the modules are in the executable directory and so will be found without trouble. Restore this setting to its default by calling SetDllDirectory again, passing NULL, once the plugin has loaded and resolved all of its imports.
If you have multiple sub-directories then use AddDllDirectory.
This is a rather confusing application.
Some questions then:
Which dll's does app.exe implicitly import?
core.dll is both implicitly loaded by a.dll AND as a plugin via LoadLibraryEx?
How was the call to LoadLibraryEx on /plugin.dll ever succeeding? If the path was FQ and did not point at an actual dll, LoadLibrary should have failed outright on that dll.
I can't tell if you're giving sample code or real code. You wrote:
LoadLibrary("root/a/bin/a.dll", ...)
If that's the real code, there are two problems here.
First, LoadLibrary doesn't do what you'd expect with a relative path. From MSDN:
To load a module from a relative path without searching any other
path, use GetFullPathName to get a nonrelative path and call
LoadLibrary with the nonrelative path. For more information on the DLL
search order, see Dynamic-Link Library Search Order.
Basically, you give it a full path and get that file, or you let it search for a name in all the "usual" locations. If you give it a relative path, it basically ignores that path, grabs the name, and looks in the usual locations.
If you really meant LoadLibraryEx, note that when you use LOAD_WITH_ALTERED_SEARCH_PATH you get "undefined behavior" if handed a relative path. Again quoting MSDN:
If this value is used and lpFileName specifies a relative path, the behavior is undefined.
Second, you have forward slashes instead of backslashes. Neither LoadLibrary nor LoadLibraryEx likes those.
To my astonishment I found VB6 code that uses Declare statements to define functions in a .dll that lives in the Program Folder without it being registered on Windows. This seems like a supersimple way to avoid the .dll hell without having to resort to using Side by side manifests. Can I read some more about this somewhere? Are there snags?
The Declare statement is used to do "just in time" binding to non-ActiveX DLLs. Until your program "touches" a Declared entrypoint no attempt is made to load the library.
It basically has nothing at all to do with the topic of DLL Hell.
Muddled thinking can even lead people to plop ActiveX DLLs "next to" the EXE which actually can result in DLL Hell because people who tend to do this also use poor techniques for installing and uninstalling applications.
Poorly designed application A deployment plops a commonly shared DLL or OCX next to the EXE.
Poorly designed application is run, the VB6 runtime can't find the classes in the registry, does a DLL Search using Windows heuristics, immediately locates the DLL next to the EXE and calls its self-registration entrypoint.
Innocent, properly designed applications B, C, D are later installed that use the same DLL/OCX and their installers find the library already registered.
Poorly designed application A is uninstalled, typically by simply deleteing its folder in Program Files.
Applications B, C, and D (and any future applications using the library) are now broken - due to orphaned component registration pointing to a non-existant library.
Moral of the story:
Never, never, never put DLLs "next to" your VB6 application on installation. If you have private DLLs that are not shared with other applications even then put them into a libs, etc. folder under the application folder. Possible exception might be non-COM DLLs, such as those you expect to use Declare with.
There is also a great deal of misunderstanding about manifests, of which there are multiple kinds. The ones you are probably thinking of are application and assembly manifests.
These can be used for selecting among different versions of a library installed Side by Side, or they can be used to isolate applications and assemblies which is the part that has bearing on DLL Hell.
Of course application manifests can be used to specify quite a few other things about how Windows should run the application.
Windows searches in a well-documented sequence of folders for LoadLibrary (which VB6 uses behind the scenes to resolve Declare declarations). Since the first location on the list of search folders is the app's own folder, your discovery makes perfect sense.
It doesn't resolve the "DLL hell" issue for the most part, though. It can't work for system DLLs, for instance, because Windows preloads most of them. Also, if a DLL is already loaded into memory, Windows may use that copy of the DLL (not sharing data, but code can be reused).
That's part of the reason that manifests were created; they allow an application to strictly define required versions of system DLLs in order to provide certain functionality. VB6's technique is old fashioned (just like VB6).
I want to put my dependent files in the app directory.
I seem to remember that you can force VB6 to use the files in the local directory only.
Any hints?
You may also want to try setting up Reg-Free COM for your project. There's a freeware called Unattended Make My Manifest that will do most of the work for you.
Placing component libraries in the EXE folder (with or without .local files) can be deleterious to the hygiene of target machines too.
VB6 programs will register the components here via the self-reg entrypoint behind your back if they are not previously registered. Then if the application is moved or removed you leave the user with a broken reigistration - possibly fatal to subsequently installed applications using some of the same components. This is probably fine though for application specific components, i.e. your own DLL or OCX that will never be needed by another application.
The .local trick was really not meant for use with VB6 programs and if it is used your installer needs to be aware and properly install and register the components if they are not already on the machine. It was meant as a manual hack to get around DLL version compatibility problems on individual machines, not a deployment strategy.
Move up to SxS application and assembly manifests (Reg-Free COM and more) for a better solution. DLL/COM Redirection (.local) was a good try but it has many warts.
Clay Nichol's answer about the search order is not quite correct. That search order only applies to non-COM components. I.e. only some DLLs, and not OCXs. If you register your COM objects, they will be used from the directory where they are registered regardless of what's in the local directory, unless you use reg-free COM or a .local file.
EDIT:
MakeMyManifest is well spoken of as an automatic tool for creating manifests for VB6 projects, haven't tried it myself.
DirectCOM also has fans, again I haven't tried it.
EDIT The MMM website is down. I see here that the author was having trouble with their hosting and has provided another location to get Make My Manifest - download it here.
There is a semi-automatic technique to generate reg-free COM manifests. You can create the manifests with Visual Studio 2008 (you can use a free version like Visual Basic Express Edition). Then make a couple of edits by hand to make the manifests suitable for use from VB6. See this section of this MSDN article for step-by-step instructions - ignore the rest of the article which is about ClickOnce.
It can be sort of confusing because every version of windows, the rules change. Older versions of Windows search the path before the current directory.
A simple solution without manifests:
If your executable file is A.EXE, add a (0-byte, empty) file in the same directory named A.EXE.local -- for older versions of Windows this puts the app directory ahead of the path in the search order.
Found it myself:
Windows does look in the App Directory first:
If SafeDllSearchMode is enabled, the search order is as follows:
The directory from which the application loaded.
The system directory. Use the GetSystemDirectory function to get the path of this directory.
The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
The current directory.
The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
If SafeDllSearchMode is disabled, the search order is as follows:
1. The directory from which the application loaded.
2. The current directory.
3. The system directory. Use the GetSystemDirectory function to get the path of this directory.
4. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
6. The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
according to : http://msdn.microsoft.com/en-us/library/ms682586.aspx
But you can redirect where it looks for .dll's using a Manifest:
http://msdn.microsoft.com/en-us/library/aa375365(VS.85).aspx