-
Website
http://www.matasano.com/log -
Original page
http://www.matasano.com/log/1032/this-new-vulnerability-dowds-inhuman-flash-exploit/ -
Subscribe
All Comments -
Community
-
Top Commenters
-
Press Controls
3 comments · 2 points
-
ChrisMtso
12 comments · 1 points
-
Eric Monti
11 comments · 1 points
-
StatlerAndWaldorf
12 comments · 3 points
-
Dave G.
7 comments · 1 points
-
-
Popular Threads
Guess who was arguing the contrary ;) ?
Do you mean 2 gigs? I don't know how a signed-unsigned issue would result in anything to do with 2 megs.
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).
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.
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..
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.
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. :)
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.
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?
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 :-)
Very well written article as well!
Additionally, you are a golden god for that video, and does anyone know where the "perfect" video is?
Every programmer can write their own malloc wrapper (possibly a macro or inline function) so there's no need in changing any C libraries.
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.
So the free software people might be safe.
Did anyone check? :-)
http://www.gnu.org/software/gnash/
There's also some interesting wrinkles to what can go wrong dereferencing null. ptr->array[input] is also an arbitrary memory write.
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.,.
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.
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.
Furthermore I suspect he only decided to NOT make it processor-independent as well purely for nostalgic reasons :)
@mark: While doing this, did you consider getting rid of the Intel shellcode altogether and relying on a subverted VM to JIT the bytecode for you?
The following excerpt from Flash Player 9 Security - pp. 16 triggered the idea when I read it (the advantage of doing this would be that you can write platform-independent second stage in Action Script itself and, needless to say, the idea may also potentially apply to Java and .NET):
"Byte code verification is done in the client’s computer (by the Verifier) and consists of determining the types that each byte code will consume and produce, by forming a map of the types of the values on the value stack for each byte code. Therefore, byte code programs that use invalid byte codes (given their operand types) can be detected and rejected early. This eliminates some runtime checking that would otherwise be necessary for system integrity. ActionScript 3.0 goes further at this point and produces native machine instructions to perform the actions of the program, having assured itself of the types of the values it will obtain as the program runs. This translation to native instructions depends mainly on knowledge that byte code verification must generate as it proceeds.
Although this process is fairly complex, it is also well defined and it is clear what the interpreter would do under the conditions that the Verifier has proven will occur. Therefore, it is relatively direct for AVM2 to map these to the appropriate machine instructions."
Does the flash plugin runs always with admin privileges by default inside the IEFRAME?
Greets, Erwin
WE'RE NOT WORTHY!
WE'RE NOT WORTHY!
Just amazing.
The patch for this is implemented in .124 but the "brains" at Adobe have compiled in SSE instructions into the build since .115, which crashes processors (Pentium 2 and on on). So anyone still capable of running flash with those are still vulnerable.
I would argue that the result of a malloc() should be checked every time, not just from a security standpoint, but also from a user interaction standpoint. I don't want any of my code to result in "Segmentation fault" or "Program XXX unexpectedly quit" -- I want to gracefully exit and tell the user what happened, NOT just abort. Since I'm a Mac guy, anybody else remember 0xDEADBEEF? :-)
Sun's JVM is a catastrophic, insecure turd sitting on almost as many PCs as Flash. If you don't think it's an even bigger risk (given what it is and what it does) you're kidding yourself.
jb
If you honestly think the quality of Flash's VM is anywhere near the one produced by Sun (and used by the Real World [TM] at large), then I recommend a visit to your local asylum.
I could be wrong about the YouTube video, though.
The problem with Flash is not that the bytecode verifier is broken. So far as we know, it works fine. The problem with Flash is that its C-code native extensions can corrupt the verifier and shut it off.
The exact same problem potentially exists in Java, and Python, and C# (well --- less likely in C#). The problem isn't the language or the language runtime. The problem is that in 2008, none of these things are self-sufficient; they all rely on C-code extensions. The combination of high-level language runtimes and low-level C code memory corruptions moots OS security mechanisms.
That's the problem. Java has it too.
You know what would be even more cool? Writing an excellent blog entry like this explaining the exploit, while at the same time incorporating that very same exploit in a flash animation on this page, infecting any unpatched readers with the exploit as they read about it.... :)
That is SO stupid that I have no words for it.
Hint - It's at a lower level, you mush brained idiots.
Malloc bugs tend to crash when tickled non-maliciously, it's pretty much only the malicious attacks that don't crash the program. (Or, you know, do, but also run their evil exploit.) So, if malloc fails with no error handling, you're boned either way. Better to crash under both normal use and attack than to crash under normal use and run exploit code under attack.
There are a vanishingly small number of cases where a typical large program might do something intelligent with a malloc failure."
It is a common situation in an embedded system. And most of the times you to handle running out of resources gracefully without aborting an entire process.
Embedded systems are a practice focus for us, and I'll argue with anyone who says that embedded devs are more disciplined about allocation than application devs.
Think about this. You're proposing a scenario in which, failure or not, the very pattern of calls to malloc is sensitive. Your code is insecure with or without a change in the library. You think that code should simply call "malloc"? I'd rename "malloc" to "super_unsafe_side_channel_vector_malloc" for that case even if NULL pointers couldn't be offset.
This all sounds "so stupid" to you because you're not working in security. News flash: security sucks. Things would be a lot easier if innocuous programmer errors couldn't be warped into exploits.
{
p1=malloc(secret);
p2=malloc(user_supplied);
p3=malloc(secret);
if( !p3 || !p2 || !p1) abort()
}
which I cant with TQBF's super_duper_extra_safe_malloc() that you'd like me to use.
btw, as far as I recall the debug options of phkmalloc can be used to force an abort() on ENOMEM. In fact i think that's the default behavior now.
* Where you say "super_duper_extra_safe_malloc", I say "malloc"
* Where you say "malloc", I say "unsafe_malloc"
* I'm not arguing that "unsafe_malloc" shouldn't be available.
Again: the default should be, fail safe.
So, your example doesn't work with libtqbfmalloc, because you misused it. Just add the token "unsafe_" to each call and you're fine.
First of all, thanks so much for sharing your discovery of an entire new class of cryptographic side channel attacks.
As far as I am aware (unless I missed something), breaking crypto with memory allocation failure is a previously unpublished result.
It's quite astonishing to learn something so interesting from a short random blog comment.
So I don't mean this too critically, but I think that your proposed countermeasure doesn't really work in the case where the allocation patterns are deterministic and repeatable. That is to say, exactly the same situations where you would expect to be able to use this attack.
It's the same if you fail immediately upon allocation or if you test everything together and then fail. In most scenarios that I can imagine, you can't even know the difference.
Either way you can easily guess the sum of the two secret lengths with a binary search, and also the exact values if you have an easy (ie: polynomial time) way to verify your guesses.
Depending on constraints for the possible secret length values, and how much control you have over allocations leading up to this situation, you might be able to also guess the secrets directly by punching appropriate sized holes in the heap and then running experiments.
Have you tried your attack against the new constant-time modular exponentiation in OpenSSL? I was instrumenting the allocations over the weekend and it seems very promising.
I admit though, I don't understand why so many smart people stick up for the K&R; malloc semantics. They suck. We have empirical proof of that: spend an afternoon taking a random sample, using only projects people have heard of. You will find three classes of codebase:
(1) Those that have given up on K&R; malloc and replaced it with a wrapper that does exactly what I advocate.
(2) Those that don't know what's wrong with K&R; malloc, pretend to check rigorously, but fail either directly or in a byzantine manner when an attacker exhausts their resources.
(3) Those that do understand what's dangerous about K&R; malloc, and therefore expend great effort to carefully check and recover from all allocation failures.
Even if I unrealistically concede that the codebases are divided evenly among the three classes --- and they are not (I've provided them in ranked order) --- that's still a failure for the K&R; semantics.
I really was sincerely blown away and amazed by the implications of what Ivan posted last week. Maybe it's old hat to everybody else here but it's honestly the first time I've ever heard the idea.
I spent the weekend thinking about what might be possible with a technique like this. Came up
with silly examples like:
What if there was a hypothetical operating system that was configured by default to set up RLIMIT_DATA to some value other than 'unlimited'. Let's call it OpenBSD.
Then suppose that this OS runs some imaginary network service that makes it almost too easy
to allocate (and free) a lot of memory in arbitrarily sized chunks. We'll name that one OpenSSH.
Now let's add a feature to our fantasy network service that performs an asymmetrical private
key operation. We can call it 'signing the key exchange with the host RSA key'.
Ok, now suppose that the RSA signing is implemented by say, an exponentiation that rakes over the
private exponent 6 bits at a time and performs a different pattern of big number multiplications (and hence allocations) depending of the value of each particular window of private key bits.
Now to _really_ stack the deck in our favour, we'll assume that during the modular exponentiation all the allocations are saved up and free()ed together at the end of the operation by releasing the BN_CTX.
Sure, it's a contrived example, but I'm just saying that it might be kind of interesting to remotely measure precisely where sbrk() is failing in a theoretical scenario like that.
I don't recall making any claims about new classes of attacks nor of my three lines of code being a solution for anything else than proving the point that not having malloc() abort on failure _may_ be actually useful and desired.
I just blurted a general idea (I guess that is what a blog comment is for) of when automatically exiting on malloc failure may have security implications beyond code execution but then again I do not have any precise concrete real-world examples to show for, the only thing that came to mind as potentially relate when I posted was THC's paper on execution path timing analysis applied to OpenSSH some years ago.
I can think of other equally random and bogus possibilities to entertain you (like BIND or Apache for example) but I think you are way smarter than me for that kind of thing.
@tom: I don't stick to K&R semantics because I think they're "right", I just think that changing malloc() semantics and encouraging programmers to not do error checking and to rely on the now declared "safe" malloc will not produce more secure programs.
That's definitely not the case and I'm becoming more and more fascinated by this idea every day.
Remember when Daniel Bleichenbacher tore OpenSSL a new one over PKCS #1 padding?
No no, the other time. Ten years ago. The PKCS1 'oracle' attack to invert RSA.
That problem was 'fixed' by kludging implementations to not leak information about the outcome of padding verification.
If something goes wrong, just fill the premaster-secret with random bytes and pretend that nothing strange is happening. This prevents an attacker from knowing if his malicious ciphertext decodes to a PKCS1 conforming message or not.
At least directly through the SSL protocol....
When invalid padding is detected during RSA decryption, the RSA module logs the failure to the internal OpenSSL error handling system. This error is never exposed to the client (for example, in an SSL protocol message), and just to be on the safe side, if the RSA decryption fails for any reason the SSL module calls shenanigans and explicitly clears this internal error log before implementing the random byte hack described above.
Hmm...
It seems that the first time a thread or process logs an error through the error API, a ~400 byte ring buffer structure is allocated with malloc().
Oh, and if that malloc() fails for some reason?
In that case, OpenSSL does something reasonable (?) and returns a reference to a single static instance of the structure. (A special bonus quirk for fans of extreme sports such as thread racing to double free).
So you can't crash the service by forcing that particular malloc() to fail, but you can later measure if the allocation occurred or not if you understand the allocation failure regime used throughout OpenSSL:
Don't crash, abort(), or exit() if malloc() fails. Just fail the current operation and all future operations until memory is available again.
I think you could build an oracle for the Bleichenbacher attack out of this behavior. An oracle that precisely detects that the problem with your message is broken padding, (and not the less useful: broken padding OR malformed plaintext).
This is the most powerful leak possible for this attack and with the Klima-Pokorny-Rosa optimizations you would only need a few thousand oracle consultations to sign or decrypt something.
It's just an idea, and I'm not claiming to have implemented the attack or anything. There would obviously be a lot of hurdles to jump over and details to hammer out in order to build a practical implementation. I've just been browsing OpenSSL looking for ways to break things with creative malicious memory exhaustion.
I don't know how you could even begin fixing bugs like this in a general way. Deciding when to run out of memory in some non-deterministic way?
This stuff is a lot more interesting to me than bickering about whether or not NULL pointer dereference bugs are commonly exploited outside of imaginationland. So sorry if I'm dragging this thread too far off topic.
BURSA SOHBET
ISTANBUL CHAT
ISLAMI CHAT
IZMIR CHAT
ANKARA ARKADAS
ALMANYA CHAT
TURKEY CHAT
MYNET
SITENE EKLE
VIDEO KLIP IZLE