Shattering Windows 965
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."
Scott Charney (Score:1, Informative)
Re:Ummm... 'Kay (Score:5, Informative)
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.
Read the BugTraq replies first (Score:4, Informative)
you're totally missing the point (Score:2, Informative)
Re:This is not new (Score:1, Informative)
It's really no different than if I bang out an exploitable daemon that has to run as root under Linux.
window messages a privilege boundary? (Score:5, Informative)
An application that needs to provide a window interface to the user should do so through another process running as the user and not system. That process should then communicate to the higher privileged process using a mechanism such as COM or named pipes.
A similar problem was found by Dildog quite a while ago, "NetDDE Message Vulnerability":a dvisories/2001/a0 20501-1.txt
http://www.atstake.com/research/
The method of exploit described in Shatter is new: putting shellcode into memory through edit controls and then using timer messages. But the fundemental problem is not a new class of problem. It is with the application's design not windows.
-weld
Re:Ummm... 'Kay (Score:2, Informative)
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.
Re:Jeez (Score:1, Informative)
Microsoft has done something about it... (Score:2, Informative)
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.
Re:Don't Do That (Score:5, Informative)
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.
Window "shattering" utilities (Score:3, Informative)
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++ [microsoft.com], 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
Re:Don't Do That (Score:3, Informative)
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.
Re:Security from the ground up. (Score:5, Informative)
Or perhaps we can come to understand that some APIs have limits and not use them in sensitive situations. If you read the MS reply, he's saying that they recommend that a system service should not interact with the desktop, and recommends they take this up with Mcafee since they wrote Viruscan. The SecurityFocus article [securityfocus.com] that MS referenced shows that when this type of exploit was found in a MS service, they fixed it.
Re:Fixability (Score:3, Informative)
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
Re:Ummm... 'Kay (Score:2, Informative)
The problem is developers not used to multi-user (Score:3, Informative)
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...
Virus in his code (Score:3, Informative)
Oh really? (Score:2, Informative)
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.
Re:Fixability (Score:1, Informative)
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.
Won't work because of multi threaded/process apps (Score:4, Informative)
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.
not just physical access (Score:4, Informative)
the terminal server may be behind 8 inches of steel and plexi-glass. but this compromise shatters that, too.
Re:Fixability (Score:3, Informative)
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
sprintf (Score:3, Informative)
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.
Re:Ummm... 'Kay (Score:4, Informative)
Here's the WM_COPYDATA documentation. Read it and tremble in fear. [microsoft.com]
Re:Security from the ground up. (Score:5, Informative)
The paper indicates that there are a lot of 'windowless' services running as localSystem that the program is able to exploit, and these services are Microsoft-created.
VirusScan was used in this example because it's easy.
What a load of tripe. (Score:5, Informative)
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 [tombom.co.uk], 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.
Re:Security from the ground up. (Score:3, Informative)
I can't imagine enybody suggesting that only Microsoft signed Excel scripts will be allowed to run, for example.
Re:Virus in his code (Score:2, Informative)
Re:Ummm... 'Kay (Score:3, Informative)
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.
Re:window messages a privilege boundary? (Score:3, Informative)
Re:sprintf (Score:3, Informative)
As the original poster said, snprintf is good, sprintf bad.
"Run As" is your friend. (Score:3, Informative)
It works with poorly coded proggies like Palm Desktop. Palm Desktop wants you to be the Admin when it runs. Nine times out of ten "run as" works in cases like these. However, with Nero Burning Rom, it won't work. You have to log in as Admin for that puppy to run right. SuxRoxio also requires this. NTI Professional CD Maker's new version totally eliminates the need to be Admin to burn CDs, which is very cool.
I never run as Admin if I don't have to.
Re:Ummm... 'Kay (Score:4, Informative)
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.
Re:Fixability (Score:2, Informative)
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.
Re:Yes, but who's fault is it? Not MS'! (Score:3, Informative)
I verified this using Spy++ (from Visual C++) and Proccess Explorer (www.sysinternals.com).
Re:Yes, but who's fault is it? Not MS'! (Score:4, Informative)
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.
Re:What a load of tripe. (Score:4, Informative)
This isn't a Win32 vulnerability, it's a Virusscan vuln
It is a Win32 vulnerability in that it is even possible to have this kind of elevation of priveleges by "poorly written" services. Basically, any system service that interacts with the desktop is vulnerable (virus scanners). McAfee interacts with the desktop. This is bad too.
Viruscan's GUI is very poorly written and doesn't check for a maximum length on a text box's input
It does, it has a set a maxlength of 4. Unfortunately, the vulnerability allows this to be bypassed. I'm sure the GUI will complain when you press the OK button but since we never get that far, the app never gets a chance to check the length change.
He then sends it a WM_TIMER message to trigger it... but, there's two simple ways to defend against it
That's good practice but the real problem is where you comment yourself... And if I'm not using it, special-case it so that it doesn't fall through to DefWindowProc(). It's too much to ask any app that doesn't use WM_TIMER to put in this handler in all their apps. Further, I bet WM_TIMER isn't the only message he could use to trigger code.
Seriously, all this guy is doing is buffer overflowing a poorly written program to get Administrator privs
Not any poorly written program but all desktop interactive services in existance.
Ya know, I can buffer overflow a poorly written suid app too
Fortunately, the community tends to be able to fix these things by patching the source. There is no fix for this problem
This guy's just trying to sell himself
OK, I will agree with you here... a little
Maybe I should write a system service that subclasses MSIE's WndProc
No no no... you've cheated. As a system service, you already have priveleges. The exercise of this paper was to do with with Guest priveleges
Regards,
Re:Ummm... 'Kay (Score:5, Informative)
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.
Re:What a load of tripe. (Score:4, Informative)
The problem is that the vast majority of apps out there look like this, mainly since Microsoft uses this structure in their own books and help files:
switch(message_type)
{
case WM_CREATE:
case WM_LBUTTONDOWN:
etc...
default:
return DefWindowProc(hwnd, iMsgType, lParam, wParam);
}
Microsoft provides a moderately reasonable default procedure to fall out to, so that programmers don't have to write a case for every single one of the myriad standard messages.
The problem is that DefWindowProc doesn't check the validity of a code pointer before executing it, it just automatically jumps to any function specified in a WM_TIMER message.
So, without changing that function, any program that doesn't explicitly check for WM_TIMER is vulnerable... if there is a way to insert shellcode into the process' memory space. The exploit in the article can do it because Viruscan doesn't sanity-check their text boxes, so he can paste up to 4GB of material into the box, and tada.
Re:What a load of tripe. (Score:3, Informative)
By the same token, is it a UNIX or Linux vulnerability if I run an insecure daemon suid root and someone buffer overflows it to get root privs?
It does, it has a set a maxlength of 4. Unfortunately, the vulnerability allows this to be bypassed. I'm sure the GUI will complain when you press the OK button but since we never get that far, the app never gets a chance to check the length change.
I'd disagree with you there. Viruscan's assuming that nobody will fuck with its code (the worst mistake you can make when dealing with crackers) and that it'll never get more than 4 characters. Maybe it's just me, but I automatically check length whenever I do ANYTHING with a buffer, and usually I make a quick call to IsBadWritePtr(buf, len) or IsBadCodePtr(callbk) while I'm at it.
Yes, technically you can't prevent him from changing the text box's maxlength, short of subclassing the text box with a custom WndProc after initializing that discards said messages. But that doesn't mean you can't write your code in a robust manner.
It's too much to ask any app that doesn't use WM_TIMER to put in this handler in all their apps. Further, I bet WM_TIMER isn't the only message he could use to trigger code.
I grant you that - MSFT fucked up by writing DefWindowProc() to blindly execute a callback. However, while tricky, I don't think it's fair to say that it's an unfixable problem, or that this is any different from some of the assumptions made in glibc's functions.
Fortunately, the community tends to be able to fix these things by patching the source. There is no fix for this problem.
Yep.
No no no... you've cheated. As a system service, you already have priveleges
I was being sarcastic by that point. ^_^
Re:This does not seem to be that much of a problem (Score:2, Informative)
There are UNIX versions that are fully trustworthy to the extent you can't even copy and paste between windows on a single desktop who have different security classifications. In other words there is much more security than traditional X.
SGI has a fully B1 certified UNIX which offers this level of security. I don't know about other Unix's. And unlike microsoft's supposed certification of NT - this certification allows the system to be connected to a network.
White paper on trusted irix, security labels etc [sgi.com] - good read if you want an introduction to this stuff - it is SGI slanted of course but is still general enough to give you the idea.
There is no root user on a B1 level system. Unix does this today and a few unix vendors have been shipping it for ages. Microsoft doesn't actually really care about security compartively speaking.
(BTW users don't have much fun using a B1 system but I guess if you need B1 security you aren't about to play quake or anything).
Re:What a load of tripe. (Score:2, Informative)
Re:Don't Do That (Score:4, Informative)
Err, pipes have been in Windows since NT 3.1, in August '93.
Re:High opinion (Score:3, Informative)
23 languages on 14 platforms? That's odd. As recently as 6 July 2001 Mr. Paget was posting a position-wanted ad [securityfocus.com] 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.
Pot, meet kettle (Score:4, Informative)
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.