Why .shared sections are a security hole


Many people will recommend using shared data sections as a way to share data between multiple instances of an application. This sounds like a great idea, but in fact it's a security hole.

Proper shared memory objects created by the CreateFileMapping function can be secured. They have security descriptors that let you specify which users are allowed to have what level of access. By contrast, anybody who loads your EXE or DLL gets access to your shared memory section.

Allow me to demonstrate with an intentionally insecure program.

Take the scratch program and make the following changes:

#pragma comment(linker, "/SECTION:.shared,RWS")
#pragma data_seg(".shared")
int g_iShared = 0;
#pragma data_seg()

void CALLBACK TimerProc(HWND hwnd, UINT, UINT_PTR, DWORD)
{
  int iNew = g_iShared + 1;
  if (iNew == 10) iNew = 0;
  g_iShared = iNew;
  InvalidateRect(hwnd, NULL, TRUE);
}

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
    SetTimer(hwnd, 1, 1000, TimerProc);
    return TRUE;
}

void
PaintContent(HWND hwnd, PAINTSTRUCT *pps)
{
  TCHAR sz[2];
  wsprintf(sz, TEXT("%d"), g_iShared);
  TextOut(pps->hdc, 0, 0, sz, 1);
}

Go ahead and run this program. It counts from 0 to 9 over and over again. Since the TimerProc function never lets g_iShared go above 9, the wsprintf is safe from buffer overflow.

Or is it?

Run this program. Then use the runas utility to run a second copy of this program under a different user. For extra fun, make one of the users an administrator and another a non-administrator.

Notice that the counter counts up at double speed. That's to be expected since the counter is shared.

Okay, now close one of the copies and relaunch it under a debugger. (It's more fun if you let the administrator's copy run free and run the non-administrator's copy run under a debugger.) Let both programs run, then break into the debugger and change the value of the variable g_iShared to something really big, say, 1000000.

Now, depending on how intrusive your debugger is, you might or might not see the crash. Some debuggers are "helpful" and "unshare" shared memory sections when you change their values from the debugger. Helpful for debugging (maybe), bad for my demonstration (definitely).

Here's how I did it with the built-in ntsd debugger. I opened a command prompt, which runs as myself (and I am not an administrator). I then used the runas utility to run the scratch program as administrator. It is the administrator's copy of the scratch program that I'm going to cause to crash even though I am just a boring normal non-administrative user.

From the normal command prompt, I typed "ntsd scratch" to run the scratch program under the debugger. From the debugger prompt, I typed "u TimerProc" to disassemble the TimerProc function, looking for

01001143 a300300001       mov     [scratch!g_iShared (01003000)],eax

(note: your numbers may differ). I then typed "g 1001143" to instruct the debugger to execute normally until that instruction is reached. When the debugger broke, I typed "r eax=12341234;t" to change the value of the eax register to 0x12341324 and then trace one instruction. That one-instruction trace wrote the out-of-range value into shared memory, and one second later, the administrator version of the program crashed with a buffer overflow.

What happened?

Since the memory is shared, all running copies of the scratch program have access to it. ALl I did was use the debugger to run a copy of the scratch program and change the value of the shared memory variable. Since the variable is shared, the value also changes in the administrator's copy of the program, which then causes the wsprintf buffer to overflow, thereby crashing the administrator's copy of the program.

A denial of service is bad enough, but you can really do fun things if a program keeps anything of value in shared memory. If there is a pointer, you can corrupt the pointer. If there is a string, you can remove the null terminator and cause it to become "impossibly" long, resulting in a potential buffer overflow if somebody copies it without checking the length.

And if there is a C++ object with a vtable, then you have just hit the mother lode! What you do is redirect the vtable to a bogus vtable (which you construct in the shared memory section), and put a function pointer entry in that vtable that points into some code that you generated (also into the shared memory section) that takes over the machine. (If NX is enabled, then the attack is much harder but still possible in principle.)

Even if you can't trigger a buffer overflow by messing with variables in shared memory, you can still cause the program to behave erratically. Just scribbling random numbers all over the shared memory section will certainly induce "interesting" behavior in the program under attack.

Moral of the story: Avoid shared memory sections. Since you can't attach an ACL to the section, anybody who can load your EXE or DLL can modify your variables and cause havoc in another instance of the program that is running at a higher security level.

Comments (30)
  1. Could this be prevented through some compiler cleverness? Maybe some switch that does something to the section? Not an ACL certainly, but something else?

    I’d wager there’s a lot of this going on out there.

  2. Chris Walker says:

    It used to be fairly common for DLLs to share memory across processes this way under Win31 and Win9x. For compatibility reasons, this is still allowed, but there are much better ways to do interprocess communications under Windows NT/2000/XP/Server 2003.

    Easy enough to find these using the linker to dump out the headers and look for the shared sections. These sections are invisible to the objdir or winobj program, so these tools won’t help you.

    I once saw a service that exposed a shared section like this with a function pointer in it. Most of the page was completely unused which allowed for all sorts of exploit code to be added.

    Even if you use named memory sections you will probably have a hard time parsing the data correctly because the memory can change out from under you (assume a hacker is changing it). Most code I’ve seen even if they are doing parameter checking never think the values will change. The program has to capture the data and validate the captured version to be safe.

  3. Jack Mathews says:

    Do shared memory sections work through Terminal Services session boundaries as well?

  4. Since you can’t attach an ACL to the section, anybody who can load your EXE or DLL can modify your variables

    But surely you can just attach an ACL to the EXE or DLL (on disk), can’t you?

  5. Cooney says:

    Cesar:

    But surely you can just attach an ACL to the EXE or DLL (on disk), can’t you?

    Sure can. Of course, that means that you need a different executable for each user (and a different name, too. This doesn’t scale, so why not just use the right method.

  6. James says:

    Of course, use of shared sections can’t be disabled due to backwards compatibility concerns.

    Why can’t the platform load separate shared sections for separate users, or if that’s impossible, fail to load an image containing a shared section if another user has it in memory already?

    I’d wager that nearly all applications using the technique were written without consideration for the security implications, and say that the remainder are worth breaking.

  7. Raymond Chen says:

    "Could this be prevented through some compiler cleverness?"

    Please elaborate. What compiler cleverness are you proposing?

    James: "Why can’t the platform load separate shared sections for separate users…" – that would be changing the rules after the game has already begun. Whenever you do this there are serious compatibility issues.

    "… the remainder are worth breaking." Tell that to the company whose order tracking system stops working. Tell them that they are losing hundreds of thousands of dollars a day because they "deserved to be broken." See if you make it out of that room alive.

  8. Tony Cox [MS] says:

    Raymond, I thought security fixes were one area where we had decided that it was okay to break apps (especially old busted ones) in order to fix problems.

    Backwards compat is a fine thing, and for most cases I’m definitely on the side of maintaining compatibility, for all the good reasons you’ve talked about in your blogs. However, I think security fixes are one of the things that can trump that.

    I’m not saying that this particular issue necessarily qualifies, but I also don’t think you can automatically exclude possible security improvements on compat grounds.

  9. Eric TF Bat says:

    "Tell that to the company whose order tracking system stops working. Tell them that they are losing hundreds of thousands of dollars a day because they "deserved to be broken." See if you make it out of that room alive."

    James, you’re too nice. Raymond, you’re too optimistic. The solution is simple: use the security hole in the tracking system and get the company to deliver you a thousand iPods. Send them on to a thousand random journalists, but first store a message on them as follows: "These iPods are delivered free-of-charge courtesy of a security hole in [company]’s software that Microsoft has already fixed for them. If you want more goodies, just don’t tell [company] that they have a problem."

  10. Carlos says:

    Ah Tony, but Raymond just likes to complain for complaining’s sake. Maybe no one’s told him about XP SP2 yet and all the horrible breaking of apps that will bring which will cause many a sleepless nights for our favorite blogger.

  11. foxyshadis says:

    I remember that UPX had a fit when you tried to pack writable shared sections, though I was never clear on why. My understanding was that the executable’s shared memory section pointers couldn’t be rewritten though the rest could be. What tended to happen in this case was formerly shared dlls would run in their own process.

    A very unexpected behavior came up after forcing a compress on comdlg32.dll in win95. (Yes, yes, but that was actually one of the less bastardized aspects of my system.) The dialogs worked great and all in an app, but as soon as another app used them, they just failed to appear when going back to the previous app. Which necessitated a huge amount of app restarts and copy-pasting. I’m not sure how, but I actually got used to the behavior. I’m fairly sure it was due to its writable shared sections, but I have no idea how it interacted between the host apps.

    I know I traced some other funky behavior to using a packed shell32 for a while, but I can’t remember what, since I replaced it with 98’s anyway. (Animated scrolling and menus on a 486! Woot.)

    Here there be dragons.

  12. Paul Walker says:

    Providing some way to back out the security fix would seem a reasonable compromise there. People can try it, and if it breaks programs for them, they undo it. They then know they have a problem and can start fixing it, while they still have a working system.

  13. Raymond Chen says:

    Packing EXEs is typically counter-productive. Sure you reduce disk space but you increase file load time (the entire program needs to be uncompressed) and increases memory use (pages cannot be shared and they get spilled into the pagefile instead of being demand-paged).

    Carlos: I’ve seen programs break for much smaller reasons than this. Companies get EXTREMELY angry when a security fix halts their day-to-day business. And if you think *I’m* paranoid I don’t know what you’d call some of the other people who are concerned with compatibility. Compared to them, I’m the crazy one!

  14. Carlos (a different one) says:

    "Do shared memory sections work through Terminal Services session boundaries as well?"

    They don’t.

    I once had to write a program that communicated across Terminal Server sessions and I tried all kinds of things, including this. I ended up using a file mapping.

  15. AlisdairM says:

    Raymond: "Why can’t the platform load separate shared sections for separate users…" – that would be changing the rules after the game has already begun. Whenever you do this there are serious compatibility issues.

    OK, changing rules after the event is nasty, but could you not make this an option for ‘security enhanced’ accounts, and recommend all admin accounts be ‘security enhanced’. I suspect there are several other breaking changes you could put in as part of the security enhancement (if not already rolled into XP SP2) and then the user can choose whether they prefer broken apps or security risks.

  16. Ben Hutchings says:

    Raymond: Why bend over backwards to maintain bug-compatibility for customers that probably won’t upgrade anyway? Didn’t MS just extend workstation/server OS support out to 10 years, acknowledging that many business customers don’t want upgrades?

  17. Ben Hutchings says:

    Raymond: Re UPX, it seems to be somewhat useful for downloadable installers. When I download stuff from Far Eastern OEMs it’s usually packaged as self-extracting executables for Windows which I then need to puzzle out how to extract under Linux. Typically I find that it’s a self-extracting RAR or Zip archive packed with UPX which achieves a little further saving on the code. Thankfully Windows UPX executables can be unpacked by UPX for Linux.

  18. Raymond Chen says:

    Many companies do upgrade Windows but don’t (or can’t) upgrade their applications. Like say the apartment complex that upgraded their OS to Windows XP even though their management software is a DOS app.

    http://weblogs.asp.net/oldnewthing/archive/2004/03/01/82103.aspx

    AlisdairM: Yes, a switch to revert to the old behavior is a possibility. Of course, a company first has to realize that that is the reason why their order tracking system stopped working. For something subtle like this, it may go undetected for a long time before a company realizes that, say, when a shipping address is changed from a domestic address to an international one, the order gets deleted from their system. (Because the domestic and international tracking systems communicate through a shared section, and since the shared section is no longer shared, the international system doesn’t pick up the order.) In the meantime, millions of dollars is lost.

  19. Ah, shared memory versus a secure method like file maps. Several years back I wrote a program that used Windows hooks and posted it on CodeProject.com/CodeGuru.com. The common solution at the time for passing the hook handle was a shared memory section, but I went with file maps. I can’t tell you how many emails I got from people saying I was making it needlessly complex.

  20. Norman Diamond says:

    8/4/2004 11:28 PM Raymond Chen

    > Packing EXEs is typically counter-productive.

    Hmm, I had underestimated the effect of this. In most of my installations of Windows 2000 or XP, I created the partition with the compression option in the first place. The installer and/or Windows itself are smart enough to disable compression on pagefile.sys but not on .exe and other .sys files. I thought the result would be a small waste of time since one uncompression operation would take a small amount of CPU time after achieving no savings in reads from disk (since executables don’t compress much). But if there’s such a big effect, then why don’t the installer and/or Windows itself also default to omitting .exe and .sys files from the compression option?

    > Companies get EXTREMELY angry when a security

    > fix halts their day-to-day business.

    Something is starting to become clear now. When one big corporate customer wants to be 0wned by haxxors, then instead of letting that customer get 0wned, ALL customers get 0wned. In the arguments on security, where some say that Microsoft gets it and some say not, it seems that both sides are right.

  21. Raymond Chen says:

    "When one big corporate customer wants to be 0wned by haxxors".

    No they don’t want to be 0wned either. They want a solution that protects them while simultaneously allowing their programs to continue running.

    Filesystem compression is different – in that case the filesystem can decompress on the fly a randomly-selected chunk of the file. The compression isn’t as good because each 4K chunk must be compressed independently. But it permits random access, which is critical for demand paging.

    This is not the same as packing EXEs, which makes the EXE self-extracting at run time.

  22. AlisdairM says:

    Raymond: Yes, a switch to revert to the old behavior is a possibility. Of course, a company first has to realize that that is the reason why their order tracking system stopped working.

    If we give up the problem as intractible, how will we ever move those customers over to a more secure system? Microsoft has clearly acknowledged this as a concern with the huge focus on security in applications over the last 12 months or so. IIUC, it is now one of the main driver behind the move to .NET.

    By adding such a switch, the more alert and technically knowledgable firms can ‘opt-in’ and get the benefit. They actively need to turn the switch on, but experience is built up and MS get useful feedback. Next OS down the line ‘Secure accounts’ become the default, and it is an option you can switch off. By now the interactions are much better known, and there is a great deal of material published and easily available, on MSDN and elsewhere, about what symptoms to look for, and how to recognise this kind of security problem.

    In the meantime, a lot of the 3rd party vendors these folks are rely on for the building blocks for their systems will in turn have already caught up with the game, as there will be commercial pressure on them to do so from their customers wanting to build secure systems. Likewise, those that adopt early have the commercial advantage of demonstrably improved technology, rather than theoretical. "Look, we can run in secure accounts too."

    In the ideal world we would be able to take the big-bang approach and just say ‘you all have to be secure now’. MS clearly has such a large customer base this attitude cannot work. Oh the perils of success [g]

    I’m still not clear why the phased transition cannot work though.

  23. James says:

    "Tell that to the company whose order tracking system stops working."

    Raymond, I think you have it backwards. Tell 300 million users that you’re not going to make the OS secure, because it would break some company’s order tracking system. Tell them that they are losing hundreds of thousands of pounds a day because a worm has a way to utilise the shared section of Microsoft IntelliPoint’s point32.dll in doing bad things. See if you make it out of that room alive.

    (Okay, point32 was just a guess: it probably uses a windows hook in every process, which means a dll, which means some form of shared data. dumpbin seems to confirm that the DLL has a shared section, but I’ll just trust you that it’s secure. Maybe you can get someone to explain the precautions MS took in this case?)

  24. Raymond Chen says:

    James: Precisely. The challenge is to get out of *both* rooms alive. That’s one of the reasons why security patches take so long to develop. It’s quite a tricky task to close the hole while simultaneously managing to get out of both rooms.

  25. IPv6 says:

    What about XP SP2?

    Will this SP apply additional restrictions for shared sections breaking existing apps or not?

    that is the question :)

  26. This post was inspired by a case I worked recently. In this case, the customer was using the 5.5 Event…

  27. This post was inspired by a case I worked recently. In this case, the customer was using the 5.5 Event

Comments are closed.