Well, It’s now two weeks since the release of Shatter, and my inbox has finally started calming down. I’ve tried to reply to most of the messages I’ve received, but I’m getting a lot of the same things said over and over and over again, and there’s a fair amount of FUD going round about this thing. So, I’ve written this as a followup, to try and answer some of the more common questions I’m being asked, and to present some new Shatter techniques that I’ve been working on (often at the suggestion of others). Corrections are immediately below, followed by some new techniques, and then finally some explanations to common questions I’m being asked.
I have working code to substantiate most of what I say in here, but don’t expect me to release ANY of it. Don’t even ask. I only wrote the code because a) I wanted to prove to my own satisfaction whether or not these things are possible, and b) because I’m a professional security consultant. Anything that escalates privileges is useful to me, so I’m using these tools for my job. When I say that something works you’ll just have to either take my word for it or write some code yourself to verify it. None of my code after Shatter is ever going to be released. Please don’t waste my time and yours by asking for it.
The information contained in this followup is just the tip of the iceberg, believe me. Every time I work on Smashing (my own exploit code) I think of new techniques to try, new code injection mechanisms, and new services to exploit. These things are everywhere. There’s a lot of ways to exploit them too – don’t believe for a second that simply filtering WM_TIMER is going to fix this. It’s not. One chap wrote an application to try and prove that message filtering was the solution to Shatter attacks. He successfully filtered WM_TIMER messages for the edit control in his test app, but forgot to filter the messages for the parent window. It took me less time to shatter his application than it did to write out an email explaining to him how to repeat my results. Smashing is now a generic exploit mechanism for these attacks, and adding new examples is trivial. I’m willing to bet that there are dozens of people around the world writing similar applications. My point has been proved – these vulnerabilities are everywhere, and they’re going to keep on biting anyone trying to keep a system secure. Internet cafes beware.
A few things I got a little bit wrong…:)
1. WM_TIMER DOES get placed in the message queue. Yes, the application has the choice of whether or not to process the message. The problem is that unless a new handler for WM_TIMER has been installed, it’s caught by DefWindowProc(), which conveniently jumps to the callback address, if specified. Yes, you can filter it out. No, that won’t solve the problem (read on for why).
2. X11. The information I was given on X11 was a tad incomplete. Controls in X11 may or may not be windows in their own right, but either way they don’t support the same kind of “do this” messages that Windows does (if I understand what people have been saying). Also, they have a flag to say whether they’re real or “synthetic” messages, ie generated by real input or sent by another program. Win32 lacks this. X11 has its own set of issues, but it doesn’t suffer from these same attacks in the same way as Win32. If you want to know more about how X11 works and what security measures are built in to make things better, I suggest you read this. Please don’t ask me about X11 – I really don’t know it in enough detail to answer most of the questions you have…
3. It ain’t new. No, these are not revolutionary new ideas. A lot of these have been discussed before. However, for every email I’ve received claiming that these are well-known and well-documented ideas, I’ve received 20 from people who had no idea about them. New they may not be, but hitherto unkown to the general community they most definitely ARE.
4. It’s unfixable. Well, kinda. The problem, fundamentally, is the Win32 API (People are gonna disagree with that one, I know, but trust me – I’ll shoot down those arguments in a few paragraphs), much like the root cause of buffer overflows is the way that functions like strcat() and sprintf() work in the standard C library. Since the Win32 API can’t really be changed without breaking a LOT of stuff, people could (and will have to) be aware of these things and work around them. It’s never going to go away though, much like buffer overflows are still commonplace after they’ve been VERY well known and VERY widely documented for years. The root problem cannot be fixed, but a lot of the symptoms can be if developers put in extra work. Personally, I believe that the blame should ultimately lie with Microsoft; they designed Windows so that it was easy to use, easy to code for, and (as a consequence) easy to break into. Disagree if you will, but you won’t change my mind on that one.
I’ve been in two minds as to whether or not to release a lot of this information. But, I figured that a) most people regard local security on Windows as utterly shite anyway, b) a lot of this stuff was suggested by other people on the internet, so people are already thinking along similar lines, and c) I’m getting hacked off waiting for CERT and Microsoft to respond to my emails. The whole US-UK time difference means we exchange one email every 24 hours, at best. In the case of MS, that’s 24 hours for them to stall for time by saying “Please give us more detail”, and in the case of CERT that’s 24 hours for them to say, well, nothing at all. So here it is – enjoy.
LocalSystem desktop windows on a default installation of Win2K
Yes, it’s true. Microsoft break their own rules on this score. The general consensus from the readers of Bugtraq / NTBugtraq / Slashdot / god knows where else, as well as from Microsoft, is that if you place a window on the desktop as LocalSystem, you’re begging for trouble. Well, I guess Microsoft must be begging for trouble – I’ve found two that are normally there, and a third you can create.
1 – NetDDE. Regular DDE runs within explorer.exe as whoever is logged on at the time, however the network flavour has a window on the desktop which is running within winlogon.exe. What’s that you say? Winlogon is a critical system process? Why yes! Critical system processes having windows on the desktop is a really clever thing to do! Wonderful!
2 – “MM Notify Callback” Quite frankly, I don’t actually have the faintest clue WHAT this window does. Quite frankly, I don’t care either. What I do care about, is that it’s owned by winlogon.exe. What’s that you say? Winlogon is a critical system process? Why yes! More loveliness!
3 – Messenger service (discovered by Georgi Guninski). On a default installation of Win2K pro, drop to a command prompt and type “net send 127.0.0.1 hello“. A neat little window pops up that says who the message was from, at what time, yadda yadda yadda. Unfortunately, that window is owned by the Client-Server Runtime Sub-System (great acronym), AKA csrss.exe. Guess what? It’s another critical system process, with another localsystem window on the desktop.
No localsystem desktop windows? No problem!
Well actually, you really don’t need a window handle to a LocalSystem process. As Michael Durig kindly pointed out, the Win32 API has a friendly little function called PostThreadMessage(). Enumerating processes is not a privileged operation, so you can get a PID for a localsystem process with no problems. The ToolHelp functions allow you to also enumerate the threads for that process (Under Win2k and XP, anyway), and even if you’re stuck under NT4 the performance database does the same thing. So, you have a list of all threads running as LocalSystem on the machine. PostThreadMessage will send any message you like to any thread you like, assuming that thread has implemented a message queue. No, you don’t have to call OpenProcess(). No, there are no privileges involved.
Yes, I know what you’re thinking, because I thought the same thing. “You’re trying to tell me that a non-privileged user can tell winlogon.exe to jump to an arbitrary location in memory?”. Yes folks, I am. You can. It works. Send enough crap WM_TIMER messages to any system process through PostThreadMessage, and eventually the structured exception handling (SEH) will fall over, the process will die, and the machine will bluescreen. Injecting code is a little more tricky, but I’ve certainly got it working (No, I will NOT give out my code so don’t even ask) and I’m sure other people will too. Anyone still have any arguments that Shatter attacks are the fault of the people who put localsystem windows on the desktop? Anyone think of a way to not have any LocalSystem message queues on the system? Theoretically, it’s potentially possible. I doubt it though. Looks like Microsoft are going to have to extend the Win32 API, and recode several very critical system processes. Fun fun fun.
There’s a few new ideas here as well, for ways to get your code into your target process. Some are easier than others, but all (in principle) work.
SetWindowText() This function sets the caption for any window on the system. Or the contents of any edit box. Or the title for any static labels. Or the label on any button. You get the idea? Binary code is absolutely allowed, and you get a LOT of space to play with. Smashing uses SetWindowText() to inject half a megabyte of NOPs before my shellcode; as far as I can tell, the only limit is the stack size of the receiving process. Pass in a meg of NOPs and you get a stack overflow, although there may even be a way around that. This technique does have a few drawbacks, but nothing that can’t be fixed with a bit of clever assembler. Like I said, the first version of Smashing was based around SetWindowText – it works reliably.
GlobalAlloc() Peter Arnold suggested using GlobalAlloc (specifying GMEM_FIXED) to grab a chunk of memory at a fixed location for all processes, filling it with your shellcode, and specifying that as your callback address within WM_TIMER. I’ve not had time to research this, but MSDN seems to support the idea in theory. While it clearly states that “Windows memory management does not provide a separate local heap and global heap, as 16-bit Windows does.” supporting the theory of using shared heap-space to inject shellcode, it also says that “Memory objects allocated by GlobalAlloc and LocalAlloc are in private, committed pages with read/write access that cannot be accessed by other processes”. Might work, might not – I’ll try it when I get time and update this page.
Other buffers As Kristian Koehntopp pointed out, you just have to get your code into the address space of the process – any kind of file buffer, I/O buffer or network buffer will do. Connect to a named pipe / network socket / whatever, send your shellcode, and then send the callback before you get kicked off. Should work nicely, assuming you don’t kill the process with an SEH overload before you find your shellcode…
Well, that’s all for the moment on new techniques. Like I said, a sizeable chunk of this stuff has come from people who read the original paper and are thinking along similar lines; I see no problem in telling people what others are already thinking about / working on / exploiting. After all, local security on Windows is an oxymoron, isn’t it?
This is not a Microsoft problem, it’s a VirusScan problem.
Technically, it’s both. It’s a VirusScan problem which exposes a Windows problem. To draw an analogy, consider buffer overflows. The standard C library contains some functions that an attacker can use to overflow a buffer, such as sprintf() and strcat(). These functions are flawed, in that they do not sanity-check their input – that’s left to the programmer. If the programmer (either through laziness or ignorance) doesn’t do this, then the resulting program will be vulnerable due to the flaws in the underlying library. This is the same thing. The poorly-controlled privileges on the VirusScan admin tool expose a flaw in the underlying Win32 API.
So you can escalate your privileges if you’re sat in front of the box. Big deal. Physical access == hacked box
Physical access is NOT required, just a desktop. Terminal Services or Citrix both work perfectly, so ASPs based on either of those are in trouble.
What about Window Stations and multiple desktops?
Irrelevant. The whole point is that you have a requirement for an admin tool running on the desktop. If you want your users to interact with that admin tool, then it has to be on THEIR desktop within THEIR window station, considering that enumerating and opening desktops / window stations is a privileged operation.
What if there aren’t any applications running as Localsystem on the desktop?
There are. There’s numerous hidden windows on a standard desktop (such as the NetDDE server) which run as Localsystem. Microsoft break their own rules here – you just can’t see the windows concerned. Shatter can talk to them though – if you enumerate the windows you’ll see a whole load of stuff that isn’t actually visible, some of which runs as LocalSystem. Failing that, just use PostThreadMessage to get the same effect.
What about filtering messages as they go into the queue?
So this is a privilege escalation attack, right?
No – it’s far more than that. It allows you to take control of another process on the desktop, and do what you like with it. Let’s say you have a personal firewall that only allows Internet Explorer to access the network. Some malicious code shatters its way into IE, and gets IE to do the work for it. As far as the firewall is concerned, it’s IE accessing the network, which is allowed. Your firewall doesn’t know that you’ve contracted a virus that has infected IE; it only knows that IE is accessing the network, which is allowed. Hell, the virus could shatter its way into your firewall admin tool as well, and allow itself to access the internet. If you have any kind of software that controls access based on its own user database, you can shatter your way into that too, and just enable the buttons that are greyed out if you don’t have the privs. The possibilities are endless.
You say this is unfixable. So why publish it?
This is more or less unfixable by microsoft. To fix all of these problems in one fell swoop would require a change in the Win32 API, which you can’t do. However, the vendors can fix each specific problem as it arises, and write code that’s inherently secure against these types of attack. Hence publishing the paper – if people know that you can do this, then people will know to code around it. NAI certainly didn’t know about it, regardless of how well Microsoft claim to have documented it.
What about Macintosh / Mac OS X / VAX/VMS / GEM / Bob’s friendly windowing system? Is that vulnerable too?
No idea, but possibly. If it allows you to control other applications through unprivileged, un-authenticated messages, then quite possibly. You’d have to check it. I neither like nor use Mac OS, so please don’t ask me about it…
I saw your paper on *website* but there was no Shatter zipfile. Where can I get it?
A few websites have mirrored the original paper without mirroring the zipfile as well. The original paper is at http://security.tombom.co.uk/shatter.html and the Shatter zipfile is linked from there.
Do you have a PGP key / Hushmail address / other preferred encryption method?
Your site coped remarkably well with a slashdotting. Where’s it hosted?
So I have a sense of humour. Sue me 🙂