Adam Innes · Blog

Patch Apache: The Range Header DoS

· 4 min · apache, security, linux

Around August 20 someone posted a Perl script to the Full Disclosure mailing list under the subject “Apache Killer”. Four days later the Apache HTTP Server project put out an advisory for CVE-2011-3192, saying the tool was circulating and being actively used, and that a modest number of requests could cause very significant memory and CPU usage on a default install. The fixed release, 2.2.20, has been out for a week now. If you run Apache 2.2 and haven’t upgraded yet, this is your nudge.

Byte ranges, many times over

HTTP lets a client ask for part of a file instead of the whole thing. That’s how download managers resume, and how a PDF reader or a video player can jump to the middle of a file. The client sends a Range header such as bytes=0-499, and the server answers with a 206 and just those bytes. RFC 2616 also lets you ask for several ranges in one header, separated by commas, and the server sends them back in one multipart response.

Nothing in that says the ranges can’t overlap. Apache’s advisory points out that the current reading of the protocol means a server should return overlapping ranges in the order they were requested, so a client can ask for a huge range, even the whole file from byte 0 to the end, hundreds of times in a single request. Inside Apache, that one request turns into hundreds of large fetches that are all held in memory in an inefficient way. The attacker sends one modest request and the server does a huge amount of work. Send a bunch of those at once and the box runs out of memory.

One detail from the advisory is worth knowing if you tested with the attack script: most versions check for mod_deflate and will say you’re safe if it isn’t loaded. The vulnerability doesn’t depend on mod_deflate, so a “not vulnerable” from the script tells you nothing.

The stopgap Apache published

Before a release existed, the advisory offered a few mitigations. The one I’d reach for on 2.2 uses mod_setenvif and mod_headers to drop the Range header when it holds too many ranges, and to drop the legacy Request-Range header (a leftover from Netscape Navigator 2 and 3 and MSIE 3) entirely:

# CVE-2011-3192: ignore Range when it has more than 5 ranges
SetEnvIf Range (?:,.*?){5,5} bad-range=1
RequestHeader unset Range env=bad-range
RequestHeader unset Request-Range
CustomLog logs/range-CVE-2011-3192.log common env=bad-range

A client sending a sane header still gets partial content, and anyone sending a long list of ranges just gets the full file. The advisory calls the limit of 5 arbitrary and says a limit of several tens should be fine, and may be needed if you serve PDFs to e-readers or do HTTP video streaming. It also warns that this may not work everywhere, because mod_cache or language modules can act before the header gets unset. For older setups there’s a mod_rewrite version that rejects requests with too many ranges outright, and the blunt option is RequestHeader unset Range for everyone, which can break e-readers and streaming video. The advisory also suggests shrinking LimitRequestFieldSize to a few hundred bytes, but warns that can break other headers such as sizeable cookies, so I’d skip it.

Whichever you pick, make sure it covers every virtual host (mod_rewrite rules in the main server config aren’t inherited by virtual hosts by default), and the log line is handy for seeing whether anyone is actually trying this against you.

Then upgrade

The real fix is in 2.2.20, which Apache’s 2.2 vulnerabilities page lists as released on August 30 and rates as important. According to the CHANGES file, byte range requests now use less memory, and if the ranges in a request add up to more than the size of the file, Apache ignores them and sends the whole file. That’s a sensible trade: a legitimate client asking for more than the whole file in pieces can live with getting the whole file.

If your Apache comes from your distribution, you’ll want the distro’s patched package rather than building 2.2.20 yourself, so check its security announcements and compare package versions instead of looking for the upstream number. If you’re still on Apache 2.0, no upstream 2.0 release has the fix yet. Take your distribution’s patched package if it has one; otherwise the config mitigation is your stopgap, and it’s a good moment to plan the move to 2.2.

After upgrading, check the version with httpd -v (or apache2 -v), and on a distro build check the package version with rpm -q httpd or dpkg -l apache2 instead, since the upstream number won’t tell you whether the patch is in, make sure the service really restarted, and keep an eye on memory for a day or two. I’d leave the SetEnvIf rules in place for a while too. They cost next to nothing, and a little defense in depth is welcome when the attack script is this easy to run.

← all posts