Just picked up this link from Robert Hensing's blog - http://www.builderau.com.au/news/soa/Vista-security-to-be-obliterated-at-Black-Hat/0,339028227,339290040,00.htm. Seems Mark Dowd is going to be doing a presentation on how to bypass some of the defenses used by programs. I wrote the section on that for Writing Secure Code for Windows Vista, and I'm curious to see what he's come up with.
First of all, I'm going to paraphrase Jon Pincus again – there are 2 properties of a countermeasure that are important:
- Any effective countermeasure will stop some exploits completely
- For any given countermeasure, a sufficiently complex attack may be able to evade the countermeasure
This was the topic of a somewhat infamous exchange between myself and Gary McGraw some years ago. Gary found a way to evade an early version of /GS, and we hadn't claimed it was perfect, just better.
Let's review what I know about these defenses:
ASLR – there is a limited amount of randomness in where things show up in memory – only 8 bits. In some cases, you can get 16 bits because you might have 2 things moving independently (e.g., where the stack is and where a DLL loads). Problems you can run into include information leaks, or crashes that tell you things about where DLLs are in memory. Once a DLL is loaded, it stays in the same place. For DLLs used by a lot of apps, this could persist until you reboot. Thus an information leak in one app might help refine an attack against another. It is also true that a poorly implemented exception handler could make ASLR ineffective by allowing as many attacks as you need. This was seen in the .ani exploit. I've written about this problem previously here. ASLR also may not be applied to all DLLs, and that gives you a non-moving target. Additionally, if the exploit mechanics allow it, you might be able to code something that depends on an offset rather than finding a fixed address. For example, if you can jump to a predictable offset from where you are to something that calls the function you want to hit, then you have a way to find the function, even if you cannot predict where it is and go there directly.
DEP – first problem is that a lot of hardware vendors don't turn it on, and "software DEP" isn't really DEP, but more like SafeSEH (sort of). If you're down to "software DEP", it might prevent an exploit that wasn't looking, but it won't help that much. Next problem is the return into lib C issue, and a related nuance – if the exploit code you want to run can be constructed from code that's already in the app, you don't really need to execute shell code, just find something that's already there. You could also do data driven attacks, like how the original getadmin exploit against NT 4.0 just flipped a bit in the kernel. Second set of problems is finding a place to put your code. RWX pages do show up in apps for various reasons, and if you have enough control over your exploit to find that spot and put your shell code there, DEP won't save anyone. There's also the rather neat attack Mark did against byte code – if something essentially compiles input and runs it, you might be able to alter that, and DEP can't help with that problem, either.
SafeSEH – this is good to have, but it isn't one of the more robust mechanisms. Coupled with DEP, it isn't too bad. My preference here is to go to 64-bit code, where the exception handlers aren't writable.
I'm sure that Mark's clever enough to come up with something I haven't thought of – be interesting to see what he presents on that goes beyond what I have here.
Also, as I've said many times, all this stuff is just like seatbelts. They might help make the difference between a bad day and a REALLY bad day, but you don't want to have to use them. I would never punt a fuzz bug because of one of these. That said, I still recommend using seat belts. It's also true that with these countermeasures in place, there will be some set of previously exploitable conditions that can no longer be exploited at all because a sufficiently complex attack cannot be launched.