Dowd’s Flash Report: What Have We Learned?

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.

15 Comments so far

  1. nirva April 15th, 2008 5:52 pm

    too late…

    PARIS, April 9 /PRNewswire-FirstCall/ — During the Go Tournament in
    Paris, staged between 22 and 24 March 2008 by the French Go Federation
    (FFG), the MoGo artificial intelligence (IA) engine developed by INRIA -
    the French National Institute for Research in Computer Science and Control
    - running on a Bull NovaScale supercomputer, won a 9×9 game of Go against
    professional 5th DAN Catalin Taranu. This was the first ever officially
    sanctioned ‘non blitz’ victory of a ‘machine’ over a Go Master.

  2. Chris April 15th, 2008 6:12 pm

    Only got about half way through the paper. Nice summary posts Thomas. I think everyone is waiting for the next ‘bug class’ but in reality its probably not going to happen. It’s mostly likely going to be application specific ‘attack classes’ from now on. I don’t think I’m gonna break out the water colors just yet…

  3. Thomas Ptacek April 15th, 2008 7:07 pm

    Danny, come on. That’s a 9×9 game, and the problem with Go is the combinatorics. Real Go games are 19×19.

  4. Ryan Russell April 15th, 2008 7:35 pm

    I don’t see how W^X and ASLR and ilk are supposed to help significantly when the target is a language interpreter…

  5. Thomas Ptacek April 15th, 2008 7:37 pm

    I think that’s my point; they’re assuming that the target of memory corruption is a function pointer for the native architecture.

  6. Christian April 15th, 2008 10:43 pm

    Glad to see that the Terminator comes from Australia :)

  7. pepe April 16th, 2008 7:32 am

    So…people are going towards complex language interpreters and discover new unforseen consequences. But everybody knows that flash is crap, as is java. Isn’t “complexity is evil” one of the main lessens here?

    Wish W3C would have included videos in HTML..

    Anyway, what about c++ exception handling? Without a handler for expected errors that can be handled, it should safely abort my program, right?
    Or is it, in a way, the same problem: A lot of generated code that could be buggy and thus could be exploited during stack unwinding?

  8. sigsegv April 16th, 2008 7:59 am

    That line about all of us being human beings and Mark Dowd being a remorseless killing machine made me grin.

    Props to Mark for finding the vuln.

  9. cyber globetrotter April 16th, 2008 2:04 pm

    I agree that interpreters are likely an easier target for application-specific attacks due to the degree of control an attacker has on the target environment. However, I disagree with the claim that mitigations may offer little help for flaws found in an interpreted runtime. One will still need to have knowledge of the address space in most cases and there should never be a reason for interpreted bytecode to be stored in an executable memory region.

    For example, if Adobe had compiled with dynamicbase and had marked the memory region containing the SWF file as NX (with appropriate hardware support), it would have been quite a bit more difficult to exploit this vulnerability. It would not have been possible to assume the address of the AS3_argmask array and it would not have been possible to execute native code embedded within the SWF file. Both of these things were pivotal requirements for Dowd’s exploit.

    Regardless, many props to Mark for his awesome work :)

  10. mark April 16th, 2008 7:20 pm

    @cyber globetrotter:
    Another mitigation that would make this exploit hard (impossible?) would be to make the AS3_argmask table read-only. I presume the data in this table is static and could be put in a read-only section of the binary?

  11. ivan April 17th, 2008 2:55 am

    @Tom: another item for your laundry list of security consideration for modern high-level languages is that their powerful executives and feature rich components don’t make it even necessary to escape to run low level instructions on the CPU. If I’ve subverted the VM and I have complete control of all this I may not need to go further down.

  12. Rosyna April 17th, 2008 4:14 am

    Stuff like this makes me hope I am never forced to go back in time and use a computer to browse the internets.

    (Where stuff like this==any reproducible exploit found in the last 10 years)

  13. […] Thomas is in love, or at least very, very deep like, of Mark Dowd (here), and (here) and his recent flash vulnerability findings (here) which, Tom states “Combined with any DNS […]

  14. […] on secure coding than I am. And I’m telling you this because LeBlanc’s response to my last post is —- faithful paraphrase —- “are you […]

  15. […] may bring to mind some recent discussions on whether callers of memory allocation functions should check the return value […]

Leave a reply