DLL Hijacking Just Won’t Die

The folks that build the NSIS Installer have released updates to mitigate a serious security bug related to DLL loading. (v2.5 and v3.0b3 include the fixes).

To make a long and complicated story short, a bad guy who exploits this vulnerability places a malicious DLL into your browser’s Downloads folder, then waits. When you run an installer built by an earlier version of NSIS from that folder, the elevation prompt (assuming it runs at admin) shows the legitimate installer’s signature asking you for permission to run the installer. After you grant permission, the victim installer loads the malicious DLL which runs its malicious code with the installer’s permissions. And then it’s not your computer anymore.

So, how does the attacker get the malicious DLL into the browser’s Downloads folder? Surely that must involve some crazy hacking or social engineering, right?

Nah. The bad guy just navigates a frame of your browser to the DLL of his choice and, if you’re on Chrome orMicrosoft Edge, the DLL is dropped in the Downloads folder without even asking. Oops.

It’s trivial to simulate this attack to find vulnerable installers, even as a total noob.

1. Simply click this link which downloads a fake DLL (a simple text file containing the string ThisIsNotADLL).

2.Then download and run an installer built by an old version of NSIS:

image

3. Accept the elevation prompt:

image

4. Boom… watch Windows try to load your fake DLL as code:

image

Of course, a real bad guy isn’t going to be nice enough to use a fake DLL, he’ll instead use a valid but malicious one containing code that runs during DLL load, silently rooting your computer or whatever other nefarious thing he’d like.

Around 80% of the installers in my \Downloads\ folder are vulnerable to this attack; half of those are built on NSIS and half are built using other technologies.

You can learn more about DLL Hijacking attacks here.

EXE Hijacking

I’ve found some otherwise-safe installers will perform unqualified calls to system utilities (e.g. to manipulate the firewall or registry, etc). If a bad guy plants an executable of the target name (e.g. “netsh.exe”) in the \Downloads\ folder, it could be executed without warning, much like the DLL hijacking attack.

Action Items

Here are my suggestions:

  1. If you build installers with NSIS, please upgrade immediately.
  2. If you build installers with other technology, test for this attack (version.dll, msi.dll, shfolder.dll, etc). This post suggests a more complete way of finding target filenames. Another post notes that InnoSetup is vulnerable.
  3. Read Microsoft’s guidance for mitigating this vulnerability.
  4. Ask your browser vendor what they’re doing to prevent attacks of this nature. If they offer a “Confirm downloads” setting, use it.

-Eric Lawrence

Update: Chromium-based browsers quickly addressed this by classifying the .DLL file type as DANGEROUS, meaning that it will not download without explicit user consent.

Tip: Spartacus is a very cool tool to discover and exploit DLL Hijacking issues.

Published by ericlaw

Impatient optimist. Dad. Author/speaker. Created Fiddler & SlickRun. PM @ Microsoft 2001-2012, and 2018-, working on Office, IE, and Edge. Now a GPM for Microsoft Defender. My words are my own, I do not speak for any other entity.

22 thoughts on “DLL Hijacking Just Won’t Die

  1. This is basically a Windows design flaw and anything < Vista is impossible to fix 100% by ISVs.

    On Vista/7 with updates or 8 and later applications should call SetDefaultDllDirectories so dlls specified by relative paths only load from system32. That is only half the battle and only applies to things loaded by LoadLibrary at run-time. To stay secure without restricting the search to system32 only you would have to test every single Windows API function you call on every Windows version you support at all service pack levels and all new updates. Example: OleInitialize internally calls CreateWindow to create a hidden window for COM. The kernel part of CreateWindow calls back into usermode and uses LoadLibrary to load uxtheme.dll so it can hook in its Visual Styles support. To fix this particular function your application has to manually call LoadLibrary on %windir%\system32\uxtheme.dll before it calls OleInitialize. This vulnerability also exists for every PE delay-loaded function because the delay-load helper stub on these systems use a relative path to the .dll. Shell32.dll has a fair amount of delay-loaded functions in common code paths.

    The other problem is implicitly linked libraries that are loaded before the application starts running. The KnownDLLs list prevents this issue somewhat but it was never designed as a security feature and the list is not the same on all Windows versions. Taking this to the extreme you could say that you can only implicitly link with kernel32.dll and all functions in other dlls have to be delay-loaded after calling SetDefaultDllDirectories!

  2. Someday, someone is going to realize that trusted/signed code systems really don’t work if you load untrusted code in to the process. How hard is it to for the trusted system to ask for authorization for signatures on *any* code that isn’t already installed in to the system directory?!?

    1. While Firefox users can be attacked via this method, Firefox requires user confirmation of every DLL download and does not permit the user to opt out of that prompt.

    1. That *and* doing each save in a subfolder (surely someone has already written an extension that does that?).

    1. There’s no one “malicious DLL”– any attacker can simply write their own DLL that does anything they want.

      My test DLL is simply


      #include <windows.h>
      #include <stdio.h>
      int WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
      wchar_t bufferEXE[MAX_PATH];
      wchar_t bufferDLL[MAX_PATH];
      wchar_t sMsg[MAX_PATH*3];

      GetModuleFileName(hinst, bufferDLL, MAX_PATH);
      GetModuleFileName(NULL, bufferEXE, MAX_PATH);
      wsprintf(sMsg, L"%s\n\nis vulnerable to DLL Hijacking via\n\n%s", bufferEXE, bufferDLL);
      MessageBox(NULL,
      sMsg, L"DLL Hijacked", MB_ICONEXCLAMATION | MB_OK);
      }

    1. @Mark: Yes, creating a unique subfolder for each executable would definitely help, although you’d also probably want to ACL it such that a regular “user” cannot write to that folder if the target setup.exe is going to run elevated (at Admin).

  3. Google Chrome version 48.0.2564.97 gives a warning message when I download this version.dll: “This type of file can harm your computer. Do you want to keep version.dll anyway?” I haven’t changed settings from the defaults. This hardly counts as silent.

    1. 1. Chrome didn’t warn at the time of this post.
      2. The word “silent” was not used in the discussion of the download.
      3. Microsoft Edge has not taken a fix for this problem.
      4. I currently work on the Chrome security team.

    1. It works in other contexts, but it’s mostly interesting in the context of browser downloads, because this is a scenario where 1> Untrusted content gets onto the computer and 2> Executables are run by the user. Trusted executables also commingle with untrusted content in e.g. the user’s %TEMP% folder and possibly on things like UNC share locations.

Leave a comment