Saturday, January 28, 2012

The dirty secret of browser security #1

Here's a curiousity that's developing in modern browser security: The security of a given browser is dominated by how much effort it puts into other peoples' problems.

This may sound absurd at first but we're heading towards a world where the main browsers will have (with a few notable exceptions):
  • Rapid autoupdate to fix security issues.

  • Some form of sandboxing.

  • A long history of fuzzing and security research.
These factors, combined with an ever more balanced distribution of browser usage, are making it uneconomical for mass malware to go after the browsers themselves.

Enter plug-ins

Plug-ins are an attractive target because some of them have drastically more market share than even the most popular browser. And a lot of plug-ins haven't received the same security attention that browsers have over the past years.

The traditional view in security is to look after your own house and let others look after theirs. But is this conscionable in a world where -- as a browser vendor -- you have the power to defend users from other peoples' bugs?

As a robust illustrative point, a lot of security professionals recently noticed some interesting exploit kit data, showing a big difference in exploitation success between Chrome (~0%) and IE / Firefox (~15%).

The particular exploits successfully targeted are largely old, fixed plug-in bugs in Java, Flash and Reader. So why the big difference between browsers?

The answer is largely the investment Chrome's security team has made in defending against other peoples' problems, with initiatives such as:
  • Blocking out-of-date plug-ins by default and encouraging the user to update.

  • Blocking lesser-used plug-ins (such as Java, RealPlayer, Shockwave etc). by default.

  • Having the Flash plug-in bundled such that it is autoupdated using Chrome's fast autoupdate strategy (this is why Chrome probably has the best Flash security story).

  • The inclusion of a lightweight and reasonably sandboxed default PDF viewer (not all sandboxes are created equal!)

  • The Open Type Sanitizer, which defends against a subset of Windows kernel bugs and Freetype bugs. Chrome often autoupdates OTS faster than e.g. Microsoft / Apple / Linux vendors fix the underlying bug.

  • Certificate public key pinning. This new technology defends against the generally gnarly SSL Certificate Authority problem, and caught a serious CA compromise being abused in Iran last year.
In conclusion, some of the biggest browser security wins over the past couple of years have come from browser vendors defending against other peoples' problems. So I repeat the hypothesis:

The security of a given browser is dominated by how much effort it puts into other peoples' problems

Funny world we live in.

Sunday, July 3, 2011

Alert: vsftpd download backdoored

[With thanks to Mathias Kresin for being the first to notice]

An incident, what fun! Earlier today, I was alerted that a vsftpd download from the master site (vsftpd-2.3.4.tar.gz) appeared to contain a backdoor:

http://pastebin.com/AetT9sS5

The bad tarball is (sha256sum):

2a4bb16562e0d594c37b4dd3b426cb012aa8457151d4718a5abd226cef9be3a5 vsftpd-2.3.4.tar.gz

And, of course, the GPG signature notices:

$ gpg ./vsftpd-2.3.4.tar.gz.asc
gpg: Signature made Tue 15 Feb 2011 02:38:11 PM PST using DSA key ID 3C0E751C
gpg: BAD signature from "Chris Evans <chris@scary.beasts.org>"

Check your signatures :)

Ideally, you'll see something like:

gpg: Signature made Tue 15 Feb 2011 02:38:11 PM PST using DSA key ID 3C0E751C
gpg: Good signature from "Chris Evans <chris@scary.beasts.org>"
Primary key fingerprint: 8660 FD32 91B1 84CD BC2F 6418 AA62 EC46 3C0E 751C


Signatures aside, I also took the liberty of moving most of the vsftpd site and latest download to a hosting provider I have more faith in:

https://security.appspot.com/vsftpd.html
https://security.appspot.com/downloads/vsftpd-2.3.4.tar.gz
https://security.appspot.com/downloads/vsftpd-2.3.4.tar.gz.asc

The backdoor payload is interesting. In response to a :) smiley face in the FTP username, a TCP callback shell is attempted. There is no obfuscation. More interestingly, there's no attempt to broadcast any notification of installation of the bad package. So it's unclear how victims would be identified; and also pretty much guaranteed that any major redistributor would notice the badness. Therefore, perhaps someone was just having some lulz instead of seriously trying to cause trouble.

Friday, May 27, 2011

libxml vulnerability and interesting integer issues

A while ago, I was playing with grammar-based XPath fuzzing and I found and fixed an interesting libxml bug. The commit, for the curious, is here:

http://git.gnome.org/browse/libxml2/commit/?id=d7958b21e7f8c447a26bb2436f08402b2c308be4

The trigger for this bug was the XPath expression: //@*/preceding::node()/ancestor::node()/ancestor::foo['foo'] which for some reason I haven't yet analyzed leads to a pathologically large collection of nodes within libxml.

As the nodeset is grown and grown, things get interesting when the system runs out of memory whilst trying to double the size of the nodeset:

cur->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, "growing nodeset\n");
return;
}
cur->nodeTab = temp;

Notice how the max number of allocated nodes in the "cur" structure is doubled before doing, checking and assigning the reallocation. This means that in the event of a realloc() failure, one of two things will happen:
  • If you malloc() implementation exits the process upon alloc failure (such as Chromium), lucky you! You dodged a bullet.

  • More typically, you'll exit this function with "cur" in an inconsistent state, i.e. it indicates it can hold more data than it really can.
Despite the call to xmlXPathErrMemory(), XPath processing continues with "cur" in an inconsistent state, leading to a heap-based buffer overflow.

The fix is to only update the structure's "max size" member after a successful expansion of the underlying array. libxml already did this in most places, using a paradigm such as:

temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, "growing nodeset\n");
return;
}
cur->nodeMax *= 2;
cur->nodeTab = temp;

... which leads us nicely into part 2:

Interesting integer issues

Even with the fix applied, there are some really interesting integer issues going on here. The astute will have noticed a possible integer overflow in the argument to xmlRealloc(), which is a general hazard with the common pattern of "double the size of the array if we ran out of room". Digging in to more detail, we see a fascinating difference in behaviour between 32-bit and 64-bit builds and maybe even learn a thing or two about integer promotion rules.

On 32-bit builds

First, let's quickly note that cur->nodeMax is of type int. That's likely the wrong type for a couple of reasons, but we will run with it for our analysis.
On 32-bit, sizeof(int) == sizeof(size_t) so we're looking at a fairly basic possible integer overflow. But can it ever be triggered? Does the 32-bit address space offer enough room?
The case with the least space requirements for the 32-bit address space is the case where we already have a 2GB (2^31) allocation and are attempting to double it -- leading to an integer overflow at 2^32 and an attempt to realloc() to 0 bytes.
But to arrive at the 2^31 allocation, we need to have a 1GB -> 2GB realloc() succeed first.
This is actually unlikely on 32-bit Linux -- which typically has just the lower 3GB of address space usable. 1GB + 2GB + program text etc. won't simultaneously fit, so the only way 1GB -> 2GB realloc() will succeed is if the allocation can be expanded in-place. glibc malloc() will typically use mmap() for large allocations, and put them towards the top of address space in order to reserve room for standard heap expansion. So realloc() on such an mmap()ed chunk typically won't have room to mremap() to a larger size.
All in all, this seems to be a hard-to-exploit bug, unless the attacker has a lot of control over other allocations (to influence the placement of the 1GB mmap() lower down in the address space and then free() whatever chunks were needed to do that, before the realloc()). Further research might be merited for other allocators (e.g. tcmalloc) and 32-bit-process-on-64-bit-host (>3GB address space available).

On 64-bit builds

With 64-bit, we have a much larger address space. This means that we should be able to successfully allocate the growing array -- and the objects contained within it -- right up until the fault condition.

The fault condition is that cur->nodeMax * 2 will eventually become negative. This negative int is then multiplied by sizeof(xmlNodePtr). Remember that sizeof() returns a size_t, so we have an int -> size_t promotion at this point. This will result in sign-extension to a 64-bit size, which will end up massive and the system allocator will not be able to satisfy an allocation of that size. Therefore, a lucky lack of impact on 64-bit.

Finally, note that there's a real subtlety in the ordering of the expression given to xmlMalloc():

cur->nodeMax * 2 * sizeof(xmlNodePtr)

Things would be very different if we had:

cur->nodeMax * sizeof(xmlNodePtr) * 2

Promotions for left-to-right operator sequences are done in strict left-to-right ordering, as opposed to some kind of overall max(precedence) promotion. Therefore, in this latter case, the initial negative-related failure will be avoided; we're looking at:

(int) 2^30 -> (int) -(2^31) -> (size_t) 0xffffffff80000000 * 8 == (size_t) 0xfffffffc00000000
vs.
(int) 2^30 -> (size_t) 2^30 * 8 -> (size_t) 2^30 * 8 * 2 == (size_t) 2^34

Seeing that nodeNr is just an int, we would likely see subsequent memory corruption if nodeNr goes subsequently negative after its increment in a statement such as cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);

Wednesday, May 25, 2011

Bug bounties vs. black (& grey) markets

I'm just back from the fun that was HiTB Amsterdam 2011. (Plug: you should check out one of the HiTB series if you haven't yet; Dhillon and crew invariably put a good, intimate conf together).

I sat on the day 2 keynote panel on "The economics of vulnerabilities". As usual, talking about this topic was great fun and the audience asked some great questions. Predictably, the topic strayed on to black market sales as an interesting sub-discussion. With 6 people on the panel, it was hard to cover this in the detail it deserves, and I think a few important subtleties were missed. I'll try to cover some of them here.

Vulnerability reward programs do not "buy" bugs, nor do they aim to compete with the black market
Remember that the black (or grey) markets buy exploits, not vulnerabilities. The latter are just the first step towards exploits, which are hard to write on modern software. Also remember that reward programs are not buying even vulnerabilities. Typically, they are a "thank you" mechanism for talented researchers who used their skills to make things better.
Also, there's often a separation of which researchers participate where, along ethics lines; see below.

A vulnerability reward program will indirectly compete with black market sales
It's interesting to note that a reward program doesn't have to outbid dubious markets in order to have a benefit in this area. These days, there's a lot of independent rediscovery of the same vulnerabilities -- ZDI quoted 22 "collisions" for the most recent year.
So any motivation you can provide for white hats to discover vulnerabilities will inevitably kill the occasional black market vulnerability.
A quick story in support: the WebKit vulnerability used by VUPEN to pwn Safari at this years' pwn2own competition was independently reported to the Chromium project by researcher Martin Barbella. Thanks to Inferno's lightning quick fix, Chrome entered pwn2own without that bug. Martin, of course, received a $1000 Chromium Security Reward (on top of all his others).

Black / grey market sales are a dangerous alternative to consider
Each researcher has to set their own ethics, of course. Hopefully, most of us get into this industry to make users safer and software more secure. Aside from reward programs and ZDI, there's also a large number of well-paid security jobs sponsored by corporations, so no need to start selling exploits to feed the family.
If you sell an exploit to someone, it's basically going to be used to exploit end users of the software. This could harm a lot of people if the target is mass malware for financial gain. Or it could seriously harm some targeted individuals if a government of dubious human rights commitment gets their hands on it.

"Credit", whilst important, is not a full replacement for a monetary reward
To be clear about it: if you launch a vulnerability reward program, you will receive more vulnerability reports from a wider range of researchers. The power of credit and prestige is often cited as an argument to not launch a reward program, but the fact remains that you will get more reports if you have a program in place. And as long as you have a culture of fixing security bugs promptly, your users will be safer thanks to having a reward program.

Wednesday, April 27, 2011

Fiddling with Chromium's new certificate pinning

Over the past few years, there have been various high-profile incidents and concerns with the Certificate Authority-based infrastructure that underpins https connections. Various different efforts are underway to tackle the problem; many are enumerated here:

http://googleonlinesecurity.blogspot.com/2011/04/improving-ssl-certificate-security.html

And in terms of things baked directly into the browser, we have things like Firefox's Certificate Patrol add-on:

https://addons.mozilla.org/en-US/firefox/addon/certificate-patrol/

My colleague Adam Langley summarized some features and directions we've been exploring in Chromium recently, it's a good read:

http://www.imperialviolet.org/2011/05/04/pinning.html

These features can also be controlled via the command-line, so to give a glimpse of the future, I present to you:

Twitter Like A Boss

Run Chrome (v12 dev channel or newer required) with a command line like this:

google-chrome --user-data-dir=/tmp/chrome_twitter --incognito --disable-plugins --proxy-server=localhost:1 --proxy-bypass-list=https://twitter.com,https://*.twitter.com,https://*.twimg.com --hsts-hosts='{"df0sSkr4gOg4VK8d/NNTAWFtAN/MjCgPCJ5ml+ucdZE=":{"expiry":2000000000.0,"include_subdomains":true,"mode":"strict","public_key_hashes":["sha1/TXoScD1SXPfhmRO8ACTPrkXD9Yk="]},"tGm+XsbBPK211uMWtg2k071vijQkuVLvd62QzfNFol8=":{"expiry":2000000000.0,"include_subdomains":true,"mode":"strict","public_key_hashes":["sha1/06curQTaPH4PGumbNSeL79da23s="]},"wZU3atDOXaxKkaRgSdlWwB4UYjulRq46SGnIBij5I98=":{"expiry":2000000000.0,"include_subdomains":true,"mode":"strict","public_key_hashes":["sha1/O6hykhOmHJ5HQUREC0DTDeu6+mE="]}}' --user-agent='LIKE A BOSS'

(You'll need to edit a couple of things such as the command name and the temp directory if you're on Windows or Mac).

If you wish to connect securely to Twitter, well it pretty much does so... like a boss. It does the following things and defends against the following situations:

  • The --user-data-dir flag loads Twitter in a new profile so that you get a new Chrome instance and therefore new cookie jar. Therefore, carelessly clicked links in your other browsing windows won't get you XSSed.

  • The --incognito flag applies the usual incognito changes; notably, things like profile photos won't be cached to disk; might be useful if you're an activist.

  • --disable-plugins is strictly unnecessary since Twitter generally isn't using plug-ins. However, any "secure" command line should likely include that flag.

  • The --proxy-server=localhost:1 is a good defensive catch-all which will stop any site traffic being sent by your browser unless it is whitelisted. Specifically, a http://bit.ly/ link to an XSS payload won't work on you. (You'll need to paste such links into an alternate browser which shouldn't be logged in to Twitter). This will also stop non-pinned https requests going out (which might otherwise compromise the integrity of the main page). Mixed-content bugs, cookie forcing and failure to mark cookies "Secure" will also be mitigated.

  • --hsts-hosts is the magic. It locks twitter.com, api.twitter.com and twimg.com such that SSL traffic from/to Twitter will only be accepted if the leaf SSL certificate's public key is exactly what we expect. It's called "certificate pinning", and along with HSTS, it defends against any compromised root CA, Comodo-gate, Tunisia-like sslstrip attacks, and the "evil country owns firewall + CA" situation.

  • --user-agent='LIKE A BOSS' is strictly optional, depending on your mood.

The above command line isn't finished. Although Twitter seems to run fine, there are under-the-hood failures to scribe.twitter.com and other places because I haven't added the correct certificate pin for that host. The above will break if any of the leaf certificate public keys change (this doesn't necessarily happen on expiry rollover but may otherwise happen for various reasons).

Hopefully this demo is compelling. The plan is to push this technology more and more under the covers so that it happens for less technical users who have an empty command line!

Wednesday, March 9, 2011

Multi-browser heap address leak in XSLT

It's not often that I find a bug that affects multiple different codebases in the same way, but here is an interesting info-leak bug that is currently unpatched in Firefox, Internet Explorer and Safari.

I'm releasing it now for a few reasons:
  1. The bug was already publicly noted here.

  2. This bug cannot damage anyone in and of itself; it's a low severity info-leak that does not corrupt anything. It needs to be paired with other bugs, perhaps as an exploit aid against ASLR.

  3. This is a rare and unique opportunity to directly compare vendor responses and response times for a near-identical bug. It's nice that this is a lower-severity issue as all vendors tend to treat critical issues with at least some urgency; lower severity issues serve as a better differentiator.

The bug
The bug is in the generate-id() XPath function, and is sometimes used in XSL transforms. Here's an web page that simply calls generate-id() and renders the result as a web page:

https://cevans-app.appspot.com/static/genid.xml

Let's see how this renders in different browsers:

Firefox (64-bit Linux)
id0x00007fbac51c1000

There is no "obfuscation" that this is a raw heap address. Since Firefox is open source, we can go and look at the source code to find that indeed, the string is generated from a pointer (txXPathNodeUtils::getXSLTId):
const char gPrintfFmt[] = "id0x%016p";

Internet Explorer 8 (Windows 7)
IDAW0MLB

Doesn't look like a heap address, does it? If, however, you strip off the "ID" prefix and treat the string as a [A-Z0-5] base32 encoded "little endian" string, you resolve to a nice heap address. At that address is a pointer in msxml.dll, possibly the address of a vtable for some internal xml node class.

Safari 5 (Mac OS X)
id35865226

Also does not immediately look like a heap address, but libxslt is doing a simple transform on a heap address:

val = (unsigned long)((char *)cur - (char *)0);
val /= sizeof(xmlNode);
sprintf((char *)str, "id%ld", val);

Opera
o14022440
o2148150600
These object ids bounce around all over the place. I don't know what is going on so I'm not making the claim that Opera is affected.

Chrome
Latest stable Chrome (Chrome 10) is not affected. It has been removed from the "time to fix" competition in order to keep things fair.


It's on!! Who will fix it first and who will be the security laggard? Updates to be provided via Twitter: @scarybeasts

Tuesday, March 8, 2011

Busy Chrome day...

I did a bunch of fairly interesting things with my corporate hat on today (not to be confused with any of my personal research ;-)

Firstly, Chrome 10 went out with a record $16k+ series of rewards. It's continually humbling to see such a wide range of researchers and a wide range of bug categories!

http://googlechromereleases.blogspot.com/2011/03/chrome-stable-release.html

Also, there are some nice new security pieces in Chrome 10. I blogged about some of these:

http://blog.chromium.org/2011/03/mini-newsletter-from-your-google-chrome.html

My personal favourite is "plug-in blocking enhancements", probably because I implemented it and am therefore biased :-) In reality, the change that's going to really help end user security is "out-of-date plug-in warnings". Users are encouraged to update to the latest security patches for their plug-ins. I personally believe this will be particularly helpful for Java, which is widely installed but users are not always the most uptodate.

And then I spoke at SANS AppSec with Adam Mein about Google's two vulnerability reward programs (Chromium and Web). This seemed to be very well received, as evidenced by the stack of insightful questions. We released a few new stats and charts, so it's probably worth me linking to the slides:

https://docs.google.com/present/edit?id=0Ae_usSLlqH60ZGZnYjI0NTVfMjBobngybWRoaA&hl=en

All in all a fun day!