閒聊

[資安]Host header attack

Description
In many cases, developers are trusting the HTTP Host header value and using it to generate links, import scripts and evengenerate password resets links with its value. This is a very bad idea, because the HTTP Host header can be controlled byan attacker. This can be exploited using web-cache poisoning and by abusing alternative channels like password resetemails.

在許多情況下,開發人員信任 HTTP 主機標頭值,並使用它來產生連結、匯入腳本,甚至使用其值產生密碼重設連結。這是一個非常糟糕的主意,因為 HTTP 主機頭可以被攻擊者控制。這可以透過網路快取中毒和濫用密碼重置電子郵件等替代管道來利用。
Impact
An attacker can manipulate the Host header as seen by the web application and cause the application to behave inunexpected ways.

攻擊者可以操縱 Web 應用程式所看到的 Host 標頭,並導致應用程式出現意外行為。
Recommendation
The web application should use the SERVER_NAME instead of the Host header. It should also create a dummy vhost thatcatches all requests with unrecognized Host headers. This can also be done under Nginx by specifying a non-wildcardSERVER_NAME, and under Apache by using a non-wildcard serverName and turning the UseCanonicalName directive on.Consult references for detailed information.

Web 應用程式應使用 SERVER_NAME 而非 Host 標頭。它還應該建立一個虛擬虛擬主機,捕獲所有具有無法識別的主機標頭的請求。這也可以在 Nginx 下透過指定非通配符SERVER_NAME 來完成,在 Apache 下也可以透過使用非通配符 serverName 並開啟 UseCanonicalName 指令來完成。

解決方法

你也可以透過如php的方式直接檢查來源的header是否跟你的domain一致,當然缺點就是你有多少頁的php就要每頁都檢查,是比較麻煩

或是你可以直接在apche直接全域設定,(當然是你的主機只能有一個domain的情況下)

sudo nano /etc/apache2/apache2.conf
#或是你可能是httpd.conf

然後在文件內新增一行

RequestHeader set Host blog.davidou.org
#請記得把上面網址換成你的domain網址

之後重開你的apache即可

service apache2 restart

下面是一個測試你有沒有修復完成的python範例,你可以修復前先打一次看看,請記得把裡面的測試目標改為你的網站domain

import requests
import re
import urllib3

# 忽略 SSL 憑證驗證警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# 測試目標
TARGET_URL = "https://www.microsoft.com/"  # 你的測試網址 (可更改)
EVIL_HOST = "evilhostupu8j9r6.com"  # 模擬攻擊者的 Host

# 設定請求標頭
headers = {
    "Host": EVIL_HOST,
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
}

try:
    # 發送請求 (不檢查 SSL 憑證)
    response = requests.get(TARGET_URL, headers=headers, verify=False, timeout=5)
    
    # 顯示回應碼
    print(f"HTTP 狀態碼: {response.status_code}")

    # 檢查是否有回應內容
    if response.status_code == 200:
        response_text = response.text

        # 搜尋 HTML 內容是否包含惡意 Host
        if re.search(EVIL_HOST, response_text, re.IGNORECASE):
            print(" 可能存在 Host Header Injection!回應內容包含惡意 Host!")
        else:
            print(" 沒有發現惡意 Host,漏洞可能已修復!")
    
    elif response.status_code in [400, 403]:
        print(" 伺服器已正確拒絕異常 Host (400/403 Forbidden)")
    
    else:
        print(f" 伺服器回應碼: {response.status_code},請手動檢查內容")

except requests.exceptions.ConnectionError:
    print(" 連線失敗!請確認伺服器是否可用。")
except requests.exceptions.Timeout:
    print(" 請求超時!請確認伺服器的回應速度。")
except Exception as e:
    print(f" 發生錯誤: {e}")

以下為測試畫面

Be the First to comment.

Leave a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

(若看不到驗證碼,請重新整理網頁。)