



服务端请求伪造(Server-Side Request Forgery, SSRF)指的是在未取得服务器所有权限时,利用服务器上的应用程序从其他服务器上获取数据,通过构造数据利用服务器发送伪造的请求到目标内网,以此达到访问目标内网的数据,进行内网信息探测或者内网漏洞利用的目的。
SSRF漏洞攻击的主要目标是从外网无法访问的内网系统,由于服务器并未对目标地址、协议等重要参数进行过滤和限制,导致攻击者可以伪造请求。因为是由目标服务器发起,所以内部服务器并不会判断这个请求是否合法,而是以其身份访问其他内部资源。SSRF入口一般出现在调用外部资源的地方。
SSRF漏洞的出现场景如下。
● 需要本地访问,请求头无法绕过。
● 在URL中提交参数获取文件。
● 对外发起网络请求。
● 从远程服务器请求资源。
● 数据库内置功能。
● WebMail收取其他邮件。
● 文件处理、编码处理、属性信息处理。
1.内网访问
在CTF中,SSRF漏洞最常见的利用方式就是探测内网,根据127.0.0.1或找到的内网IP,对内网进行访问,结合BurpSuite可快速对目标端口进行检测。
靶场环境为CTFHub技能树-Web-SSRF-内网访问。
靶场中的URL通过GET方式传递参数变量url的值,通过该参数调用外部资源,所以成了SSRF漏洞的入口。构造Payload访问服务器本地资源:?url=127.0.0.1/flag.php。发送伪造后的请求,即可获取flag。
2.伪协议
伪协议就是利用不同URL协议类型配合SSRF,也就是URL scheme机制。URL scheme是系统提供的一种机制,由应用程序注册,其他程序通过URL scheme调用该应用程序,包括系统默认的URL scheme与应用程序自定义的URL scheme。https://www.ctfhub.com中https://就属于系统默认的机制。
以CURL工具为例,其支持的协议如下。
● file://:访问本地文件系统(不受allow_url_fopen与allow_url_include的影响)。
● dict://:约定服务器端侦听的端口号。
● sftp://:基于SSH的文件传输协议。
● tftp://:基于lockstep机制的文件传输协议。
● ldap://:轻量化目录访问协议。
● gopher://:分布式文档传递服务。
举个例子,CTFHub技能树-Web-SSRF-伪协议读取文件,使用file://协议读取flag.php的源码,构造Payload,?url=file:///var/www/html/flag.php,发送请求即可得到flag。
3.端口扫描
内网的防护相较于外网来说较为薄弱,通过扫描服务器与内网主机的端口,可发现外网无法访问的服务,扩大可攻击范围,增加攻破系统的可能性。
靶场环境为CTFHub技能树-Web-SSRF端口扫描。与上一题环境类似,构造Payload ?url=127.0.0.1:8000可直接使用BurpSuite对端口进行爆破,使用Intruder模块中的Sniper类型,选中8000端口号为变量,如图1-84所示。
图1-84 爆破端口
设置Payload类型为Numbers,这是因为题目提示端口号范围在8000~9000之间,设置如图1-85所示。
图1-85 设置端口范围
通过爆破结果的长度,获取flag,如图1-86所示。
图1-86 8910端口获得flag
也可以结合dict://协议对端口进行爆破,构造Payload ?url=dict://127.0.0.1:8000,同样设置8000为变量,如图1-87所示。
图1-87 设置爆破端口
与之前的Payload设置相同,获得爆破结果,如图1-88所示。
图1-88 爆破结果
根据页面响应状态(即返回长度)判断结果。
4.Gopher协议
Gopher协议是HTTP出现之前在互联网上最常见,也是最常用的协议。Gopher协议能够传递底层的TCP数据流,攻击内网的FTP、Telnet、Redis、Memcache,也可以进行GET、POST请求,所以在SSRF中Gopher协议的攻击面最广。
Gopher协议的格式为gopher://127.0.0.1:70/_+ TCP/IP数据,这里的_是一种数据连接格式,也可以是任意字符。Gopher协议在各种编程语言中的使用限制如表1-5所示。
表1-5 Gopher协议的使用限制
下面通过CTFHub的靶场举几个Gopher协议在SSRF中被利用的例子。
以CTFHub技能树-Web-SSRF-POST请求题目为例,通过GET方式传参访问:?url=127.0.0.1/flag.php。查看网页源码,代码如下。
其中含有key=51457bb0a50c1eb2c92dcc3ec3c2cc13,将key值添加到输入框中并提交,得到回显,如图1-89所示。
使用file://协议读取index.php以及flag.php页面源码:?url=file:///var/www/html/flag.php。得到index.php页面的源码,如图1-90所示。
图1-89 页面回显
图1-90 index.php页面源码
得到flag.php页面的源码,如图1-91所示。
图1-91 flag.php页面源码
尝试使用Gopher协议向服务器发送POST包。首先构造Gopher协议所需的POST请求,请求包如图1-92所示。
图1-92 构造请求包
在使用Gopher协议发送POST请求包时,Host、Content-Type和Content-Length请求头是必不可少的,但在GET请求中可以没有。
在向服务器发送请求时,浏览器会进行一次URL解码,服务器收到请求后,在执行CURL功能时,进行第二次URL解码,所以我们需要对构造的请求包进行两次URL编码。
首先将构造好的请求包进行第一次URL编码,如图1-93所示。
图1-93 第一次URL编码
将第一次编码后的数据中的%0A全部替换为%0D%0A。因为Gopher协议包含的请求数据包中,可能包含=、&等特殊字符,为避免与服务器解析传入的参数键值对混淆,所以对数据包进行第二次URL编码,这样服务端会把%后的字节当作普通字节。进行第二次URL编码,得到如下Gopher请求内容。
因为flag.php中的$_SERVER["REMOTE_ADDR"]无法绕过,所以只能通过index.php页面中的CURL功能向目标发送POST请求,构造如下Payload。
向目标发送数据包,得到flag,如图1-94所示。
图1-94 获取flag
以CTFHub技能树-Web-SSRF-POST上传文件题目为例,通过GET传参访问?url=127.0.0.1/flag.php,得到一个空上传功能点,如图1-95所示。
提示需要上传WebShell,只能选择文件,没有提交按钮。使用file://协议读取flag.php的源码:?url=file:///var/www/html/flag.php。得到目标源码,如图1-96所示。
图1-95 空上传功能点
图1-96 flag.php源码
后端无任何过滤,也无文件类型限制,上传文件大小大于0即可,如图1-97所示。
在flag.php页面中,还须满足请求只允许从本地访问,使用BurpSuite抓取数据包,如图1-98所示。
图1-97 上传页面源码
图1-98 BurpSuite抓包
构造Gopher协议所需的POST请求,如图1-99所示。
图1-99 POST请求
与之前相同,将第一次URL编码后的数据中的%0A替换为%0D%0A,并进行二次URL编码,如图1-100所示。
图1-100 URL编码
构造Payload,发送数据包,得到flag,代码如下。
5.攻击Redis
Redis是一个key-value存储系统,根据题目的提示,需要使用SSRF攻击内网的Redis服务,使用Gopherus工具生成攻击Redis的Payload,如图1-101所示。
选择PHPShell,根目录路径为默认值,使用默认的PHPShell,得到构造好的Gopher协议Payload,其默认经过了一次URL编码,将%0A替换为%0D%0A,对其进行二次URL编码,如图1-102所示。
图1-101 使用工具生成攻击Redis的Payload
图1-102 进行二次URL编码
构造最终的Payload,代码如下。
发送数据包,题目环境显示504,但Shell已经写入,访问shell.php,结果如图1-103所示。
图1-103 访问结果
虽然有脏数据,但是页面已经存在,WebShell参数为cmd,尝试寻找flag:shell.php?cmd=ls/。得到flag文件名,如图1-104所示。
图1-104 得到flag文件名
使用cat命令查看flag:shell.php?cmd=cat/flag_2596562d0e4a36c94823864f1d7a505b得到flag。其中Redis写WebShell用到的命令如下。
需要先将其转化为Redis RESP协议的格式,再进行URL编码。
1.URL Bypass
靶场环境为CTFHub技能树-Web-SSRF-URL Bypass。
题目要求请求的URL中必须包含http://notfound.ctfhub.com,我们需要利用合适的方法绕过该限制,可以利用HTTP基本身份认证绕过。
HTTP的基本身份认证允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证,格式为http://user@domain。以@分割URL,前面为用户信息,后面才是真正的请求地址,我们可以利用这个特性去绕过一些URL过滤,直接请求http://notfound.ctfhub.com@127.0.0.1获得flag。
2.数字IP Bypass
指向127.0.0.1的地址如下。
● http://localhost/:localhost代表127.0.0.1。
● http://0/:0在Windows中代表0.0.0.0,在Linux下代表127.0.0.1。
● http://0.0.0.0/:这个IP表示本机IPv4的所有地址。
● http://[0:0:0:0:0:ffff:127.0.0.1]/:Linux系统下可用,Windows系统下不可用。
● http://[::]:80/:Linux系统下可用,Windows系统下不可用。
● http://127。0。0。1/:用中文句号绕过关键字检测。
● http://①②⑦.
.
.①:封闭式字母数字。
● http://127.1/:省略0。
● http://127.000.000.001:1和0的数量没影响,最终依然指向127.0.0.1。
3.使用不同进制代理IP地址Bypass
使用Python写一个IP地址进制转换脚本,代码如下。
4.302跳转Bypass
靶场环境为CTFHub技能树-Web-SSRF-302跳转Bypass。通过GET传参的URL,尝试访问127.0.0.1/flag.php页面,如图1-105所示。
不允许企业内部IP访问,使用file协议获取源码:?url=file:///var/www/html/flag.php。得到flag.php页面源码,如下所示。
图1-105 无法访问
与之前一样,通过REMOTE_ADDR请求头限制本地IP请求,源码中并没有之前的hacker! Ban Intranet IP,查看index.php页面的源码:?url=file:///var/www/html/index.php。得到的index.php页面源码如下。
发现其中存在黑名单,限制了127、172、10、192网段,题目提示使用302跳转方式。尝试使用短网址绕过,使用在线平台https://4m.cn/,将http://127.0.0.1/flag.php转换为短地址,如图1-106所示。
图1-106 转换短地址
利用生成的短地址构造Payload:?url=surl-2.cn/0nPI。通过浏览器发送请求,利用302跳转绕过IP限制,即可得到flag。
5.DNS重绑定Bypass
DNS重绑定(DNS Rebinding)指的是在网页访问过程中,用户在地址栏输入域名,浏览器通过DNS服务器将域名解析为IP,然后向对应的IP请求资源。域名所有者可以设置域名所对应的IP,用户第一次访问时,域名会解析一个IP。域名持有者修改绑定的IP,当用户再次访问时,会重绑定到一个新的IP上,但对于浏览器来说,整个过程都是访问同一个域名,所以浏览器认为是安全的,于是造成DNS重绑定漏洞。
攻击过程大致如下。
1)控制恶意的DNS服务器回复用户对域的查询。
2)诱导受害者加载域名。
3)受害者打开链接,浏览器发送DNS请求,获取域名的IP地址。
4)恶意DNS服务器收到受害者请求,并使用真实的IP响应,设置较低的TTL值,减少DNS记录在DNS服务器上缓存的时间。
5)从域名加载的网页中若包含恶意的JavaScript代码,构造恶意的请求将再次访问域名,导致受害者的浏览器执行恶意请求。
DNS重绑定攻击可使同源策略失效,由于同源策略是指同域名、同协议、同端口,检测的是域名而不是IP,而DNS重绑定的域名是一样的,因此同源策略就失效了。
靶场环境为CTFHub技能树-Web-SSRF-DNS重绑定Bypass。
首先使用file://协议读取index.php的源码,发现存在黑名单,限制了127、172、10、192网段,题目提示使用DNS重绑定方式,通过https://lock.cmpxchg8b.com/rebinder.html网站设置DNS,如图1-107所示。
图1-107 设置DNS
使用生成的域名构造Payload:?url=7f000001.7f000002.rbndr.us/flag.php。通过浏览器发送请求,得到flag。
1.无回显SSRF
无回显SSRF即我们无法看到通过SSRF请求的结果,这样就极大减少了SSRF的攻击面。下面介绍当碰到无回显SSRF时,我们如何利用。
先看看如何判断SSRF漏洞是否存在。我们可以先在自己的服务器上用Netcat工具监听某个端口,然后通过SSRF去请求。如果我们的服务器收到请求了,说明存在SSRF。如果未收到,也不能判断其不存在,还需要考虑目标机器不出网的情况,如图1-108所示。
也可以通过DNSLOG去探测SSRF。
虽然没有回显,但是我们还是能够通过一些别的信息去判断探测的结果,比如状态码、响应时间或者页面上的某一个特征。
图1-108 Netcat接收请求
在没有回显的情况下攻击内网的某些服务,如Redis,盲打内网的应用和服务。因为没有回显,所以很难判断我们构造的Payload是否攻击成功了。
2.攻击有认证的Redis服务
前面说到SSRF可以攻击内网无认证的Redis服务。如果碰到有认证的Redis服务,还能通过SSRF去利用吗?答案是可以。虽然SSRF每次只能发送一个数据包,无法保持登录状态,但是Redis使用的是RESP(Redis序列化协议),Redis客户端支持管道操作,可以通过单个写入操作发送多个命令,而无须在发出下一条命令之前读取上一条命令的服务器回复,所有的回复都可以在最后阅读。这样我们就可以通过SSRF发送一个数据包,完成认证和写入文件的操作。
我们来看Redis是如何进行认证的。先设置一个密码root@123,如图1-109所示。
图1-109 Redis认证
使用Wireshark抓包看一下认证过程,如图1-110、图1-111所示。
图1-110 抓取Redis认证包
图1-111 Redis认证数据包详情
解释一下这些指令的含义。
● *number:代表每一行命令,number代表每行命令中数组中的元素个数。
● $number:代表每个元素的长度。
● *2:*代表数组,这里*2代表本次指令的数组大小为2。
● $4:代表指令的长度,这里是4。
● auth:指令为auth。
再来看一下如何通过SSRF去利用Redis的流量,代码如下。
● dir:指定Redis的工作路径,之后生成的RDB和AOF文件都会存储在这里。
● dbfilename:RDB文件名,默认为dump.rdb。
上述代码先设置工作路径为我们要写入文件的路径,然后设置dbfilename为我们要写入的文件名,就实现了任意文件写入。
我们抓一下流量看看,如图1-112、图1-113所示。
图1-112 Redis写文件数据包
图1-113 Redis写文件数据包详情
通过SSRF方式发送Redis写文件的数据包,就是利用Gopher协议把图1-113中的指令发送给Redis服务。把Payload解码后可以看到,其实就是写文件数据包中的指令,如图1-114所示。
图1-114 解码Payload
我们只需要在这些指令前面加上认证的指令,即可利用有认证的Redis服务写入任意文件。我们来试着构造一个包含认证部分的Payload,通过SSRF写一个ssrf_success.txt到tmp目录下,需要构造的指令如下。
将这些指令构造为Gopher协议格式。
首先进行URL编码,代码如下。
将%0A替换为%0D%0A,代码如下。
可以看到成功写入了文件,如图1-115所示。
图1-115 成功用Gopher协议写入文件
在实际应用的时候我们可能并不知道Redis的密码,既然能够进行认证,当然也可以去爆破Redis的密码。
3.网鼎杯-2020-玄武组-SSRFMe
首先访问题目,给出源码,代码如下。
简单审计发现,对输入的地址做了限制,限制协议为http、https、gopher、dict,限制了127、172、192.168、10这些内网网段,限制了跳转。
直接通过http://0.0.0.0即可绕过,这个IP表示本机IPv4的所有地址。根据提示,访问url=http://0.0.0.0/hint.php。得到hint.php源码,代码如下。
代码中有一个file_put_contents文件写入,但是会在文件的开头添加<?php echo 'redispass is root';exit();来强制退出代码执行。我们可以通过php伪协议绕过,但是这里限制了协议,所以此处绕不过去。请读者留意redispass is root这段代码,它表示存在Redis而且密码为root。
我们可以利用dict协议探测一下端口,访问url=dict://0.0.0.0:6379,发现Redis服务就在默认端口6379上。通过Redis写文件,发现写入不成功,题目的考点其实是Redis基于主从复制的RCE。当Redis读写量很大时,Redis会提供一种模式,即主从模式。主从模式指的是使用一个Redis实例作为主机,其余的作为备份机。主机和备份机的数据完全一致,主机支持数据写入和读取等操作,而备份机只支持与主机数据的同步和读取。也就是说,客户端可以将数据写入主机,由主机自动将写入操作同步到备份机。
主从模式很好地解决了数据备份问题,并且由于主从服务数据几乎是一致的,因此可以将写入数据的命令发送给主机执行,而将读取数据的命令发送给不同的备份机执行,从而达到读写分离的目的。在Redis 4. x 之后,我们可以通过外部拓展的方式,自己编译一个.so文件来构造Redis命令。
在2018年的ZeroNights会议上,Pavel Toporkov提出了一种利用主从模式RCE的思路
:首先通过Redis的主机实例同步文件到备份机上,然后在备份机上加载构造好的恶意.so文件,从而执行任意命令。
首先构造一个master Server
,代码如下。
exp.so文件代码地址为https://github.com/vulhub/redis-rogue-getshell。
通过Gopher协议发送Redis指令和目标建立主从关系,这样我们构造的exp.so文件也会被复制过去,接着通过exp.so文件构造一个Redis命令system.exec,调用我们构造的system.exec指令就可以执行任意命令了,代码如下。
将上述代码转换成Gopher协议格式,代码如下。
通过SSRF发送Payload,即可执行任意命令。