Wednesday, April 16, 2008

This New Vulnerability: Dowd’s Inhuman Flash Exploit

http://www.matasano.com/log/1032/this-new-vulnerability-dowds-inhuman-flash-exploit/


The evidence is now overwhelming that Mark Dowd was, in fact, sent back through time to kill the mother of the person who will grow up to challenge SkyNet. Please direct your attention to Dowd’s 25-page bombshell on a Flash bytecode attack.

Some context. Reliable Flash vulnerabilities are catastrophes. In 2008, we have lots of different browsers. We have different versions of the OS, and we have Mac users. But we’ve only got one Flash vendor, and everyone has Flash installed. Why do you care about Flash exploits? Because in the field, any one of them wins a commanding majority of browser installs for an attacker. It is the Cyberdyne Systems Model 101 of clientsides.

So that’s pretty bad-ass. But that’s not why the fate of humanity demands that we hunt down Dowd and dissolve him in molten steel.

Look at the details of this attack. It’s a weaponized NULL pointer attack that desynchronizes a bytecode verifier to slip malicious ActionScript bytecode into the Flash runtime. If you’re not an exploit writer, think of it this way: you know that crazy version of Super Mario Brothers that Japan refused to ship to the US markets because they thought the difficulty would upset and provoke us? This is the exploit equivalent of that guy who played the perfect game of it on YouTube.

Let’s break it down a bit:


Start with the vulnerability.

It’s an integer overflow, but not a simple one.

When the Flash runtime reads in scene data from a SWF file, there’s a numeric field that, when bounds-checked, is interpreted as a signed number, but when used is treated as unsigned. So there are values the field can take that are treated as tiny and innocuous at time-of-check, but actually evaluate as huge numbers at time-of-use.

A by-the-numbers integer overflow normally knocks the bounds checking off a strncpy or memcpy call, turning code that carefully copies, say, 1k of memory into code that will copy 2 megs of data, splattering it all over process memory. Not here. Instead, Flash uses the malicious number as a count of bytes to allocate.

When you ask Flash to allocate several gigs of memory all at once, the allocation fails, returning NULL. Attempt to use that NULL address and you will crash the program. This happens all the time in real code. Many crashes are traceable to NULL pointers. And, since nothing (usually) lives at NULL, NULL pointer crashes are usually code for “not exploitable”.

Not this time. Flash forgets to check that allocation failed, a ludicrously common error. It then uses that pointer with an offset controlled by the attacker. NULL isn’t valid. NULL plus 1024 isn’t valud. But NULL + 0x8f71ba90 is, as is NULL + N for any N that addresses valid memory.

To this address, controlled by attackers via wild offset, Flash writes a value that is also controlled by the attacker. This is the write32 pattern: a vulnerability that gives the attacker the means to set any one value in memory to a value of their choosing. Game over.


Except not quite.

The exploit doesn’t actually get to offset an arbitrary number of bytes from 0. A complicated set of conditions constrains the address it writes to and the value it gives it.

The the actual write occurs via a structure offset. Flash is hardcoded to translate your offset into another number. Working offsets, as it turns out, will be greater than 0x80000000, and will be evenly divisible by 12 after 4 is added to them. Note: I thought I was hardcore when I wrote shellcode with no lowercase letters for the IMAP vulnerability in the ’90s.

That’s not all. The value that Flash will write to the wild pointer isn’t totally controlled by the attacker either. It’s cast up from a 16 bit integer to a 32 bit integer, and has another variable subtracted to it. This is the point in the report that I started giggling uncontrollably, embarassing myself at the coffee shop.

The net result of this silliness is that it’s hard to do what attackers normally do with a write32 vulnerability, which is to clobber a function’s address with a pointer back to their buffer, so that their shellcode is called when the clobbered function is called. So Dowd’s exploit takes things in a different direction, and manipulates the ActionScript bytecode state.

ActionScript bytecode state; yeah, about that. ActionScript is Javascript that controls Flash animations. But the Javascript system used by Flash is pretty advanced; for performance, it transforms Javascript into bytecodes for a VM. For a bytecode VM, ActionScript is pretty tight; its runtime stack is integrated with the CPU’s runtime stack. The memory it uses to execute code is the same memory that the Flash C-code runtime uses to manage its own state.

ActionScript is a register-based VM, meaning that its bytecode instructions concern themselves chiefly with moving values in and out of memory slots that simulate CPU registers. Those registers live in the runtime stack and are accessed by indexing. Meaning, a malicious Flash bytecode instruction can index its way to an arbitrary address on the system stack. Game over.


Except not quite.

You can’t just inject malicious bytecodes.

Flash players have to execute bytecode sklorked directly off of web pages, most of which are controlled by organized criminals. So Flash doesn’t execute arbitrary bytecodes; they’re verified before execution. The verifier ensures, among other things, that register accesses from the bytecode stream reference valid register slots.

But. For performance, the Flash VM is broken into a two-pass system with a verifier that validates bytecode (time-of-check) and an executive that later evaluates it (time-of-use). And the interpretation of bytecode differs at time-of-check and time-of-use. Here’s the situation:

  • The verifier ignores undefined bytecodes.

  • The verifier keeps a table in memory that defines how long any one bytecode instruction is.

  • The bytecode length table is a valid target of the NULL pointer overwrite.

  • The executive has totally different machinery for interpeting bytecode.

Clobber the right value in the length table, and you can make an unused bytecode instruction that the verifier ignores seem much longer than it is. The “extra” bytes slip past the verifier. But they don’t slip past the executive, which has no idea that the unused bytecode has trailing bytes. If those trailing bytes are themselves valid bytecode, Flash will run them. Unverified. Giving them access to the whole system stack. Game over.


Except not quite.

The Koopa shell on the second platform is a trap and if you touch it you die.

Ok actually there’s no catch. Dowd’s exploit uses a NULL pointer write32 to knock the locks off the bytecode interpreter in Flash, so that his SWF file can run bytecode that will rewrite the system stack.

But, just to rub it in, or because this stuff just comes natural to you when you are manufactured by a malicious cluster of supercomputers inside SkyNet instead of nurtured by loving human parents, Dowd gives himself additional constraints.

To wit: his exploit must (because he’s messing with us) corrupt the Flash runtime, rewrite it to execute his trojan, and leave it running steady as if nothing had happened. Meaning:

  • His modification to the verifier can’t break existing instructions.

  • His bytecode has to swap values into the stack instead of clobbering them directly.

  • Portions of his shellcode have to run as both Flash bytecode and an X86 first-stage shellcode boot.


Two fun details.

First, even though IE and Firefox use different Flash builds, the addressing inside them is compatible. The exploit works in both places.

Second, Flash isn’t compiled with ASLR. So the attack works on Vista.

Mass casualty. Go Flash!

35 Comments so far

  1. Dino Dai Zovi April 15th, 2008 5:18 pm

    Oh, the *sweet* *sweet* vindication. I remember an argument that I have had twice in the last several years about whether one should check the return value of malloc. I argued that it should always be done for two reasons: Reading address zero might crash, but not necessarily zero-plus-offset and because a compare register to zero is so free performance-wise that it isn’t even funny.

    Guess who was arguing the contrary ;) ?

  2. Skorgu April 15th, 2008 5:20 pm

    Love, love, love the Pitfall banners.

  3. Hal B April 15th, 2008 5:37 pm

    “A by-the-numbers integer overflow normally knocks the bounds checking off a strncpy or memcpy call, turning code that carefully copies, say, 1k of memory into code that will copy 2 megs of data”

    Do you mean 2 gigs? I don’t know how a signed-unsigned issue would result in anything to do with 2 megs.

  4. […] How nasty is the Flash vulnerability Dowd found? […]

  5. Thomas Ptacek April 15th, 2008 5:45 pm

    Hal: most integer overflows don’t literally shove 2 gigs down the target’s throat, even though the count value says they will. “2 gigs” is usually a synonym for “unbounded”.

  6. Thomas Ptacek April 15th, 2008 5:46 pm

    Dino: you are the opposite of vindicated. See the followup post.

  7. nirva April 15th, 2008 5:58 pm

    Dino, you have that argument with everyone.. and you are still wrong :-)

    Who malloc()s and then starts writing into the returned memory at some funky offset?

    Everyone thinks of using the VM as some efficient spare data structure, but no one actually does it because in the end it doesnt pay off (also it is gross).

  8. […] Matasano Chargen » This New Vulnerability: Dowd’s Inhuman Flash Exploit “It’s a weaponized NULL pointer attack that desynchronizes a bytecode verifier to slip malicious ActionScript bytecode into the Flash runtime.” Very entertaining writeup of some incredible exploitation. (tags: security exploit) […]

  9. Thomas Ptacek April 15th, 2008 7:16 pm

    Also: compare-to-zero overhead is obviously not the argument.

    I once took over a codebase from which I was able to remove wait for it TWO THOUSAND LINES OF CODE because the code not only religiously checked malloc returns but also wait for it wait for it RETURNED NEGATIVE ONE through EVERY CODE PATH which could have an allocation failure, all the way back down to the run loop in main().

    You know what main() did when it got that -1? Yeah.

    The arguments against checking malloc are:

    * It totally cruds up your code without adding any safety or reliability.

    * It leads to situations like this bug, where you think you’re checking everything and being safe, but in reality you miss the check (or mess up the recovery regime) at the one spot that Mark Dowd happens to look at when he randomly opens up your program for 5 seconds to find his next vulnerability.

  10. mark April 15th, 2008 9:52 pm

    Hey,

    Thanks for the kind words! I had this thought also: the original exploit I made was browser independant and windows independant, but Flash version specific. I’m pretty sure you could make it Flash version inspecific also. The two things tying the exploit to a given version of flash are:

    1. Address you need to overwrite (location of AS3_argmask)
    2. Offset of EIP from your given stack location

    Issue #1 could possibly cause problems, but hey - exploiting both browsers at once worked out alright by doing multiple overwrites, so you could probably do multiple overwrites to address different versions of Flash also. But, I have never confirmed this, so I can’t be sure.

    Issue #2 could be resolved quite easily by getting the DWORD off the stack that you think should be EIP and testing it for various properties in actionscript. Then, repeating the process in 4 byte increments until you find it. (Alternatively, you could test the properties for something else that is near EIP to calculate the right offset.) What properties? I’m not sure exactly - but you can do all the usual bitwise math and stuff in AS3, I’m sure there is some simple formula there that you could come up with.

    Just a thought..

  11. Jamie Penney April 15th, 2008 9:53 pm

    @Thomas:
    Are you saying that you should not both with checks because you will inevitably miss one and that one will be exploited? If so, why check at all? I mean, if you have guys like Mark attacking your code, you are screwed, so why bother at all right?
    I do agree with you about checking the result of malloc though - if you aren’t doing anything funky with the resulting pointer then there should be no reason to check. If you are though, you should be doing bounds checking.

  12. Robert Hensing April 15th, 2008 10:24 pm

    Why do you say Flash doesn’t work with ASLR?

    I see flash9f.ocx running in iexplore.exe running at Low IL, on Vista SP1, with ASLR enabled and DEP in permanent mode (I forced IE to use DEP permanent via the ExecuteOptions reg key). Verified using Process Explorer. I played a SWF and used ProcExp to watch the threads in IE using the CPU and Flash9f.ocx was happily consuming my CPU. I wasn’t sure if you were referring to the flash9util9f.exe (which isn’t opted in to ASLR) that seems to run every time the control is activated so I killed that - and the SWF continued to play. :)

  13. Thomas Ptacek April 15th, 2008 10:40 pm

    @jamie

    I’m being pretty unclear. What I’m advocating for is that allocation *always* be checked. What I’m arguing against is requiring programmers to remember to check. Instead, malloc should abort instead of returning NULL.

    There are a vanishingly small number of cases where a typical large program might do something intelligent with a malloc failure. No, we shouldn’t just break those cases. Instead, we should keep a version of malloc with the original K&R semantics, called “unsafe_malloc”, so that those cases continue to work.

    The advantage to this scheme is twofold:

    * It is hard to introduce vulnerabilities like the one Mark found, because most of the cases where you’d allocate and blindly dereference a pointer are cases where you would not have carefully thought out your recovery regime. Those places will simply crash the program instead of handing control to Mark.

    * It pinpoints in your tens of thousands of lines of C code the places where allocation is critical to the program or susceptable to user control. You can audit those cases especially carefully.

  14. Robert Hensing April 15th, 2008 10:43 pm

    Oh - heh - nevermind. I see that flash9f.ocx isn’t compiled with /DYNAMICBASE as Mark says on page 25. :) Guess I should have read his paper before commenting. :_)

    C:\Windows\SysWOW64\Macromed\Flash>dumpbin /headers Flash9f.ocx
    Microsoft (R) COFF/PE Dumper Version 9.00.21022.08
    Copyright (C) Microsoft Corporation. All rights reserved.

    Dump of file Flash9f.ocx

    PE signature found

    File Type: DLL

    FILE HEADER VALUES
    14C machine (x86)
    6 number of sections
    47E8643E time date stamp Mon Mar 24 22:32:30 2008
    0 file pointer to symbol table
    0 number of symbols
    E0 size of optional header
    210E characteristics
    Executable
    Line numbers stripped
    Symbols stripped
    32 bit word machine
    DLL

    OPTIONAL HEADER VALUES
    10B magic # (PE32)
    7.10 linker version
    23F000 size of code
    16F000 size of initialized data
    0 size of uninitialized data
    2394C7 entry point (302394C7)
    1000 base of code
    240000 base of data
    30000000 image base (30000000 to 303AEFFF)
    1000 section alignment
    1000 file alignment
    4.00 operating system version
    0.00 image version
    4.00 subsystem version
    0 Win32 version
    3AF000 size of image
    1000 size of headers
    2E25DC checksum
    2 subsystem (Windows GUI)
    0 DLL characteristics

    That said - it’s still not completely fair to say Flash “doesn’t work with ASLR” and assume the worst . . . while it’s true that the DLL itself may not opt-in to ASLR, it IS however running in a process that IS using ASLR and thus there is some measure of randomization occuring (i.e. the thread stacks get random base addresses, the heaps etc.).

    thoughts?

  15. Sal Iverson April 15th, 2008 10:53 pm

    That exploit gave me a painful boner.

  16. Thomas Ptacek April 15th, 2008 11:05 pm

    If it lasts for more than 4 hours, consult a physician.

  17. Dino Dai Zovi April 15th, 2008 11:33 pm

    @nirva I do have that argument with everyone, you are right :). And I will continue to do so. I never said that aborting or throwing an exception on allocation failure isn’t an acceptable solution (i.e. pass appropriate flags to HeapCreate), just that ignoring the result of an allocation and using the returned pointer is a dangerous practice that should be eliminated.

  18. […] Томас Пташек (Thomas Ptacek) анализирует нечеловеческий эксп…, использующий уязвимость в Flash. […]

  19. Jamie Penney April 16th, 2008 5:45 am

    @Thomas:
    That makes a lot of sense. An error during memory allocation is one of those types of errors that you can’t do too much about - why saddle 99% of the users with the burden of basic error checking which probably results in application termination when it can be done in malloc itself.
    Thanks for the article on this exploit - it was very interesting to hear exactly how these things are created :-)

  20. steffen April 16th, 2008 10:31 am

    So funny. I guess this is the point where I will finally remove all slaveware from my Gentoo, thank the devs for paludis’ ability to allow/block by license…

    Very well written article as well!

  21. GM April 16th, 2008 11:31 am

    Thanks for the writeup; it’s great context / summary for/of the report. Disturbing to see the russian TrackBack above…

    Additionally, you are a golden god for that video, and does anyone know where the “perfect” video is?

  22. […] a detailed step by step look at Dowd’s flash exploit, check out this article. It is long, but is a gold mine for future cyber […]

  23. Rsh April 16th, 2008 12:30 pm

    @Thomas Ptacek
    Every programmer can write their own malloc wrapper (possibly a macro or inline function) so there’s no need in changing any C libraries.

  24. Thomas Ptacek April 16th, 2008 12:39 pm

    That’s awesome. Every programmer can also just not cram 200 bytes into buffers that are only big enough for 100. No need to change the C libraries. Bad programming? Use good programming. It’s so simple! How could we not have seen it!

  25. killroy_666 April 16th, 2008 12:47 pm

    this article would not exist unless the exploit had been examined by the people responsible for creating a security patch, and i am sure that Dowd has already toppled it.

  26. Nate April 16th, 2008 1:08 pm

    Mark’s work is awesome. I expect this is the future, just like exploiting smart cards.

    You’ll have to be intimately familiar with exactly how each program executes, its semantic interpretation of data (not just meta-information like buffer size), and non-x86 instruction sets (i.e. the AS opcodes here). This is a return to the era of exploiting app logic + skillful pointer dodging.

    Best of both worlds, hats off to Dowd.

  27. kutyacica April 16th, 2008 1:53 pm

    omg, this is serious man. cudos.

  28. Toni Ruottu April 16th, 2008 3:11 pm

    I suppose the bug does not exist on Gnash.
    So the free software people might be safe.
    Did anyone check? :-)

    http://www.gnu.org/software/gnash/

  29. David LeBlanc April 16th, 2008 5:45 pm

    Having a throwing new, or even a throwing malloc might be OK, but a serious side-effect is that you’re now asking people to write exception-safe code. See my post for some more detail on this. Writing exception safe code is an extremely non-trivial undertaking, and if remembering to check an alloc return is too hard, then writing exception safe code is almost certainly too hard.

    There’s also some interesting wrinkles to what can go wrong dereferencing null. ptr->array[input] is also an arbitrary memory write.

  30. bjc April 16th, 2008 6:58 pm

    @David LeBlanc:

    You can’t just drop something like that on us and not explain it! How does a couple dereferences and an addition create an arbitrary memory write? Inquiring minds, etc.,.

  31. links for 2008-04-16 | IndianGeek April 16th, 2008 7:35 pm

    […] Matasano Chargen » This New Vulnerability: Dowd’s Inhuman Flash Exploit (tags: actionscript analysis flash hack programming security) […]

  32. grey April 16th, 2008 8:09 pm

    @Toni Ruottu gnash has had its share of vulnerabilities in the past and there’s no reason to believe people wouldn’t go looking for more more if it were more widely deployed, e.g.:

    http://www.securityfocus.com/bid/23765

    As it stands, last time I tried to use it not only did it not play flv pr0n-in-a-browser but it also failed miserably with homestarrunner.com - which are basically the two reasons to even bother with flash anyway.

  33. Mike Shaver April 16th, 2008 8:27 pm

    Yeah, we’re actually taking the “if you’re out of memory, the jig is up” approach in future versions of Firefox (possibly the sequel the Firefox 3, possibly the sequel’s sequel). You can follow https://bugzilla.mozilla.org/show_bug.cgi?id=427099 and its dependents (chiefly https://bugzilla.mozilla.org/show_bug.cgi?id=427109) if you’re interested in the process, which will involve automatic rewriting across the width of our…substantial codebase. Better performance, less code complexity, improved security; it’s not every day you get all of those in one package.

  34. David LeBlanc April 16th, 2008 8:32 pm

    If you have:

    ptr->array[input] = foo;

    You are putting the foo value into the address of:

    ptr + offset(array) + sizeof(array_element) * input

    ptr is 0, offset(array) is a small, fixed value, and then sizeof(array_element) * input leads to foo being placed nearly anywhere in addressable memory.

    So to flesh this out, say we did:

    cbAlloc = sizeof(*ptr) + (input-1) * sizeof(array_element)

    Which is a pretty common thing to do. Now the alloc fails, we don’t check it, and you can now put foo just about anywhere you want. If you can put the system under pressure, you might be able to force alloc failure with relatively small values of input.

    So assuming that a null pointer is always just a plain crash is not always valid. The approach Mark used is just one way for this to happen - the one that I point out is another.

0 Comments:

Post a Comment

<< Home