Send a request for
GET /to Burp Repeater. Expand the Inspector's Request Attributes section and make sure the protocol is set to HTTP/2.
Using the Inspector, try smuggling an arbitrary header in the
:pathpseudo-header, making sure to preserve a valid request line for the downgraded request as follows:
/?cachebuster=1 HTTP/1.1\r\n Foo: bar
Observe that you still receive a normal response, confirming that you're able to inject via the
Change the request method to
HEADand use the
:pathpseudo-header to tunnel a request for another arbitrary endpoint as follows:
/?cachebuster=2 HTTP/1.1\r\n Host: YOUR-LAB-ID.web-security-academy.net\r\n \r\n GET /post?postId=1 HTTP/1.1\r\n Foo: bar
Note that we've ensured that the main request is valid by including a
Hostheader before the split. We've also left an arbitrary trailing header to capture the
HTTP/1.1suffix that will be appended to the request line by the front-end during rewriting.
Send the request and observe that you are able to view the tunnelled response. If you can't, try using a different
Remove everything except the path and cachebuster query parameter from the
:pathpseudo-header and resend the request. Observe that you have successfully poisoned the cache with the tunnelled response.
Now you need to find a gadget that reflects an HTML-based XSS payload without encoding or escaping it. Send a response for
GET /resourcesand observe that this triggers a redirect to
Try tunnelling this request via the
:pathpseudo-header, including an XSS payload in the query string as follows.
/?cachebuster=3 HTTP/1.1\r\n Host: YOUR-LAB-ID.web-security-academy.net\r\n \r\n GET /resources?<script>alert(1)</script> HTTP/1.1\r\n Foo: bar
Observe that the request times out. This is because the
Content-Lengthheader in the main response is longer than the nested response to your tunnelled request.
From the proxy history, check the
Content-Lengthin the response to a normal
GET /request and make a note of its value. Go back to your malicious request in Burp Repeater and add enough arbitrary characters after the closing
</script>tag to pad your reflected payload so that the length of the tunnelled response will exceed the
Content-Lengthyou just noted.
Send the request and confirm that your payload is successfully reflected in the tunnelled response. If you still encounter a timeout, you may not have added enough padding.
While the cache is still poisoned, visit the home page using the same cachebuster query parameter and confirm that the
In the Inspector, remove the cachebuster from your request and resend it until you have poisoned the cache. Keep resending the request every 5 seconds or so to keep the cache poisoned until the victim visits the home page and the lab is solved.
Lab: Web cache poisoning via HTTP/2 request tunnelling
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.