從偶然出發
在做測試的時候發現了這樣一個漏洞,原請求報文如下:
GET / HTTP/1.1 Host: attack_website [... HEADER ...] ...
當時最初目的是想測SSRF的,但是經過測試沒發現存在漏洞后來想起之前看過的一些漏洞案例,將請求報文中的URI部分替換成了網址:
就變成了如下的請求:
GET http://gh0st.cn HTTP/1.1 Host: attack_website [... HEADER ...] ...
在BurpSuite里進行重放測試發現返回的響應正文就是http://gh0st.cn 的,也就是說這里的attack_website可以被作為HTTP代理,於是進入下一步的測試能否使用非http/https協議進行請求?例如file:/// ,測試后發現確實沒辦法這樣玩,看來是這里代理服務器不支持。
在這里替換URI部分為內網的地址,可以直接漫游內網的系統,進行深入的滲透測試了,后續的事情就不在這多說了,那么來研究看看為什么會有這樣的問題呢?
從被動偶然到主動發現
了解原理
查閱了一番資料和詢問了一下朋友,都說具體的不太清楚,后來看見這樣一篇文章:
https://www.secpulse.com/archives/74676.html
其中所說原理大致是因為Nginx反向代理配置不當導致可以被作為正向代理,導致能被外部作為HTTP代理服務器。
正向代理 and 反向代理
正向代理
- 瀏覽器(/全局)設置代理服務器IP和對應端口
- 瀏覽器輸入目標地址->代理服務器->目標服務器
簡而言之,正向代理類似我們經常用到的跳板機,利用代理去訪問外部的資源。
反向代理
跟正代不同的地方在於反向代理相對瀏覽器來說是透明的,不需要在瀏覽器(/全局)做什么配置,而是有反向代理服務器自己做請求轉發到其服務器上所配置的地址。
大致如下的流程:
- 瀏覽器訪問網站(網站所指即反向代理服務器)
- 網站(反向代理服務器)做處理,將請求轉發給所設置的目標服務器
- 由請求最終到達的目標服務器響應給網站(反向代理服務器),然后再通過其返回給瀏覽器
TIPs:
- 一、反向代理服務器也可以變成WAF(例如Nginx支持反代功能,nginx+lua也可以搭建網站waf)
- 二、反向代理服務器也可以起到負載均衡的作用,由反向代理服務器做選擇分配Web服務器
主動發現腳本開發
腳本語言選擇:python2.7
系統環境:all
思考
如何判斷這個網站存在可以作為HTTP代理訪問資源?唯一特征是什么?
腦子中唯一的思路就是IP,如果這目標站點能作為HTTP代理訪問資源,那么我設置的這個資源就是返回真實IP的,這樣就可以判斷了~
這里我在團隊官網上小小的寫了一個,但是在大批量去測試卻無法使用,因為官網的空間沒那么大的吞吐量,承載不住高並發,后期建議大家使用 http://httpbin.org/ip 這個接口~
http://www.hi-ourlife.com/getip.php
PHP代碼:
<?php echo $_SERVER['REMOTE_ADDR']; ?>
代碼構建
Import 庫
import urllib, sys, re, json
全局變量:
poc = "http://www.hi-ourlife.com/getip.php"
獲取使用代理訪問資源后內容(IP)函數:
def useProxy(site): try: res = urllib.urlopen(poc, proxies={'http': site}).read() return res except: return getIP()
正常本機獲取IP函數:
def getIP(): res = urllib.urlopen(poc).read() return res
防止有些會出錯返回的內容不是IP,其實返回不是IP也就間接證明不存在這種漏洞,所以需要寫個正則來匹配,這時候判斷是否是IP的函數就誕生了:
def isIP(ip): compileIP = re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$') if compileIP.match(ip): return True else: return False
對比IP函數:
def isVul(site): resA = getIP() #print resA resB = useProxy(site) #print resB if resA == resB or not isIP(resB): print "\033[1;33m[INFO]\033[0m No Vulnerability!" else: print "\033[1;31m[INFO]\033[0m Existing Vulnerability!" print "\033[1;36m[INFO]\033[0m Site:[ {0} ] -> RealIP:[ {1} ]".format(site, resB)
單線程批量
從掃描器里把代碼模板剝離了出來如下:
#-*- coding:utf-8 -*- #Author: Vulkey_Chen import urllib, sys, re, json poc = "http://www.hi-ourlife.com/getip.php" def useProxy(site): try: res = urllib.urlopen(poc, proxies={'http': site}).read() return res except: return getIP() def getIP(): res = urllib.urlopen(poc).read() return res def getSite(filename): f = open(filename) res = [] for line in f: res.append(line) return res def isIP(ip): compileIP = re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$') if compileIP.match(ip): return True else: return False def isVul(site): resA = getIP() #print resA resB = useProxy(site) #print resB if resA == resB or not isIP(resB): print "\033[1;33m[INFO]\033[0m No Vulnerability!" else: print "\033[1;31m[INFO]\033[0m Existing Vulnerability!" print "\033[1;36m[INFO]\033[0m Site:[ {0} ] -> RealIP:[ {1} ]".format(site, resB) def main(filename): for i in getSite(filename): isVul(i.replace("\n","")) if __name__ == '__main__': main(sys.argv[1])
END
使用方法:python proxy_vul.py urls.txt
urls.txt 格式:
http://www.hi-ourlife.com/ https://gh0st.cn/ http://mst.hi-ourlife.com:8080/
建議批量方法:
掃描所有想檢測站點的web服務端口(Nginx容器存在此類問題居多),然后使用腳本檢測。