Saturday, August 30, 2008

Cross-domain leaks of site logins

Browsers suck. We're building our fortified web apps on foundations of sand. A little while back, I was talking with Jeremiah about an interesting attack he had to determine whether a user is logged into a given site or not. The attack relies on the target site hosting an image at a known URL for authenticated users only. It proceeds by abusing a generic browser cross-domain leak of whether an image exists or not -- via the onload vs. onerror javascript events. Browsers generally closed that leak for local filesystem URLs (thus preventing accurate profiling of a victim's machine) but neglected to close it generally.

My version of this "login determination" attack is to abuse another leaky area of browser cross-domain handling: CSS. The <link> tag permits us to load CSS resource from arbitrary domains. The two interesting observations here are that we can read arbitrary CSS property values if we know the name of the style plus the property name we are interesting in. Secondly, most websites serve different CSS depending on whether the user is logged in or not. In addition, remember that browsers will happily pluck inline style definitions out of HTML. Put these things together, and here's a FF3.0.1 snippet that will tell if you are logged into MySpace or not:


<html>
<head>
<link rel="stylesheet"
href="http://home.myspace.com/index.cfm?fuseaction=user"/>
<script>
function func() {
var ele = document.getElementById('blah');
alert(window.getComputedStyle(ele, null).getPropertyValue('margin-bottom'));
}
</script>
</head>
<body onload="func()">
<div id="blah" class="show">
</body>
</html>


If you are logged in, you'll see "3px" vs. "0px" otherwise.

You'll also appreciate from this that any CSS property value is stealable cross-domain, assuming the style names aren't randomized (which I've never seen). The natural follow-up question is, are sensitive values stored in CSS properties? Currently, generally not, although I have seen background-url storing look & feel customization which could assist fingerprinting a user. In a couple of extreme cases, I've seen background-url used with a data: URI such as data:image/png;base64,blabla. Might be worth stealing.

Friday, August 29, 2008

Ode to the bug that almost was

This post is a tribute to the hundreds of bugs that never quite were serious, and the emotional roller coaster ride on which they take researchers.

Some brief background. The skill in finding serious bugs these days isn't in being a demon code auditor or a furious fuzzer; there are thousands of these. The skill lies instead in finding a piece of software, or a piece of functionality, that has the curious mix of being important yet not having seen much scrutiny.

Onto today's almost-bug: an interesting integer condition in the ZIP parser of Sun's JDK. The ZIP parser is a critical piece of code because not only is it used to parse JARs, but also server-side Java applications will often parse untrusted ZIPs. (Such direct server-side attacks, along the lines of my JDK ICC parser vulnerabilities last year, are nasty, and starting to recently become in-vogue for Python, Ruby and Perl too). The affected API is java.util.zip.ZipFile. Best I know, java.util.zip.ZipInputStream is not backed by the same native code, and thereby unaffected.

The interesting code, in zip_util.c follows:


/* Following are unsigned 32-bit */
jlong endpos, cenpos, cenlen;
...
/* Get position and length of central directory */
cenlen = ENDSIZ(endbuf);
if (cenlen > endpos)
ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
cenpos = endpos - cenlen;


jlong is a signed 64-bit type. The ENDSIZ macro, because of the way it is formulated, returned a signed int. Therefore, the assignment to cenlen triggers sign extension. This means that cenlen can end up being negative, rather than the stated intent of being treated as an unsigned 32-bit quantity. The negative value will of course bypass the security check and lead to subsequent undesirable state. (Note that the best fix is not to enhance the check, but to add a cast to unsigned int to the underlying macro as it is used in multiple places).

So why does this appear to be just a bug and not a security vulnerability? Well, on systems without mmap(), a huge allocation will either cleanly fail, or a read() attempt past EOF will cleanly fail. On systems with mmap(), things are more interesting. A 32-bit build will attempt a 2Gb large mapping on a potentially much smaller file. This could lead to interesting SIGBUS conditions as a server DoS. By quite some luck, the Sun JVM process seems to spray mappings liberally through the address space, leaving no room for a contiguous 2Gb mapping.

The same sign-extension bug exists in other parts of the ZIP handling, and leads to some interesting negative values getting to some interesting places. But lower-level sanity checks save the day in the cases that I could find.

A zip file capable of triggering the interesting log line "mmap failed for CEN and END part of zip file" is available at http://scary.beasts.org/misc/mmap_fail.zip.

Ah well, maybe next time. Come to thing of it, my pipeline does include real JDK vulns. Watch this space.

Monday, August 25, 2008

A dangerous combination of browser features

As browsers gain more and more features, the possibility increases for interesting or dangerous interactions between these features. I was recently playing with a couple of new browser features -- <canvas> and SVGs -- and found a cross-domain leak in the development version of Webkit:

http://scary.beasts.org/security/CESA-2008-007.html

Fortunately, no production versions of the major browsers are affected - and forearmed with this information, they can keep it that way. The only production browser I found that supports all of the required pieces is Opera 9.52, and they deserve some serious credit for getting the security check correct.