Siunam's Website

My personal website

Home About Blog Writeups Projects E-Portfolio

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.

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:

  1. Web cache poisoning via HTTP/2 request tunnelling