Search: Home Bugtraq Vulnerabilities Mailing Lists Jobs Tools Vista
Digg this story   Add to del.icio.us  
Dowd's Flash Report: What Have We Learned?
Thomas Ptacek, Matasano 2008-04-15

How nasty is the Flash vulnerability Dowd found?

Combined with any DNS vulnerability or any high-profile cross-site scripting vulnerability, the weaponized version of this attack would probably clock in at tens of thousands of compromised browsers per minute.

Is this a new bug class?

Sort of. It depends on what you mean by the term “class”. For example: most researchers consider heap overflows a seperate bug class from stack overflows. In reality, though, the same underlying coding error causes both vulnerabilities: poorly bounded copies. On the other hand, epistimologically, integer overflows are a new bug class, because the underlying coding error is a type violation, which creates an unbounded copy.

See how I used the word “epistemologic” there? That means you don’t care about the difference. Wild writes from NULL pointers are probably their own bug class.

So this is like the heap overflow revelation in the late 90s? NULL pointers are exploitable now?

No. Learn everything you can from Dowd’s paper and NULL pointers still aren’t usually exploitable:

  • They need to be written to, not read from; lots of fuzzer advisories trace down to loads, not stores, from NULL.

  • The offset needs to be controlled by the attacker; most of the time, offsets are hardcoded (most offsets are structure references).

  • The wild write needs to happen before any pointer loads that will crash the program.

Is there a pattern worth looking for here? Absolutely. Look for things that can return NULL that have random-access indexing. Malloc is a perfect example.

Wait a minute. Didn’t you say people shouldn’t check malloc?

Yes. This bug is a perfect case in point for why I’m right.

Consider: it is not the case that the Flash runtime never checks for allocation failure. What happened is, the Flash developers have an allocation checking regime that defaults to unchecked, and requires them to audit every allocation.

The way it should work is, by default, when using the simplest, most common allocation calls (malloc, or, in Flash, mem_Alloc), the program should abort if malloc fails. Returning and catching NULL is inadequate.

“But we can’t just abort when any given malloc fails! What about user-specified sizes?” You don’t have to abort on every malloc. You just have to abort by default. When you know you’re taking a value from a user, or any other unsafe input, you should use “unsafeAlloc”, which is simply malloc. Then you audit your code for the 3 places in the whole project that use “unsafeAlloc” and make sure the checking regime works.

Doesn’t runtime security, like in Vista, solve most of these problems?

Maybe, maybe not. Obviously it didn’t here, because Flash turned runtime security off.

But look at the bigger picture. Runtime security measures like ASLR and cookies and W^X memory all address the “dumb exploit” pattern. The “dumb exploit” pattern is an artifact of hardcoded runtimes generated by C compilers. When your exploit is shotgunned in through a dumb runtime, you lose both predictability and control of the target program. That’s basically what runtime security is capitalizing on: your exploit doesn’t know where DLLs are based, and so it can’t return directly into them.

The problem is, the hardcoded runtimes are going away. The vast majority of code written going forward targets extremely complicated runtimes, like the bytecode VM in ActionScript. In a bytecode VM scenario, an exploit has much more flexibility:

  • There might be 10x as many places to overwrite that will compromise the target; for instance, the abstract syntax tree objects containing method tables.

  • Valuable information might be readily accessible from known relative offsets or, better still, from registers kept loaded with intepreter state.

  • Just as with ActionScript, the content buffer that vectors your exploit in might be executable in the target runtime, leaving you only with the problem of compromising the verifier.

  • Just as with ActionScript, there may be an extremely powerful executive running on top of the CPU, rather than just machine code instructions running directly on the CPU.

These are all ways that high-level languages make runtime security harder.

But high-level languages are supposed to be a huge security win!

They probably are. But remember, even in the most intricate schemes (and Javscript compiled to a bytecode VM that runs off the system stack qualifies), high-level languages are really just glue around low-level languages: the most interesting features in Python, Ruby, and Javascript are implemented natively.

So, you get two interesting phenomenon:

  • You need to audit the runtime to make sure that the C code that implements the core language isn’t vulnerable (this is why Perl was a bad bet in 1995, when everyone was saying that buffer overflows were C’s fault).

  • You need to audit all the native extensions (such as Quicktime for Java), bearing in mind that unlike a server or a client, the attack surface for a language extension is arbitrary callers with arbitrary arguments —- a much more painful place to be.

Has Mark Dowd simply outclassed us? Should we pack it up and quit?

Yes. But don’t feel bad about that. You’re a human being, and he’s a remorseless killing machine. Big Blue crushed Kasparov, and now he’s not the prime minister of Russia! At a certain point, you have to concede the field, moving on to games where human beings still have the advantage. Computers haven’t solved Go, for instance. For us researchers, I suggest we take advantage of Mark Dowd’s robotic inability to love, and take up the arts, such as watercolors or interpretive dance.


Comments


The information, views, and opinions contained on this page are those of the author and do not necessarily reflect the views and opinions of SecurityFocus.






 

Privacy Statement
Copyright 2007, SecurityFocus