It rather involved being on the other side of this airtight hatchway: Writing to the application directory

We received a security vulnerability report that went roughly like this:

There is a security vulnerability in the X component. It loads shell32.dll from the current directory, thereby making it vulnerable to a current directory attack. Here is a sample program that illustrates the problem. Copy a rogue shell32.dll into the current directory and run the program. Observe that the rogue shell32.dll is loaded instead of the system one.

If you actually followed the instructions, what you saw depended on your definition of "run the program." Let's assume that the program has been placed in the directory C:\sample\sample.exe.

  1. Setting the current directory to the application directory.
    cd /d C:\sample
    copy \\rogue\server\shell32.dll

    In this case, the attack succeeds.

  2. Setting the current directory to an unrelated directory.
    cd /d %USERPROFILE%
    copy \\rogue\server\shell32.dll

    In this case, the attack fails.

  3. Running the application from Explorer.
    copy \\rogue\server\shell32.dll C:\sample
    double-click sample.exe in Explorer

    In this case, the attack succeeds.

Let's look at case 3 first. In case 3, what is the current directory? When you launch a program from Explorer, the current directory is set to the directory of the thing you double-clicked. Therefore, case 3 is identical to case 1. That's one less case to have to study.

We also see that the attack is not strictly a current directory attack, because the attack failed in case 2 even though a rogue shell32.dll was in the current directory.

What we're actually seeing is an application directory attack.

Recall that the application directory is searched ahead of the system directory. Therefore, you can override a file in the system directory by putting it in your application directory. This is part of the directory as a bundle principle. If you packaged a DLL with your application, then presumably that's the one you want, even if a future version of Windows decides to create a DLL of the same name.

The vulnerability report sort of acknowledged that this was an application directory attack rather than a current directory attack when they explained why this is a serious problem:

By placing a rogue copy of shell32.dll in the C:\Program Files\Microsoft Office\Office12 directory, an attacker can inject arbitrary code into all Office applications.

If the attack were really a current directory attack, the attacker would have put a rogue copy of shell32.dll in the directory containing your Excel spreadsheet, not the directory containing EXCEL.EXE.

And that's where you reach the airtight hatchway: Normal users do not have write permission into the C:\Program Files\Microsoft Office\Office12 directory. You need administrator privileges to create files there. And if you have administrator privileges, then you already pwn the machine. It's not really a vulnerability that you can do anything you want once you pwn the machine.

Of course, this non-vulnerability does expose a security issue you need to bear in mind when you run your own programs: Your application's directory is its airtight hatchway. Make sure you control who you let in! If you leave your application directory world-writeable, then you've effectively left your airtight hatchway unlocked. This is one reason why the Microsoft Logo guidelines recommend (require?) that programs be installed into the Program Files directory: The default security descriptor for subdirectories of Program Files does not grant write permission to normal users. It's secure by default.

There are many variations of this type of vulnerability report, and they nearly always are mischaracterized as a current directory attack. They usually go like this:

There is a DLL planting vulnerability in LITWARE.EXE. Place a rogue DLL named SHELL32.DLL in the same directory as LITWARE.EXE. When LITWARE.EXE is run, the rogue DLL is loaded from the current directory, resulting in code injection.

The person who submits the report has confused the application directory with the current directory, probably because they never considered that the two might be different.

C:\> mkdir C:\test
C:\> cd C:\test
C:\test> copy \\trusted\server\LITWARE.EXE
C:\test> copy \\rogue\server\SHELL32.DLL
C:\test> LITWARE
-- observe that the rogue DLL is loaded
-- proof of current directory attack

They never tried this:

C:\> mkdir C:\test
C:\> cd C:\test
C:\test> copy \\trusted\server\LITWARE.EXE
C:\> mkdir C:\test2
C:\> cd C:\test2
C:\test2> copy \\rogue\server\SHELL32.DLL
C:\test2> ..\test\LITWARE
-- observe that the rogue DLL is not loaded

That second experiment shows that the attack is not a current directory attack at all. It's an application directory attack.

Each time one of these reports comes in, we have to perform the same evaluation to confirm that it really is an application directory attack and not a current directory attack. (This means, among other things, repeating the test on every version of Windows, and every version of LitWare, and every combination of the two, just to make sure all the possibilities have been covered. The odds are strong that it will all turn into a false alarm, but who knows. Maybe there's something about the interaction between LitWare 5.2 SP2 and Windows XP SP3 that triggers a new code path that does indeed try to load shell32.dll from the current directory. And it's that specific combination of circumstances the person was trying to report, but did a bad job of expressing.)

Comments (25)
  1. John says:

    Each time one of these reports comes in, we have to perform the same evaluation to confirm that it really is an application directory attack and not a current directory attack.  This means, among other things, repeating the test on every version of Windows, and every version of LitWare, and every combination of the two, just to make sure all the possibilities have been covered.

    Today I learned how to perform a denial of service attack on Microsoft's security / testing group.

  2. alegr1 says:

    Why bother with placing a rogue DLL? Just replace the EXE, if you have write permissions to the directory.

  3. John Doe says:

    The Dynamic Link Library Search Order (…/ms682586(v=vs.85).aspx ) is the recommended reading material about this stuff.

    The most interesting thing is that shell32.dll is a known DLL, so this situation is still strange.

  4. Cesar says:

    As a (related) aside, what would be the set of magic calls you should do in your main() for new programs? I have come up with:




    plus a RegisterApplicationRestart() before the main loop. (Of course, using GetProcAddress for all calls, so it will not break on Windows XP.)

    The SetDllDirectoryW call would be the one which prevents the sort of vulnerability being discussed, since IIRC it removes the current directory from the search path.

  5. Mark says:

    Why not ask the submitter: are you sure this is an X attack and not a Y attack?  We've tested this and can only reproduce it as a Y attack.  Which version of Litware are you running?

    That way the submitter can save you from doing all that work for nothing.

    [A significant fraction of submitters wouldn't understand the difference between X and Y, so asking the question doesn't get you anywhere. Some submitters aren't really interested in solving the problem; they just want street cred. Or the submitter may say, "Of course I know the difference between X and Y, and this is clearly an X. Man, what a bunch of idiots you guys are." And even if the submitter agrees that it's a Y, you still have the risk that there really was an X vulnerability, and then when it is finally discovered, the submitter will brag "I reported this to Microsoft 2 years ago and they told me it wasn't a vulnerability." A lot of the job is PR. -Raymond]
  6. Brian_EE says:

    @Mark: What if the submitter doesn't reply to the request yet this turns out to be a real vulnerability? People would jump all over Microsoft for not investigating a reported attack vector.

  7. kog999 says:

    i discovered a way to make this attack work even i do have not write permissions to the applications directory. I create a new folder in a location i have access to, such as my Desktop, i then copy the contents of the application folder to the folder on my desktop, replace shell32.dll since i have write access to that folder and run the application from this location! /Scarcasm

  8. JoeWoodbury says:

    This entirely ignores the fact that if you have administrative access to any computer, there is no security. In addition, the old rule of thumb was physical access means all access. This is generally true to this day.

  9. Write access says:

    Just the other day I was thinking if it's less secure for a service to have its event log message format strings stored as resource for easier localization, or hardcoded in the code. I was saying to myself, well, if it's resource, then it's also easier for people to mess up and create buffer overflows; putting the format strings in the code at least saves some LoadString calls and keeps away those less determined. Several seconds later I concluded that it's free game to those who can modify the binary and forgot about it.

  10. Joshua says:

    @EricF: An awful lot of cross-platform software misbehaves like that only on Windows because the port framework is an old version that assumes Windows 9x rules. There's another one in particular that is doing that because it really wants to be setgid but can't be.

  11. voo says:

    I'm pretty sure you can still tell Office to install itself into a different folder (and really anything else isn't especially user-friendly) so I hope they don't rely on the implicit protection by Program Files but also make sure that the permissions are correctly set when installing anywhere else.

  12. laonianren says:

    @Write access: Why would your service be reading event log message format strings?  Event log messages are formatted when they are viewed.

  13. Evan says:

    I know of an unpatched vulnerability. If you find "command prompt" in the start menu, choose "run as administrator", authenticate if necessary, and run "del /s /q ", you'll have destroyed your system! How can Micro$oft let such bugs be?

    [Actually the funny thing is there's a chance this wouldn't even work, a la 'rm -rf /' being blocked my many rm implementations. Not that I've tried that either.]

  14. JJJ says:

    @Evan:  That brings up another issue.  All you have to do to get administrator access is click 'Yes' on a UAC dialog.  Surely that MUST be a security hole because really, what kind of attacker would click "no"?


  15. This was a concern with earlier versions of Office which required write access on their program folder tree to enable the built-in Wizards to run.

  16. ErikF says:

    There is a program that I use that *still* insist on having world-writable shared data in their application directory! I've mitigated the solution as much as I can for them by only giving the directories that need to be writable that access, but it's still a pain; how long has Windows had an "All Users" directory for? (This program in question is cross-platform, but still the issue remains: how many admins have their /usr and /opt directories world-writable?)

  17. ErikF says:

    @Evan: It probably wouldn't work as well as the Unix version because of the way that Windows handles files in use. Unix will generally allow you to delete in-use files, whereas Windows won't. Yes, I have been bitten by this when I accidentally deleted "busybox" while in recovery mode on a Unix box; what fun I had trying to get that system running again!

  18. David Maas says:


    Except Windows isn't using everything important all the time. You'd still really mess up the system.

  19. RicarDog says:

    I'm a bit confused regarding the recommended place to install DLLs. The guidelines article (…/ms997548.aspx) suggests creating a System folder under the application folder and then adding it to the App Paths registry key. However, the DLL search order article (…/ms682586(v=vs.85).aspx) says that Apps Path is not used when computing the DLL search path. So, should we ignore the guidelines and put all DLLs under the application folder?

  20. Harry Johnston says:

    @RicarDog: it is preferable not to use the DLL search order at all.  Instead, look up your entry in the App Paths registry key and use it to constuct the full path to the DLL.  You can then load the DLL with an explicit path.

  21. Evan says:

    @JJJ: "All you have to do to get administrator access is click 'Yes' on a UAC dialog.  Surely that MUST be a security hole because really, what kind of attacker would click "no"?"

    If you're serious, you're confusing a malicious user with a malicious program. 99% of the time it's the latter that's the problem, not the former. (For the same reason, "physical access is game over" is also irrelevant 99% of the time.) The whole point of the secure desktop that the UAC uses is that only the *user* him/herself can respond — programs can't emulate clicks. So if someone in OVERSEAS_COUNTRY wants to take over my computer, he'll need my "consent".

    (Of course, that's ignoring whether the "make this dialog go away!" reaction really counts as consent. UAC is a small step in the right direction, but only a small step; it's a VERY difficult problem to solve well.)

  22. Evan says:

    Also reading JJJ's comment again I think I'm dumb.

  23. Joshua says:

    @Harry Johnston: My preferred way to do it is to assemble the app path from my own executable's path.

  24. Steve says:

    Does this mean I need to make sure there are no rogue dlls in my Downloads folder when running .exe installers?

  25. ender says:

    @Steve: yes, and also make sure there isn't a msiexec.exe in your Downloads folder either.

Comments are closed.