Web cache poisoning to exploit a DOM vulnerability via a cache with strict cacheability criteria | Jan 23, 2023
Introduction
Welcome to my another writeup! In this Portswigger Labs lab, you’ll learn: Web cache poisoning to exploit a DOM vulnerability via a cache with strict cacheability criteria! Without further ado, let’s dive in.
- Overall difficulty for me (From 1-10 stars): ★★★☆☆☆☆☆☆☆
Background
This lab contains a DOM-based vulnerability that can be exploited as part of a web cache poisoning attack. A user visits the home page roughly once a minute. Note that the cache used by this lab has stricter criteria for deciding which responses are cacheable, so you will need to study the cache behavior closely.
To solve the lab, poison the cache with a response that executes alert(document.cookie)
in the visitor’s browser.
Exploitation
Home page:
Burp Suite HTTP history:
In here, we see the /
doesn’t allow cache.
View source page:
[...]
<script>
data = {
"host":"0ad3008703db2f5fc1725345009d0044.web-security-academy.net",
"path":"/",
}
</script>
[...]
<script type="text/javascript" src="/resources/js/geolocate.js"></script>
[...]
<script>
initGeoLocate('//' + data.host + '/resources/json/geolocate.json');
</script>
As you can see, the website will load a JavaScript called geolocate.js
:
function initGeoLocate(jsonUrl)
{
fetch(jsonUrl)
.then(r => r.json())
.then(j => {
let geoLocateContent = document.getElementById('shipping-info');
let img = document.createElement("img");
img.setAttribute("src", "/resources/images/localShipping.svg");
geoLocateContent.appendChild(img)
let div = document.createElement("div");
div.innerHTML = 'Free shipping to ' + j.country;
geoLocateContent.appendChild(div)
});
}
It also ran a JavaScript function called initGeoLocate()
, which parses the data.host
JSON data.
geolocate.json
:
{
"country": "United Kingdom"
}
Let’s break geolocate.js
down!
When the initGeoLocate()
function is called, it’ll:
- Send a GET request to
geolocate.json
and fetches it’s JSON content - Then it’ll create an
<img>
element, and set thesrc
attribute tolocalShipping.svg
- After that, create an
<div>
element, and usinginnerHTML
sink (Dangerous function) to append thegeolocate.json
’scountry
value.
Armed with above information, we can try to exploit DOM-based XSS via the innerHTML
sink in geolocate.js
!
But first, we need to find the source (Attacker’s controlled input).
In the view source page, we found this:
<script>
data = {
"host":"0ad3008703db2f5fc1725345009d0044.web-security-academy.net",
"path":"/",
}
</script>
If we can control the data.host
value, we can basically load any JSON file from anywhere!
The evil JSON file can contain an XSS payload:
{
"country": "<img src=errorpls onerror=alert(document.cookie)>"
}
After it fetches our evil JSON file, it’ll append our XSS payload to the j.country
in the geolocate.js
JavaScript file, which will then trigger our XSS payload!!
After some trial and error, I found that the web application accept X-Forwarded-Host
HTTP header!!
That being said, we can override the data.host
value!!
We now can go to exploit server, and host our evil JSON file!
Then, intercept the /
GET request, and add the X-Forwarded-Host
HTTP header with the exploit server domain:
Forward the request:
Nope. That doesn’t work.
Burp Suite HTTP history:
By studying the request histories, we found that our normal request has been cached!
Armed with above information, we can wait for the normal request’s cache dies, and poison the cache with our X-Forwarded-Host
:
Then, go to /
, we should be able to trigger the XSS payload:
Nope…
Our evil JSON file didn’t loaded because of the CORS (Cross-Origin Resource Sharing) Policy…
Luckly, we can add a HTTP header called Access-Control-Allow-Origin
!
That being said, go back to the exploit server, and add a HTTP header:
Now poison the cache again, and we should trigger the XSS payload:
Nice!!!
When a victim visit the website, the poisoned cache will be delivered to the victim, and thus trigger our XSS payload!!
What we’ve learned:
- Web cache poisoning to exploit a DOM vulnerability via a cache with strict cacheability criteria