Web cache poisoning via HTTP/2 request tunnelling | Feb 26, 2023
Introduction
Welcome to my another writeup! In this Portswigger Labs lab, you’ll learn: Web cache poisoning via HTTP/2 request tunnelling! Without further ado, let’s dive in.
- Overall difficulty for me (From 1-10 stars): ★★★★★★★☆☆☆
Background
This lab is vulnerable to request smuggling because the front-end server downgrades HTTP/2 requests and doesn’t consistently sanitize incoming headers.
To solve the lab, poison the cache in such a way that when the victim visits the home page, their browser executes alert(1)
. A victim user will visit the home page every 15 seconds.
The front-end server doesn’t reuse the connection to the back-end, so isn’t vulnerable to classic request smuggling attacks. However, it is still vulnerable to request tunnelling.
Note:
This lab supports HTTP/2 but doesn’t advertise this via ALPN. To send HTTP/2 requests using Burp Repeater, you need to enable the Allow HTTP/2 ALPN override option and manually change the protocol to HTTP/2 using the Inspector.
Please note that this feature is only available from Burp Suite Professional / Community 2021.9.1.
Exploitation
Home page:
In here, we can try to send a HTTP/2 request to see the web application accept it or not:
It accepts HTTP/2 requests.
Then, we can try to smuggle an arbitrary header in the :path
pseudo-header:
As you can see, we still receive a normal response, which means we’re able to inject arbitrary headers via the :path
.
Then, we can use the request tunnelling to perform web cache poisoning.
First, change the request method to HEAD:
Next, use the :path
pseudo-header to tunnel a request for another arbitrary endpoint:
Send the request, and we should able to view our tunnelled request:
Note: If you recieved timed out response, try to change different
postId
.
Now, what if we remove everything except the path and cachebuster query parameter from the :path
pseudo-header and resend the request?
As you can see, we successfully poisoned the cache with the tunnelled response!
Now we need to find a gadget that reflects an HTML-based XSS payload without encoding or escaping it.
When we send a GET request to /resources
, it’ll redirect us to /resources/
:
Hmm… What if we tunnelling this request via the :path
pseudo-header?
Timed out… Which means the Content-Length
header in the main response is longer than the nested response to our tunnelled request.
In the normal GET /
request, we can see that the Content-Length
header’s value is 8512
:
We can append a padding to the /resources
path:
┌[siunam♥earth]-(~/ctf/Portswigger-Labs/HTTP-Request-Smuggling)-[2023.02.26|13:11:04(HKT)]
└> python3 -c "print('A'*8512)"
AAA[...]
Nice! it’s reflected in the tunnelled response.
Now, what if I add a XSS payload in the /resources
path?
┌[siunam♥earth]-(~/ctf/Portswigger-Labs/HTTP-Request-Smuggling)-[2023.02.26|13:15:52(HKT)]
└> python3
[...]
>>> payload = '<script>alert(1)</script>'
>>> print(payload + 'A' * (8512 - len(payload)))
<script>alert(1)</script>AAAAAAAAAAAAA[...]
After the cache is hitted, go to /?cachebuster=1
:
Nice! We successfully triggered our XSS payload!
Finally, remove the cache buster:
The victim should see:
What we’ve learned:
- Web cache poisoning via HTTP/2 request tunnelling