Lab: Web cache poisoning via HTTP2 request tunnelling

After starting the exercise, the blog will look similar to the image shown.

In Burp Suite, we open Burp Proxy and then the HTTP history tab. Here, we search for the first request to the application that begins with the following line: GET / HTTP/1.1. We send this request to Burp Repeater using the key combination CTRL + R.

In Burp Repeater, we expand Request attributes in the Inspector and activate HTTP/2.

We now activate the Show non-printable chars function in Burp Repeater. This makes all \r\n visible and copyable in the Request section.

In the Inspector, we expand Request headers and click on the > in the :path line.

In the Value field, we now enter the following request. The \r\n characters should be copied from the Request section, as entering them manually can lead to errors. We then confirm the entry by clicking on Apply changes.

The following message now appears in the Request section.

We send the request to the application and receive an HTTP/2 200 OK response. From this, we can conclude that we can inject via the path :path. In the Inspector, we now change the request method to HEAD. To do this, we expand Request attributes in the Inspector, click on > in the first line and enter HEAD in the Value field. We then confirm the entry by clicking on Apply changes.

In the Inspector, we expand Request headers again, go back to the :path line and click on >. We now adjust the Value field as follows.

Then click on Apply changes.

Note that we have ensured that the main request is valid by inserting a Host header before splitting. We have also left any trailing headers in place to capture the HTTP/1.1 suffix that is appended to the request line when rewriting from the front end.

The Age header in the response should contain a value less than 15 seconds. If the value reaches 30 seconds, the cache is refreshed and you receive a HTTP/2 400 Bad Request Response.

When we send this request, we receive an HTTP/2 200 response containing another HTTP/2 200 response. If you receive a different response (status codes 500 and so far), change the postId to a different number. It could be possible, that you have to change the postId more than once. The following code snippet shows an excerpt from the response.

We now switch back to the Inspector in the Request headers section and remove everything from :path except for /?cachebuster=2 HTTP/1.1\r\n and confirm the entry with Apply changes.

When we send this request, we receive the same response, confirming that we have poisoned the cache with our tunneled response.

Now we need to find a gadget that reproduces an HTML-based XSS payload without encoding or escape characters. We switch to Burp Proxy and send the first request GET / HTTP/1.1 to Burp Repeater again using the key combination CTRL + R. In Burp Repeater, we insert the endpoint /resources and send it to the application.

We receive the following response. It contains a redirect to /resources/.

In our previous request, we now adjust our path :path as follows and confirm by clicking on the Apply changes button.

When we send this request, we receive an HTTP/2 500 Internal Server Error response, which tells us that we have a timeout on the connection. In Burp Proxy, we look at the value of the Content-Length header in the response of a normal GET / HTTP/1.1 request.

In Burp Repeater, we adjust our path :path in the Inspector under Request headers as follows. The value of the Content-Length header is 8473, so we need to insert enough characters after </script> to exceed 8473.

If we insert too many characters and send a request, we receive a message stating that there are too many characters. If we are just over 8473 characters, we receive the following response.

From our request, we now copy /?cachebuster=3 and switch to the browser. There, we add /?cachebuster=3 to the end of the address bar and confirm with ENTER. The following window appears.

In the Inspector, we now remove the ?cachebuster=3 from the request and send the request to the application. Then we wait 5 seconds and send the request again to keep the cache poisoned. The other user will access the website at some point, and then the exercise is complete.

Video solution

Last updated