web漏洞之SSRF
一、基础概念
服务端请求伪造(SSRF) 是一种攻击者通过构造恶意请求,诱导服务器向非预期目标发起网络请求的安全漏洞。攻击者利用存在漏洞的服务器作为代理,绕过访问限制,访问内部资源或第三方系统,可能导致敏感信息泄露、内网探测、甚至远程代码执行。
攻击目标:一般是外网无法直接访问的内部系统。
二、漏洞原理
2.1 漏洞原因
- 用户可控数据:应用程序接收用户提供的 url 或域名(例如图片下载、API 调用、网页预览功能)。
- 服务器发起请求:服务器未严格校验用户数据,直接向该 url 发起 HTTP、FTP、Gopher 等协议请求。
- 访问非授权资源:攻击者通过篡改数据,使服务器访问内网服务(如数据库、管理后台)、本地文件(
file://协议)或云元数据接口(如 AWS/Aliyun 的元数据服务)。
2.2 漏洞攻击场景
- 内网探测:扫描内网IP和端口,识别存活服务(如Redis、MySQL)。
- 敏感信息泄露:访问云服务器元数据(如
http://169.254.169.254/获取临时密钥)。 - 协议滥用:
- 使用
file://协议读取服务器本地文件(如file:///etc/passwd)。 - 利用
gopher://协议构造任意TCP流量攻击内网服务(如Redis未授权访问)。
- 使用
- 绕过防御:通过服务器IP绕过IP白名单限制或身份认证。
三、漏洞产生
漏洞产生的相关函数
- file_get_contents()
- fsockopen()
- curl_exec()
- fopen()
- readfile()
3.1 file_get_contents()
1 | |
file_get_content函数从用户指定的url获取内容,然后指定一个文件名进行保存,并展示给用户。file_put_content函数把一个字符串写入文件中。
3.2 fsockopen()
1 | |
fsockopen函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限
3.3 curl_exec()
1 | |
curl_exec函数用于执行指定的cURL会话
四、漏洞利用
4.1 ssrf 端口探测
- http 协议探测
1 | |
- dict 协议探测
1 | |
4.2 ssrf 任意文件读取
- file 协议读取任意文件
1 | |
4.3 dict 协议利用
4.3.1 dict 协议读取 redis 版本信息
1 | |
4.3.2 dict 协议操作redis 键值对
- 获取所有键名
1 | |
- 获取指定键的值
1 | |
- 设置指定键的值
1 | |
- 清空数据库
1 | |
4.3.3 dict 协议利用 redis 写文件
以下方法可以实现修改文件内容、新建文件写入内容;
- 通过
info探测是否设置口令
1 | |
出现类似NOAUTH Authentication required等字样就是设置有口令,可以通过弱口令爆破进行认证
1 | |
- 更改 rdb 文件的目录至需要写文件的目录
1 | |
- 将 rdb 文件名 dbfilename 修改为写入的文件名 webshell.php
1 | |
- 写入内容到
dbfilename中,如果内容存在过滤或者转义,可以利用 16 进制内容写入
1 | |
- 保存写入的内容
1 | |
4.3.4 dict 配置定时任务反弹 shell
要求系统存在cron,且知道其相应的配置文件,假设配置文件为/etc/crontab,与上述写文件相似,将定时任务写到/etc/crontab
1 | |
4.4 gopher 协议利用
相关工具和资料
- https://github.com/firebroo/sec_tools/tree/master
- https://github.com/tarunkant/Gopherus
- https://zhuanlan.zhihu.com/p/112055947
4.4.1 gopher 发送 GET 数据包
通过gopher发送get请求,请求数据包内容如下:
1 | |
gopher发送get请求方法
1 | |
4.4.2 gopher 发送 POST 数据包
通过gopher发送post请求,请求数据包内容如下:
1 | |
gopher发送post请求
1 | |
4.4.3 Gopher Payload 生成脚本
以下 Python 脚本可以自动将原始 HTTP 请求(GET/POST)转换为 Gopher 协议 Payload。使用时,只需将 BurpSuite 等工具抓取的原始数据包粘贴到脚本中运行即可。
1 | |
4.4.4 gopher 利用 redis 写文件
这里我们主要运用一下项目https://github.com/firebroo/sec_tools/tree/master
redis TCP Stream 数据读取内容编排方式
1 | |
与 dict 协议利用 redis 类似,也是通过 redis 实现写文件的,但是需要将 redis 的命令编写为 TCP Stream 的形式,这部分借助写好的工具脚本即可
redis.cmd文件为需要执行的redis命令,一行一条命令, e.g.
1 | |
得到的payload
1 | |
只需要在payload前面加上需要攻击机器的gopher://ip:port/_使用curl就行,最终payload
1 | |
4.4.5 gopher 配置定时任务反弹 shell
运用一下项目https://github.com/firebroo/sec_tools/tree/master
在 redis.cmd 文件输入自己需要执行的命令
1 | |
得到 tcp 数据流后利用 gopher 协议发送即可。
4.5 ftp 协议利用
ftp:// 协议主要用于探测内网端口或进行文件操作。在 SSRF 中,如果后端支持 FTP 协议(如 PHP 的 curl 扩展),可以利用它来探测内网存活服务或读取 FTP 服务器上的文件。
4.5.1 内网端口探测
通过计算响应时间或查看错误信息,可以探测内网主机开放的端口。
1 | |
4.5.2 读取文件与目录列表
如果内网存在 FTP 服务且允许匿名访问(或已知账号密码),可以读取文件或列出目录。
1. 匿名访问列出目录
1 | |
2. 读取指定文件
1 | |
3. 带凭证访问
格式:ftp://user:password@ip:port/path
1 | |
4.6 云主机元数据读取
云服务商(如阿里云、AWS、GCP等)通常提供元数据服务(Metadata Service),用于查询当前实例的配置信息。如果存在SSRF漏洞,攻击者可以访问这些接口获取敏感信息(如临时凭证、AccessKey、Token等),进而控制云资源。
4.6.1 常用元数据地址
1. AWS (Amazon Web Services) / 阿里云 (Alibaba Cloud) / 腾讯云
- 地址:
http://169.254.169.254/latest/meta-data/ - 利用:
- 获取实例ID:
http://169.254.169.254/latest/meta-data/instance-id - 获取IAM角色名称:
http://169.254.169.254/latest/meta-data/iam/security-credentials/ - 获取临时凭证 (AccessKey/SecretKey/Token):
http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE_NAME> - 获取用户数据 (User Data):
http://169.254.169.254/latest/user-data/(可能包含脚本或密码)
- 获取实例ID:
2. 阿里云 (Alibaba Cloud) 额外地址
- 地址:
http://100.100.100.200/latest/meta-data/(阿里云经典网络或VPC均可尝试) - 利用:
- 获取敏感信息:
http://100.100.100.200/latest/meta-data/ram/security-credentials/<ROLE_NAME>
- 获取敏感信息:
3. Google Cloud Platform (GCP)
- 地址:
http://metadata.google.internal/computeMetadata/v1/ - 注意: GCP通常需要添加请求头
Metadata-Flavor: Google,普通的SSRF(无头控制)较难利用,但如果支持CRLF注入头部或未校验头部则可利用。 - 利用:
- 获取SSH公钥:
http://metadata.google.internal/computeMetadata/v1/project/attributes/ssh-keys?alt=json - 获取Access Token:
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
- 获取SSH公钥:
4. Microsoft Azure
- 地址:
http://169.254.169.254/metadata/instance?api-version=2021-02-01 - 注意: Azure通常需要添加请求头
Metadata: true。
5. DigitalOcean
- 地址:
http://169.254.169.254/metadata/v1/
4.6.2 利用示例
假设目标存在SSRF漏洞 http://target.com/ssrf.php?url=...
探测是否存在元数据服务
访问http://target.com/ssrf.php?url=http://169.254.169.254/latest/meta-data/
如果返回目录列表(如ami-id,iam/,instance-id等),则说明存在。获取凭证
访问http://target.com/ssrf.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/获取角色名称(例如admin-role)。
访问http://target.com/ssrf.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/admin-role获取JSON格式的AccessKeyId,SecretAccessKey,Token。本地利用凭证接管
使用获取的凭证配置本地 CLI 工具(如aws-cli或aliyun-cli),即可操作该云账号下的资源。
五、ssrf 常见绕过方式
5.1 常用绕过方法
@绕过;例如:http://abc.com@127.0.0.1
添加端口号;例如:http:127.0.0.1:8080
短地址;例如:https://0x9.me/cuGfD
可以指向任意ip的域名;例如:xip.io
ip地址转换成进制来访问;例如:192.168.0.1=3232235521(十进制)
非HTTP协议;
DNS重绑定;
利用[::]绕过;例如:http://[::]:80 == http://127.0.0.1:80
句号绕过;例如:127。0。0。1 == 127.0.0.1
利用302跳转绕过;
5.2 常见限制
- 限制为http://www.xxx.com 域名
采用http基本身份认证的方式绕过,即@;
利用http://www.xxx.com@www.xxc.com == http://www.xxc.com进行绕过
- 限制请求IP不为内网地址
当不允许ip为内网地址时:
(1)采取短网址绕过
(2)采取特殊域名
(3)采取进制转换
- 限制请求只为http协议
(1)采取302跳转
(2)采取短地址
六、SSRF 盲打
当 SSRF 完全没有回显来获取 SSRF 的利用结果的时候,可以通过外带去判断是否存在 SSRF,可以通过利用 dnslog 或者 vps 主机监听来判断是否存在漏洞;
如在 vps 主机上进行监听
1 | |
然后可能存在漏洞的位置写如 vps 监听的 ip 和端口
1 | |
也可以通过 dnslog 平台实现https://dig.pm/、http://dnslog.cn/
七、SSRF 防御方案
ssrf 的防御思路通常有以下几种:
- 限制危险协议,仅允许必要的协议,如 http、https;禁止危险协议,如 file://、gopher://、dict://;
- 设置 ip 黑名单,禁止访问私钥 ip 端和 保留地址
1 | |
- 限制请求的端口为 http 常用的端口,如:80、443、8080、8090;
八、针对绕过方式的防御方案
DNS重绑定绕过
防御方案的设计核心在于 固定解析结果,不给攻击者二次解析的机会。
对于可能存在ssrf漏洞的点对输入的 url 的 hostname 进行 DNS 解析,校验ip地址是否属于合法区域地址,禁止访问私有地址段、环回地址和特殊用途的地址(如云平台元数据地址)。
302 跳转
防御方案的设计核心在于将重定向过程受控化,确保每一次跳转都经过与初始请求同等强度的校验。
- 禁用自动重定向
- 自定义重定向策略(Hook/Interceptor):对Location字段重复进行URL解析和IP地址段校验,保证最后跳转地址的合法有效。
- 网络层出口限制(深度防御)