Shattering Windows
ChrisPaget writes: "I've just released a paper documenting and exploiting fundamental flaws in the Win32 API. Essentially, they allow you to take control of any window on your desktop, regardless of whether that window is running as you, localsystem, or anywhere in between. The technique has been discussed before, but AFAIK this is the first working exploit. Oh, did I mention it's unfixable?" You may want to read this CNET interview with Microsoft security head Scott Charney to learn even more about "trustworthy computing."
I went to University of Michigan with Scott Charney and he's a really cool guy. A little background info is in order here. I really hope that he improves the Microsoft security record, and I really think he's enough of a go-getter to do just that.
I got a 1600 on the SATs.
You misunderstand. He's talking about NT/2000/XP, where you have privilege and non-privilege accounts, and where even as a non-privilege account, you can have stuff running as the Windows equivalent of "root", and you can use any window that "setuid root" application pops up to root the box yourself.
The example he gave is the anti-virus program that runs with administrator privs (because it has to do stuff to the registry), when you're logged in as Joe User without admin privs. The anti-virus program pops up a window, and bam, you've hijacked the window, given yourself admin privs, made a new administrator login for yourself, and you're away to the races.
The next Cmdr Taco duplicate will be ready soon, but subscribers can beat the rush and see it early!
Before jumping to conclusions, read the reply to the "vulnerabilities" on the BugTraq mailing list here. Doesn't look like its something unknown to the public and its really more of a vendor problem, not MS one.
This is very true. Had they been following the guidelines for proper Win32 application development this would not be an issue. In this case, they should have decoupled the GUI control interface from the service/daemon that must run as LocalSystem and used a secure interprocess communication mechanism.
It's really no different than if I bang out an exploitable daemon that has to run as root under Linux.
"If the user doesn't have any access to these programs (which they probably shouldn't in a truly secure environment) it isn't an issue. Turn off the user component."
You don't understand. The example antivirus program would be running, even if the user component was off. if its running, it can receive messages. if it can recive messages, it can be 'rooted' by the user.
Actually X doesn't care where the clients come from. This is a similar situation to X events. However X11 is more secure because you can tell when an event is synthetic (made by another client) rather than one generated by the trusted server. However, you can't tell which client made the event... there is no ownership or authentication scheme for events. However you can deny clients the ability to connect to the server even if they are on the same system (MIT magic cookies).
It's true that once you're logged into a desktop, all apps that respond to Windows messages within that desktop are vulnerable, there are ways to secure your app from that.
Beginning with Windows 2000 (and possibly later editions of NT4), THE desktop became "Desktops" and they all run in their own space with their own Windows messaging stacks. "Desktops" cannot request window handles from other "Desktops" so this exploit is stopped. All Screensavers will be spawned into their own desktop, so that they can't affect running apps.
All apps can be set to run in their own "Desktop" as well. But it has to be proactively designed that way IN THE PROGRAM. The operation is not by default, and users cannot specify that apps run in their own space. (AFAIK)
Secondly, the documentation for making use of this feature doing so is (as usual) very fuzzy and poorly written.
It is also still possible to develop a ring 0 DLL which can query the existing desktops, get the desktop handles, and then query windows from within them.
This isn't a flaw in the win32 API. This is a flaw in some applications which run under windows.
No to the former; yes to the latter. The reason why this is a critical flaw in the Win32 API is because it means that in order for code to be secure it is essential that every existing piece of software be carefully checked to make sure it separates interface from back-end.
If you're talking about a large enterprise installation with lots of closed-source apps and a vendor turnaround time on security issues which runs into the months--and there are a lot of them out there, make no bones about it--then your boxes are at critical risk. In that case, you're essentially guaranteed to have at least one app an attacker can root the box through. Sure, it may be the app's fault for not separating interface and back-end sanely, like most of us who give a damn about good engineering learned (sometimes the hard way)... but it's also the Win32 API's fault for not requiring separation of interface and back-end [*], it's the Win32 API's fault for being so shoddily designed that an attack like this becomes possible in the first place.
A good analog in the UNIX world is sprintf(), which has been responsible for more stack-smash attacks than probably any other thing. Even though we have snprintf(), most people don't know snprintf() exists or why it should be used. Like the Win32 window exploit, our sprintf() vulnerabilities will continue for as long as people write stupid code. Like the Win32 window exploit, we can't fix the problem by removing sprintf() [**]. All we can do is provide snprintf() and hope and pray that people use it.
sprintf() is a flaw in the UNIX/C API which has led and continues to lead to attacks against the system, facilitated by stupidly-designed software which uses the call even when snprintf() is available. sprintf() can't be removed without breaking literally thousands of stupidly-written apps which depend upon it.
This Win32 attack is a flaw in the Win32 API which has led and continues to lead to attacks against the system, facilitated by stupidly-designed software which doesn't separate interface and back-end. This Win32 attack can't be removed without breaking literally thousands of programs.
Hey, guess what, world? We've found Win32's version of sprintf(). Oh, happy day.
My condolences go out to any security engineers working in Win2K land. You guys have got your work cut out for you minimizing this exploit.
[*] Not that I have any idea how they could do this.
[**] sprintf() is defined in C89. Good luck getting rid of it.
I can't share my work because of bandwidth problems, but it was indeed groundbreaking. Through raw HWND handles or Microsoft's (or my own version) of the WinFinder control found in Spy++, one is able to select any existing window in the system. Arbitrary messages can be sent or posted. One can draw pixels over windows, enable disabled controls (as often found in shareware), send text to edit controls (I developed a simple scripting language to automate this process), right/left/middle/double click at any position (useful in closing annoying dialogs, like the dialup reconnection message for those on 56k), and even move or kill any window one choses.
As for the vulnerability itself...its well worth the read. Basically, shellcode is injected into an edit box by removing the CEdit's length restrictions. This is a valuable lesson to all Windows programmers out there -- do not rely on a control's restrictions to validate data! Its as dangerous as using JavaScript to validate web forms. Always in all cases check for buffer limits in the application code.
Of course, not all flaws can be blamed on the application developer:
I've only skimmed the article, but this is the first documented exploit I've heard of in my half a decade of Windows programming. But its worth mentioning that these fundamental flaws have existed since Windows 95, and Foon is correct in that there's absolutely nothing Microsoft can do about it, except ditch the current Win32 API and start anew. Guess we'll have to wait until Longhorn. -sr
> This "vulnerability" only effects poorly-written applications, running with system priviledges, which create windows in user-space. You're not supposed to do that.
You mean, like antivirus software?
Almost all programs on Windows have an integrated MVC design - I can't think of one that uses your (better) separated M/VC design. This is a flaw in more than "some" applications.
A cursory look through the hook API does not suggest that one can block messages based on the calling process. There appears to be a way to determine if the current thread made the call, but that is largely useless. Threads post inter-thread messages all the time. The real question is whether that thread belongs to the same processes as the receiving window.
The callback function has the following prototype:
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
nCode specifies handling semantics.
wParam specifies whether the message was sent by the current thread.
lParam is a pointer to a CWPSTRUCT structure.
The CWPSTRUCT contains the following:
typedef struct
{
LPARAM lParam;
WPARAM wParam;
UINT message;
HWND hwnd;
} CWPSTRUCT, *PCWPSTRUCT;
There is nothing of use here that would permit a filter to determine the origin of a message. At most it could kill all inter-thread messaging, seriously breaking multi-threaded applications.
-Hope
For example, the virus scan writer probably tests, develops and uses windows while logged into the "administrator" account interactively, as do most users. In Win98, it's not even a question -- you're always the "root" of the box. I'd wager that "What access could a non-administrator user obtain through our GUI/message interface?" hardly occurred to the GUI programmers for this particular virus scan software, even though their interface ran at LocalSystem (geeez).
As mentioned elsewhere, there are ways to avoid these problems: If you really need to use gui elements as administrator, run them under a separate desktop or windowstation where the interactive user can't touch it. Run the GUI interface impersonated down to a lower level, such as that of the interactive user. If you have to be administrator or local system, treat all input as untrusted, including (especially) window messages! Don't use edit box constraints as buffer limits. Don't use the default window procedure. etc etc etc.
It's true, however, that the default behavior for WM_TIMER is a pretty big security flaw (IMHO). But contrary to what this writer inaccurately claimed, you can still intercept the WM_TIMER message and handle it yourself, and any security-conscious program should never pass unexamined WM_TIMER functions to the default window procedure. This makes me start to wonder about other flaws in the default window procedures... as Microsoft wants you to think that the defaults it gives you are safe and secure. hmmmm...
The following sentence is true. The preceding sentence was false.
In the shatter exploit he's linking on his website, there is a virus in the sploit.bin file. It's a W32.Beavuh, and Norton Corporate flagged it immediately. So, surfer beware! It's hard to take security fame-seekers seriously when their code is trojaned.
Under Windows (at least Win2K), look at your Services control and find out how many of them are set to run as LocalSystem. Most to all of them? Ah, yes. Now look at how many of those are built-in Microsoft Windows components. Most to all of them? Ah, yes.
I suppose, by this token, that it's not the fault of the Win32 API, but instead each and every Windows service that Microsoft shipped and each and every third-party service delivered post-install.
Bad programming, indeed.
--
Please do not feed or tease the trolls.
Another problem with this is that windows hooks only see messages Post'ed to the window, not messages sent using SendMessage, so write the exploit using SendMessage.
Another way to see messages is to subclass all windows using SetWindowLongPtr to replace the WndProc, but you would have to do that to every window. I think it is impractical. It also relies on all WndProc functions being written according to the guidlines from MS, which not even MS programmers do.
Many applications actually use Windows messages to synchronize themselves between threads and processes.
In fact, there is also an API called PostThreadMessage that will post any windows message to any win32 application (all you need is the thread handle) with a message pump.
Out of process COM interfaces using windows messages will also be broken by removing this functionality.
Microsoft's excuse that having physical access to a machine is required is abysmal.
Do not spread "09 F9 11 02 9D 74 E3 5B D8 41 56 C5 63 56 88 C0" over the internet, thank you.
it works across terminal server, also, which means you bring in a terminal client and plug in, get access to a user account (think there aren't any giant post-it notes of "my login/password" on half the desks in any given office?), and viola. they have rooted the terminal server and can do whatever the hell they want.
the terminal server may be behind 8 inches of steel and plexi-glass. but this compromise shatters that, too.
MORTAR COMBAT!
Correct me if I'm wrong but I'm pretty sure that PostMessage puts a message on the queue whereas SendMessage skips the queue and gets handled straight by the application without examination
No. Both place messages in the queue, the difference is that PostMessage does not give you the return value of the message (and hence does not block).
I think you misunderstand that. WPARAM is a fixed size double word (4 bytes)
Exactly, but for the message EN_SETLIMIT, this is not a pointer.
AFAIK, if you don't handle the callback feature (99% of apps), you get the default handling which is to execute the code as the article describes
Which is why we would block timer messages with a callback parameter
Why this is only a Unix problem I don't understand, as sprintf is defined in ANSI C, so I would think it'd be a problem on any system with a C compiler.
Hopefully someone more knowledgeable will explain.
Sticking feathers up your butt does not make you a chicken - Tyler Durden
Actually, with careful timing, you might be able to pull this attack off on an app that doesn't have ANY windows. If the application in question makes use of the WM_COPYDATA message, this might prove to be trivial. Even if it isn't, you can still map arbitrary data into an application's memory space using WM_COPYDATA.
Here's the WM_COPYDATA documentation. Read it and tremble in fear.
I'm really, really disgusted that this even got posted. This isn't a Win32 vulnerability, it's a Virusscan vuln. (Watch my karma burn, I'm actually defending MSFT... but hear me out.)
For those of you who aren't familiar with Windows programming, here's what he's doing. Viruscan's GUI is very poorly written and doesn't check for a maximum length on a text box's input. So, he adjusts the size of a textbox using an outside program to 4GB. (Windows unfortunately allows this, since the message format doesn't include a "sender" field to check against the owner handle.) He then inserts shellcode in it, attaches a debugger to the process and searches all of memory for the start of the shellcode. Real efficient, this one.
He then sends it a WM_TIMER message to trigger it. WM_TIMER is usually sent to your window on a regular interval when you've called SetTimer(), and contains either an integral ID or a pointer to a callback in memory. So, he sends it a fake WM_TIMER, and Viruscan executes the callback blindly.
You know what, I use WM_TIMER too in my apps - but, there's two simple ways to defend against it.
if ((void *) msg.lparam != known_cb_address)
{
return false;
}
if (0 != IsBadCodePtr((FARPROC) msg.lparam))
{
go_fuck_yourself();
}
And if I'm not using it, special-case it so that it doesn't fall through to DefWindowProc().
Seriously, all this guy is doing is buffer overflowing a poorly written program to get Administrator privs. That's like claiming that glibc is insecure and should be thrown out because it has sprintf() or gets(). Ya know, I can buffer overflow a poorly written suid app too, but that doesn't make the libc to blame, nor have we published articles lambasting the GNU Project for not putting bounds checking into those functions.
This guy's just trying to sell himself, and you guys were more than helpful. Maybe I should write a system service that subclasses MSIE's WndProc with a single function that calls ExitProcess(1), and see if Slashdot will find me a security job.
Maybe so, but there's more Windows messaging system does (by allowing nifty shortcuts) than what typical suid app would. Read the article for god's sake.
Basically, it has similarities with Outlooks "execute anything on sight when user does something". Messaging system allows code to be executed without app having any way to intercept those calls. And those calls come from lower privileged level.
In this particular case it seems perfectly reasonable for the privileged app to expect that GUI components in question just do their UI stuff, instead of providing an instant code execution path. It is once again that Windows really makes thing easier... similar vulnerabilities potentially affect all/most message based GUIs / OSes, but not as easily as with Windows.
I like paying taxes. With them I buy civilization -- Oliver Wendell Holmes
You missed the point, I'm afraid. Once the data has been copied into the exploited app's address space, nothing the developer does can secure it 100%. The described exploits relies on two properties of the Win32 API:
1) It lets you copy arbitrary data into another process.
2) It lets you force another process to jump to an arbitrary address by faking a WM_TIME message. It's actually the default window procedure that does this.
So, in theory, there should be a certain class of applications that would allow you to inject an exploit into their address space, using WM_COPYDATA, and then jump to that data (from another thread, possibly, introducing the delicate timing issues), and executing it. Note that this can be done before the application code gets a chance to look at the WM_COPYDATA message.
Upon closer reading of the WM_TIMER message documentation, several things come to mind that could make this attack less problematic. The OS could filter all WM_TIMER messages, and discard the ones whose LParam doesn't contain an address that was previously registered as a timer callback.
PostMessage() always puts the message in the queue. SendMessage() tries to bypass the queue, but that only works when sending to the same thread. If you SendMessage() to a different thread (or process obviously), the message goes in the queue and the function doesn't return until the target thread handles it, so SendMessage() could be called PostMessageWait() or something, but it's historical (in Win16 it really did always make a direct call).
The writer's statement is somewhat incorrect about this. The WM_TIMER message does go through the queue, but windows apps don't generally examine messages at the queue-read level. They just use an API call (GetMessage()) to pop the next message (and ignore its contents), and another API call (DispatchMessage()) to dispatch it to the proper handler function, where all the real work gets done. In the case of WM_TIMER, DispatchMessage() is what decides what function to call (based on the second message parameter). An app could technically block it by making sure the second parameter is a valid timer handler function.
I'm not sure how you determined this, but on my computer the "New User" window on my system is put up by mmc.exe, which is being run by me.
I verified this using Spy++ (from Visual C++) and Proccess Explorer (www.sysinternals.com).
One more commenter who didn't even read the article aren't you? The exploit doesn't require app to blindly trust the user. App unfortunately does trust Windows API not to do stupid stunts like allowing certain messages (that may or may not originate from another app -- there's no way to tell either way!) to get to execute stuff without app having a clue as to what hit it. It's like opening a socket for doing basic network communication and Windows API allowing certain pre-determined 'helper' messages to be handled by OS before your app has any say to handling.
You are of course right about UI separation part -- as long as Microsoft really has made it totally clear that's what has to be done, for the security reasons article explains.
And as to needing a local user... yes. It's not a perfect remote hack. But that hardly invalidates the claim this is a serious issue, esp. for certain applications.
I like paying taxes. With them I buy civilization -- Oliver Wendell Holmes
The point remains that it isnt a question of "a user runs unknown code, they're screwed" - in this scenario the USER is the attacker.. they already have a legit account but it doesnt have administrator privs. They want to get past some restriction on their account - like maybe locate and disable any nasty corporate keyloggers that might get them fired for pr0n-mining, or plant some nasty stuff on a shared PC to grab other accounts credentials so somebody ELSE gets fired for it? Lots of attacks come from inside and lots of *nix attacks are described as "local root" compromises - thats what we have here.
To rephrase your statement its more a question of "if a user can get localsystem privs by running arbitrary code, you (as the sysadmin) are screwed."
This isnt specific to windows or any other OS for that matter. If any user can get arbitrary code to run with a higher privilege level than their own, this kind of hole exists.
I had a
use a pipe. Oh wait, thats only available in linux...
Err, pipes have been in Windows since NT 3.1, in August '93.
Tarsnap: Online backups for the truly paranoid
23 languages on 14 platforms? That's odd. As recently as 6 July 2001 Mr. Paget was posting a position-wanted ad on SecurityFocus, describing his language/platform knowledge as follows:
One can only wonder which 23 languages and 14 platforms he knows, if several of the most important ones are notable by their absence or explicit disclaimer. Of course, he never tires of telling us he's a fast learner. Maybe he has filled in some of those gaping holes in his basic computer knowledge in the last year and a bit.
Slashdot - News for Herds. Stuff that Splatters.
You know, I remember when you could do all sorts of fun stuff with X11. For example, you could layer a transparent window on top of a display, that passed keypresses and mouse events to the window beneath it - and capture everything the user did. You see, most people used xhost for security - which meant that you gave control of your display to anyone who had access to the machine your X client application was running on.
Due to this, we now have xauth and MIT Magic Cookie. Anyone who says a security hole "can't" be fixed is naive - even if the fix is a kludge. MIT Magic Cookie is easily snoopable, so that's another security problem. The X11 protocol itself is easily intercepted, so we have to tunnel over SSH.
I could go on, but I've made my point. Linux users who take it on faith that they are secure are sadly misguided - as are those who believe that Windows is inherently less secure. Ultimately, it comes down to the skill of the sysadmin to secure any OS.