1.1 IP地址僞造漏洞
1.1.1 漏洞挖掘
1.1.1.1 漏洞描述
漏洞描述:
IP地址僞造通常是僞造來源IP,爲了繞過服務器端IP地址過濾,使非授權IP地址可以獲取更多的防問權限。但是在正常的TCP/IP通信中,僞造數據包來源 IP會讓發送出去的數據包返回到僞造的IP上,無法實現正常的通信。既然無法實現TCP/IP層級別的IP僞造,那麼可以在應用層HTTP協議通過X-Forwarded-For這個擴展頭部來實現。X-Forwarded-For位於HTTP協議的請求頭,是一個擴展頭部簡稱 XFF 頭,只有在傳輸過程中經過 HTTP 代理或者負載均衡服務器時纔會添加該項。
XFF 格式如下:
X-Forwarded-For: client1, proxy1, proxy2
可以看出,XFF 頭信息可以有多個,中間用逗號分隔,第一項爲真實的客戶端ip,剩下的就是經過的代理或負載均衡服務器的ip地址。
那麼在代碼中獲取XFF 頭信息中的第一個元素即爲真實客戶端IP,但是X-Forwarded-For作爲HTTP協議頭中的一部分通常是可以僞造的,很多系統簡單的獲取XFF頭中定義的IP地址設置爲來源地址,進行訪問控制,攻擊者可以篡改HTTP請求包中XFF項進行IP地址僞造。
注意:此類問題漏洞不能在代碼中見到X-Forwarded-For就判斷爲IP地址僞造,應該結合上下文是否存在IP過濾或者IP訪問控制的業務邏輯,否則儘量不要提此類漏洞。本身利用X-Forwarded-For是業務需要,爲了應對傳輸過程中經過多層代理或者負載均衡服務器也能獲取到真實IP的情況。
1.1.1.2 漏洞場景復現
獲取IP地址的代碼如下:
public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Forwarded-For"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } // Proxy-Client-IP 字段和 WL-Proxy-Client-IP 字段只在 Apache(Weblogic Plug-In Enable)+WebLogic 搭配下出現,其中“WL”就是 WebLogic 的縮寫。 即訪問路徑是:Client -> Apache WebServer + Weblogic http plugin -> Weblogic Instances,只爲兼容,可以直接忽略 if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } //EMOTE_ADDR 是客戶端跟服務器“握手”時的IP,但如果使用了“匿名代理”,REMOTE_ADDR將顯示代理服務器的ip,或者最後一個代理服務器的ip if (ip != null) { if (ip.indexOf(',') > 0) { // 獲取split中的第一個元素,如果使用代理(或者是多級)從代理中取出真實IP地址 ip = ip.split(",")[0]; } } else { ip = "0.0.0.0"; } return ip; } |
綜上,在代碼中獲取到的IP,很有可能是僞造的IP地址。服務端系統會根據獲取到的IP地址,進行IP過濾或者權限控制,代碼如下:
/** * 校驗指定IP是否被禁用 * * @param ip * @return */ public boolean verifyIP(String ip) { if (StringUtils.isBlank(ip)) { return true; } return securityDAO.countIPBan(ip) > 0 ? false : true; } |
如果攻擊者通過篡改X-Forwarded-For的IP地址,即可繞過IP禁用的限制。
1.1.1.3 漏洞修復建議
針對IP僞造漏洞的修復方案需要考慮如下兩種情況:
1. 客戶端直接訪問的web應用情況,中間不存在代理或者負載均衡,這種情況可以直接在代碼層面利用ip = request.getRemoteAddr();獲取客戶端真實IP,不需要通過其它項進行獲取。
2. 客戶端需要經過多層代理或者負載均衡才能訪問到web應用,在這種情況下如果還是在代碼層面利用ip =request.getRemoteAddr();進行獲取,獲取到的是代理的IP。那麼就需要獲取XFF的split第一個元素IP作爲真實IP,但是這裏存在被僞造的風險。下面針對Nginx 進行安全配置:
在默認情況下,Nginx並不會對X-Forwarded-For頭做任何的處理,除非用戶使用proxy_set_header參數設置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
$proxy_add_x_forwarded_for變量包含客戶端請求頭中的"X-Forwarded-For",與$remote_addr用逗號分開,如果沒有"X-Forwarded-For"請求頭,則$proxy_add_x_forwarded_for等於$remote_addr。
nginx 配置 |
proxy_set_header X-Forwarded-For $remote_addr;
$remote_addr變量的值是客戶端的IP
清空了客戶端僞造傳入的X-Forwarded-For,保證了使用request.getHeader("x-forwarded-for") 獲取的ip爲真實ip。