natechoe.dev The blog Contact info Other links The github repo

DoSing web browsers with hotlinked images

A few weeks ago I read this article and this earlier article which probably inspired the first about protecting web servers with zip bombs. The idea is that the HTTP Content-Encoding and Transfer-Encoding headers allow you to send highly compressed data over the web, so you might spend just 10 MB of bandwidth to send a 10 GB payload to a client. If your client is a malicious bot that's running on a VPS with just 4 GB of memory, it will theoretically run out of memory while decompressing the message you've sent it and die. This sort of pathologically compressed payload is called a "zip bomb", and both of the articles I linked use the same technique to create one:

# create a very large file of just zero bytes
dd if=/dev/zero of=data.raw bs=1G count=10

# compress it down for use with the Content-Encoding HTTP header
gzip data.raw

# alternatively, combine both of these steps into a pipe
dd if=/dev/zero bs=1G count=10 | gzip > data.gz

To me, this feels very inefficient for two reasons. The first is that it only uses a single layer of compression. Under the hood, gzip uses the Deflate compressed data format, which can only achieve a maximum of 1032x compression on very very repetitive files. HTTP allows us to specify multiple data encodings, so we could theoretically compress our data multiple times to achieve exponentially higher levels of compression.

The second problem is that when we start with uncompressed data and then compress it, we have to work with the uncompressed data. If we want to use two layers of decompression to create a 1 MB payload, we would have to start with a 1 TB file, which means we'd have to wait for several hours as gzip churns through 1 TB of data. This is a unique problem because it only gets worse as your zip bombs get more efficient. By building our payloads this way, we're basically just hoping that we have more resources than the enemy.

One weekend of hacking later, I created my own pretty big zip bomb. I decided to use zlib instead of gzip because it's a slightly simpler file format that's still basically universal among HTTP implementations. The compressed payload is just 1 MB, but it decompresses twice to over 1 TB. It was created entirely by hand by typing ones and zeros into a text editor.

One interesting use case for these sorts of payloads is as a denial of service attack through hotlinked assets. Some websites allow you to create custom profile pages with HTML, including external resources such as CSS and images. I thought it would be funny to create a button which loads a very large image and crashes the browser, like this:

<details>
<summary>Click here to crash your web browser</summary>
<img src=bomb/bomb.svg loading=lazy />
</details>

The idea is that when you click to expand the details element, your browser loads the lazy image, runs out of memory, and crashes. I haven't actually put this on any actual profiles for actual websites, but I have created this demo site where you can try it out for yourself. I've also disabled hotlinking, so if you want to try this yourself you'll have to host my payload somewhere else.