Skip to content

Instantly share code, notes, and snippets.

@phillmv
Created November 25, 2008 19:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phillmv/29039 to your computer and use it in GitHub Desktop.
Save phillmv/29039 to your computer and use it in GitHub Desktop.
Dowd's inhuman flash exploit -- no longer available on the internets.
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 (http://documents.iss.net/whitepapers/IBM_X-Force_WP_final.pdf) 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:
p2.png
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.
p1.png
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.
p3.png
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.
p4.png
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.
p5.png
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!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment