November, 2010

  • The Old New Thing

    Why doesn't the End Task button end my task immediately?

    • 64 Comments

    Commenter littleguru asks, "Why does the End Now button not kill the process immediately?"

    When you click the End Now button, the process really does end now, but not before a brief message from our sponsor.

    When you kill a hung application, Windows Error Reporting steps in to record the state of the hung application so it can be submitted to the mother ship (with your permission). If you are running Windows XP or Windows Vista, you can briefly see a process called dumprep.exe or WerFault.exe; these are the guys who are doing the data collection.

    After being uploaded to Microsoft, these failure reports are studied to determine why the application stopped responding and what could be done to fix it. I've been asked to do quite a few of these analyses myself, and sometimes it's something pretty mundane (an application sends a cross-thread message while holding a critical section, and the thread can't receive the message because it's stuck waiting for the critical section that the sender is holding—classic deadlock), and sometimes it's something pretty weird (application has a bug if the number of sound output devices is not equal to one). Whatever the reason, I write up my analysis, and the people who are in charge of such things make arrangements for the information to be sent back to the vendors who wrote the application (assuming the vendors are registered with Winqual).

    If you don't want Windows Error Reporting to collect application crash and hang reports, you can disable it from the Group Policy Editor under Windows Error Reporting. Of course, if you do this, then you don't get to vote on which program crashes and failures Microsoft should work on fixing.

    Note: This entry is an experiment: I mentioned Windows Error Reporting and WHQL. If people complain about digital certificate authorities, that'll just confirm my bias against returning to those old debugging stories.

    Update: Experimental results obtained. No more stories involving Windows Error Reporting and WHQL.

  • The Old New Thing

    Your debugging code can be a security vulnerability: Loading optional debugging DLLs without a full path

    • 49 Comments

    Remember, the bad guys don't care that your feature exists just for debugging purposes. If it's there, they will attack it.

    Consider the following code:

    DOCLOADINGPROC g_pfnOnDocLoading;
    
    void LoadDebuggingHooks()
    {
     HMODULE hmodDebug = LoadLibrary(TEXT("DebugHooks.dll"));
     if (!hmodDebug) return;
     g_pfnOnDocLoading = (DOCLOADINGPROC)
                   GetProcAddress(hmodDebug, "OnDocLoading");
     ...
    }
    
    HRESULT LoadDocument(...)
    {
     ...
     if (g_pfnOnDocLoading) {
       // let the debugging hook replace the stream
       g_pfnOnDocLoading(&pstmDoc);
     }
     ...
    }
    

    When you need to debug the program, you can install the DebugHooks.dll DLL into the application directory. The code above looks for that DLL and if present, gets some function pointers from it. For illustrative purposes, I've included one debugging hook. The idea of this example (and it's just an example, so let's not argue about whether it's a good example) is that when we're about to load a document, we call the OnDocLoading function, telling it about the document that was just loaded. The OnDocLoading function wraps the IStream inside another object so that the contents of the document can be logged byte-by-byte as it is loaded, in an attempt to narrow down exactly where document loading fails. Or it can be used for testing purposes to inject I/O errors into the document loading path to confirm that the program behaves properly under those conditions. Use your imagination.

    But this debugging code is also a security vulnerability.

    Recall that the library search path searches directories in the following order:

    1. The directory containing the application EXE.
    2. The system32 directory.
    3. The system directory.
    4. The Windows directory.
    5. The current directory.
    6. The PATH.

    When debugging your program, you install DebugHooks.dll into the application directory so that it is found in step 1. But when your program isn't being debugged, the search in step 1 fails, and the search continues in the other directories. The DLL is not found in steps 2 through 4, and then we reach step 5: The current directory.

    And now you're pwned.

    Your application typically does not have direct control over the current directory. The user can run your program from any directory, and that directory ends up as your current directory. And then your LoadLibrary call searches the current directory, and if a bad guy put a rogue DLL in the current directory, your program just becames the victim of code injection.

    This is made particularly dangerous when your application is associated with a file type, because the user can run your application just by double-clicking an associated document.

    When you double-click a document, Explorer sets the current directory of the document handler application to the directory that contains the document being opened. This is necessary for applications which look around in the current directory for supporting files. For example, consider a hypothetical application LitWare Writer associated with *.LIT files. A LitWare Writer document ABC.LIT file is really just the representative for a family of files, ABC.LIT (the main document), ABC.LTC (the document index and table of contents), ABC.LDC (the custom spell check dictionary for the document), ABC.LLT (the custom document layout template), and so on. When you open the document C:\PROPOSAL\ABC.LIT, LitWare Writer looks for the other parts of your document in the current directory, rather than in C:\PROPOSAL. To help these applications find their files, Explorer specifies to the CreateProcess function that it should set the initial current directory of LitWare Writer to C:\PROPOSAL.

    Now, you might argue that programs like LitWare Writer (which look for the ancillary files of a multi-file document in the current directory instead of the directory containing the primary file of the multi-file document) are poorly-written, and I would agree with you, but Windows needs to work even with poorly-written programs. (Pre-emptive snarky comment: Windows is itself a poorly-written program.) There are a lot of poorly-written programs out there, some of them industry leaders in their market (see above pre-emptive snarky comment) and if Windows stopped accommodating them, people would say it was the fault of Windows and not the programs.

    I can even see in my mind's eye the bug report that resulted in this behavior being added to the MS-DOS Executive:

    "This program has worked just fine in MS-DOS, but in Windows, it doesn't work. Stupid Windows."

    Customers tend not to be happy with the reply, "Actually, that program has simply been lucky for the past X years. The authors of the program never considered the case where the document being opened is not in the current directory. And it got away with it, because the way you opened the document was to use the chdir command to move to the directory that contained your document, and then to type LWW ABC.LIT. If you had ever done LWW C:\PROPOSAL\ABC.LIT you would have run into the same problem. The behavior is by design."

    In response to "The behavior is by design" is usually "Well, a design that prevents me from getting my work done is a crappy design." or a much terser "No it's not, it's a bug." (Don't believe me? Just read Slashdot.)

    So to make these programs work in spite of themselves, the MS-DOS Executive sets the current directory of the program being launched to the directory containing the document itself. This was not an unreasonable decision because it gets the program working again, and it's not like the program cared about the current directory it inherited from the MS-DOS Executive, since it had no control over that either!

    But it means that if you launched a program by double-clicking an associated document, then unless that program takes steps to change its current directory, it will have the document's containing folder as its current directory, which prevents you from deleting that directory.

    Bonus chatter: I wrote this series of entries nearly two years ago, and even then, I didn't consider this to be anything particularly groundbreaking, but apparently some people rediscovered it a few months ago and are falling all over themselves to claim credit for having found it first. It's like a new generations of teenagers who think they invented sex. For the record, here is some official guidance. (And just to be clear, that's official guidance on the current directory attack, not official guidance on sex.)

    History chatter: Why is the current directory even considered at all? The answer goes back to CP/M. In CP/M, there was no PATH. Everything executed from the current directory. The rest is a chain of backward compatibility.

  • The Old New Thing

    You can filter the Common File dialog with wildcards

    • 46 Comments

    A customer reported an apparent inconsistency in the shell common file dialogs:

    The question mark appears to be treated differently from other invalid file name characters. I tried to save a file in Paint under the name ?.jpg but instead of telling me that I had an invalid character in the file name (as it does with other characters like / < and |) or navigating to a new folder (like \ or *), it appears to do nothing.

    Actually, it did do something. You just couldn't tell because the result of that something was the same as doing nothing.

    If you type a wildcard like ? or * into a common file dialog, the dialog interprets this as a request to filter the list of files to those which match the wildcard you specify. In this particular example, typing ?.jpg says "Show me all the single-character files with the .jpg extension." From the description in the original report, I gather that the customer's tests took place in an empty directory (so the filter had no effect).

    The customer responded with muted surprise.

    Hmmmmm.....interesting. I hadn't realized you can type in your own filter in dialog boxes. How long has this feature existed?

    The ability to filter the common file dialog has been around since the dialogs were introduced in Windows 3.1.

  • The Old New Thing

    Using delayload to detect functionality is a security vulnerability

    • 45 Comments

    We saw last time that your debugging code can be a security vulnerability when you don't control the current directory. A corollary to this is that your delayload code can also be a security vulnerability, for the same reason.

    When you use the linker's delayload functionality to defer loading a DLL until the first time it is called, the linker injects code which calls LoadLibrary on a DLL the first time you call a function in it, and then calls GetProcAddress on the functions you requested. When you call a delay-loaded function and the delayload code did not get a function pointer from GetProcAddress (either because the DLL got loaded but the function does not exist, or because the DLL never got loaded in the first place), it raises a special exception indicating that a delayed load failed.

    Let's look again at the order in which the LoadLibrary function searches for a library:

    1. The directory containing the application EXE.
    2. The system32 directory.
    3. The system directory.
    4. The Windows directory.
    5. The current directory.
    6. The PATH.

    The code which implements the delayload functionality uses a relative path when it passes the library name to LoadLibrary. (It has no choice since all it has to work with is the library name stored in the IMPLIB.) Consequently, if the DLL you are delay-loading does not exist in any of the first four search locations, the LoadLibrary will look in location 5: the current directory.

    At this point, the current directory attack becomes active, and a bad guy can inject an attack DLL into your process.

    For example, this sample code uses delayload to detect whether the functions in dwmapi.dll exist, calling them if so. If the function IsThemeEnabled is not available, then it treats themes as not enabled. If the program runs on a system without dwmapi.dll, then the delayload will throw an exception, and the exception is caught and turned into a failure. Disaster avoided.

    But in fact, the disaster was not avoided; it was introduced. If you run the program on a system without dwmapi.dll, then a bad guy can put a rogue copy of dwmapi.dll into the current directory, and boom your process just loaded an untrusted DLL. Game over.

    Using the delayload feature to probe for a DLL is morally equivalent to using a plain LoadLibrary to probe for the presence of a debugging DLL. In both cases, you are looking for a DLL with the expectation that there's a good chance it won't be there. But it is exactly in those sometimes it won't be there cases where you become vulnerable to attack.

    If you want to probe for the existence of a DLL, then you need to know what directory the DLL should be in, and then load that DLL via that full path in order to avoid the current directory attack.

    On the other hand, if the DLL you want to delayload is known to be installed in a directory ahead of the current directory in the search path (for example, you require versions of the the operating system in which the DLL is part of the mandatory install, and the directory in which it is installed is the System32 directory) then you can use delayload.

    In other words, you can use delayload for delaying the load of a DLL. But if you're using delayload to probe for a DLL's existence, then you become vulnerable to a current directory attack.

    This is one of those subtle unintended consequences of changing the list of files included with an operating system. If you take what used to be a mandatory component that can't be uninstalled, and you change it to an optional component that can be uninstalled, then not only do programs which linked to the DLL in the traditional manner stop loading, but you've also introduced a security vulnerability: Programs which had used delayload under the calculation (correct at the time it was made) that doing so was safe are now vulnerable to the current directory attack.

  • The Old New Thing

    The curse of the current directory

    • 44 Comments

    The current directory is both a convenience and a curse. It's a convenience because it saves you a lot of typing and enables the use of relative paths. It's a curse because of everything else.

    The root cause of this curse is that the Windows NT family of operating systems keeps open a handle to the process's current directory. (Pre-emptive Yuhong Bao comment: The Windows 95 series of operating systems, on the other hand, did not keep the current directory open, which had its own set of problems not relevant to this discussion.)

    The primary consequence of this curse is that you can't delete a directory if it is the current directory of a running process. I see people stumble upon this all the time without realizing it.

    I am trying to delete a directory X, but when I try, I get the error message The process cannot access the file because it is being used by another process.. After some hunting around, I found that directory X is being held open by someapp.exe. Why the heck is someapp.exe holding my directory open, and how do I get it to stop?

    The value of someapp.exe changes over time, but the underlying problem is the same. And when this happens, people tend to blame someapp.exe for stupidly holding a directory open.

    Most of the time, someapp.exe is just a victim of the curse of the current directory.

    First, let's take the case where someapp.exe is explorer.exe. Why is the current directory of Explore set to this directory?

    Well, one reason might be another curse of the current directory, namely, that the current directory is a process-wide setting. If a shell extension decided to call SetCurrentDirectory, then that changes the current directory for all of Explorer. And if that shell extension doesn't bother to call SetCurrentDirectory a second time to reset the current directory to what it was, then the current directory gets stuck at the new directory, and Explorer has now been conned into changing its current directory permanently to your directory.

    Mind you, the shell extension might have tried to do the right thing by setting the current directory back to its original location, but the attempt might have failed:

    GetCurrentDirectory(Old) // returns C:\Previous
    SetCurrentDirectory(New) // changes to C:\Victim
    .. do stuff ..
    SetCurrentDirectory(Old) // changes to C:\Previous - fails?
    

    That second call to SetCurrentDirectory can fail if, while the shell extension is busy doing stuff, the directory C:\Previous is deleted. Now the shell extension can't change the directory back, so it's left stuck at C:\Victim, and now you can't delete C:\Victim because it is Explorer's new current directory.

    (The preferred behavior, by the way, is for the shell extension not to call SetCurrentDirectory in the first place. Just operate on full paths. Since the current directory is a process-wide setting, you can't be sure that some other thread hasn't called SetCurrentDirectory out from under you.)

    Mind you, making the current directory a per-thread concept doesn't solve this problem completely, because the current directory for the thread (if such a thing existed) would still have a handle open until the thread exited. But if the current directory had been a per-thread concept, and if the thread were associated with an Explorer window, then closing that window would at least encourage that thread to exit and let you unstick the directory. That is, unless you did a Terminate­Thread, in which case the handle would be leaked and your attempt to release the handle only ensures that it never happens. (Note to technology hypochondriacs: This paragraph was a hypothetical and consequently will be completely ineffective at solving your problem.)

    The story isn't over yet, but I'll need to digress for a bit in order to lay the groundwork for the next stage of the curse.

    Bonus chatter: Hello, people. "The story isn't over yet." Please don't try to guess the next chapter in the story.

  • The Old New Thing

    Why does the Win32 Time service require the date to be correct before it will set the time?

    • 43 Comments

    Public Service Announcement: Daylight Saving Time ends in most parts of the United States this weekend.

    Andy points out that if you attempt to synchronize your clock when the date is set incorrectly, the operation fails with the error message "An error occurred while Windows was synchronizing with time.windows.com. For security reasons, Windows cannot synchronize with the server because your date does not match. Please fix the date and try again." He wonders what the security risk is.

    First of all, for people who are trying to solve the problem, the solution is to follow the steps in the error message. Set your date to the correct date, then try again. If that doesn't help, also set the time to something close to the correct time. Once your time gets close, the time server can nudge it the rest of the way.

    Back to the original question: What is the security risk being defended against, here?

    At first glance, you might think that the server is attempting to defend itself against a client whose time is set incorrectly, but actually the potential attack is in reverse: Your computer is protecting itself against a rogue time server.

    The Kerberos authentication protocol relies heavily on all participants agreeing on what time it is (with some slop tolerance). If somebody manages to fool the client into synchronizing its time against a rogue server (for example, by using a DNS poisoning attack), the attacker can use that invalid date (typically a backdate) as a foothold for the next level of attacks.

    The default configuration for the Windows Time service is to reject attempts to change the clock on domain-joined machines by more than 15 hours. You can change the configuration settings by following the instructions in this KB article (which happens also to have been the source material for most of this article).

  • The Old New Thing

    But who's going to set up their own email server?

    • 42 Comments

    Many many years ago, back in the days when Microsoft's email address had exclamation points, an internal tool was developed to permit Microsoft employees to view and update their Benefits information from the comfort of their very own offices. Welcome to the paperless office!

    One of my friends noticed an odd sentence in the instructions for using the tool: "Before running the program, make sure you are logged onto your email server."

    "That's strange," my friend thought. "Why does it matter that you're logged onto your email server? This tool doesn't use email."

    Since my friend happened at the time to be a tester for Microsoft's email product, he tried a little experiment. He created a brand new email server on one of his test machines and created an account on it called billg. He then signed onto that email server and then ran the tool.

    Welcome, Bill Gates. Here are your current Benefits selections...

    "Uh-oh," my friend thought. "This is a pretty bad security hole." The tool apparently performed authentication by asking your email server, "Hey, who are you logged in as?" The answer that came back was assumed to be an accurate representation of the user who is running the tool. The back-end server itself was not secured at all; it relied on the client application to do the security checks.

    My friend sent email to the vice president of Human Resources informing him of this problem. "You need to shut down this tool immediately. I have found a security hole that allows anybody to see anybody else's Benefits information."

    The response from the vice president of Human Resources was calm and reassuring. "My developers tell me that the tool is secure. Just enjoy the convenience of updating your Benefits information electronically."

    Frustrated by this, my friend decided to create another account on his test email server, namely one corresponding to the vice president of Human Resources. He then sent the vice president another email message.

    "Please reconsider your previous decision. Your base salary is $xxx and your wife's name is Yyyy. Would you like me to remind you one week before your son's tenth birthday? It's coming up next month."

    A reply was quickly received. "We're looking into this."

    Shortly thereafter, the tool was taken offline "for maintenance."

    Bonus reading: JenK shares her experience with the same incident.

  • The Old New Thing

    What if two programs did this? Practical exam

    • 41 Comments

    A customer who doesn't read this blog had the following question:

    The requirement for our application is that it must be on top of all other windows, even in the presence of other windows with the topmost style. In our WM_KILLFOCUS and WM_PAINT messages, we set ourselves to topmost, and then call SetWindowPos to bring ourselves to the front, and then we SetForegroundWindow ourselves just to make sure.

    The result is that our application and another application end up fighting back and forth because both applications are applying similar logic to remain on top. There are other applications that defeat our logic, and they manage to cover our application. (These other applications are third party applications we have no control over.)

    We are in the process of writing a DLL to hook all messages for all windows and [even crazier things that aren't worth elaborating on], but at the moment this causes the other hooked applciations to crash. We are also considering setting a timer and calling SetWindowPos when the timer fires, but that seems inefficient.

    The customer didn't even have to run through the thought experiment What if two programs did this?; they got to run it in real life! And the result was as predicted: The two programs fight with each other to be the one that is on top of the other. Nobody wins; everybody loses, especially the user.

  • The Old New Thing

    How full does a hard drive have to get before Explorer will start getting concerned?

    • 41 Comments

    The answer depends on which "hard drive almost full" warning you're talking about.

    Note that these boundaries are merely the current implementation (up until Windows 7). Future versions of Windows reserve the right to change the thresholds. The information provided is for entertainment purposes only.

    The thermometer under the drive icon in My Computer uses a very simple algorithm: A drive is drawn in the warning state when it is 90% full.

    The low disk space warning balloon is more complicated. The simplified version is that it warns of low disk space on drives bigger than about 3GB when free disk space drops below 200MB. The warnings become more urgent when free disk space drops below 80MB, 50MB, and finally 1MB. (For drives smaller than 3GB, the rules are different, but nobody—to within experimental error—has hard drives that small anyway, so it's pretty much dead code now.)

    These thresholds cannot be customized, but at least you can turn off the low disk space balloons.

  • The Old New Thing

    Is there any vendor bias in the way the Start menu determines which programs are most frequently used?

    • 39 Comments

    Chrissy wants to know if there is a bias towards Microsoft products in the selection of most frequently used programs on the Start menu.

    The only bias is in the initial default MFU list, the one that appears upon a fresh login. In Windows XP, the default Start menu MFU contains six slots. The first three point to Windows applications, and the second three point to programs chosen by the OEM. (If the OEM chooses not to take advantage of this feature, or if this is a boxed version of the product, then the second three slots also point to Windows applications.) Which specific three (or six) programs get displayed depend on the system configuration, so it's not like there's a single initial Start menu that applies to everyone.

    Once those initial MFU items are selected, the Start menu algorithm proceeds in a vendor-blind manner. (Indeed, it doesn't even know who the vendor is; no part of the algorithm looks at file version information.)

    The precise algorithm that is used for determining which programs go on the MFU over time has been reviewed by government-appointed regulators, who have not raised any concerns over vendor bias.

    So I hate to say it, Chrissy, but I think it's all in your head.

Page 1 of 3 (24 items) 123