【SSRF漏洞】Server-Side Request Forgery 服务器端请求伪造
一、定义
攻击者诱导服务器代替自己发起本不该发起的网络请求,从而访问或操作服务器内部网络/本地资源的一种漏洞。
二、常见危害
-
扫描/暴打内网端口、服务指纹
-
读取内部文件(
file://
) -
调用内部接口、管理 API(
http://192.168.x.x/admin/reset
) -
下载恶意文件到服务器缓存
-
造成 DoS(大文件、慢连接)
-
结合 CRLF 打 Redis、Gopher 等 TCP 协议
-
云环境可窃取元数据(
http://169.254.169.254/
)
三、触发点
业务代码需要代表用户去访问外部资源,且访问地址可被用户控制。只要满足这一点,任何语言、任何接口都可能成为入口。
-
图片/文件抓取
头像裁剪、水印生成、PDF 渲染、在线缩略图服务:
?image_url=、?file=、?thumbnail=、?watermark=
-
WebHook 与代理转发
接收用户配置的回调地址并主动 POST:
支付回调、Git webhook、短链跳转、代理浏览、RSS 订阅 -
在线翻译/爬虫/预览
输入任意网址即可“一键翻译”“网页预览”“文章采集”:
?url=、?link=、?target=、?site=
-
云存储/远程导入
从远端 URL 拉取包、镜像、脚本、插件:
WordPress 插件安装、Docker 镜像仓库、CI 远程依赖 -
数据库/大数据组件
加载外部数据源或导出结果到外部:
Hive/Spark “LOAD DATA”、Kettle ETL、Logstash 输入 -
邮件/短信/即时推送
模板里可插入外部图片、样式、附件链接:
邮件追踪像素、短信短链、Teams/Slack 卡片缩略图 -
报表/BI 可视化
用户填写“外部 JSON/CSV 地址”生成图表:
?data_source=、?api_url=、?json_path=
-
富文本&Markdown 解析器
自动把用户贴入的远程图片拉到本地缓存:
论坛发帖、文档系统、工单系统 -
物联网/设备管理
下发固件、远程配置、日志上报:
摄像头、路由器、工控网关的“升级包地址”字段 -
内网探测/运维平台
网络质量监控、漏洞扫描、资产测绘:
输入 IP 或域名后平台主动去 ping/curl/telnet -
云原生附加场景
-
K8s admission webhook 地址
-
Istio/Envoy 的 ext_authz 服务地址
-
Terraform 拉取外部 module 源
-
Serverless 函数运行时下载用户代码 zip
-
-
重定向/短链/跳转网关
先记录访问日志再 302 到用户指定地址:
短网址、广告监测、退出提醒页 -
文件协议被意外开启
允许file://、ftp://、dict://、gopher://
时,可直接读本地文件或打内网协议;即便只允许 HTTP,也可通过
http://127.0.0.1:22
扫描、通过 30x 跳转到 gopher 实现协议升级。
四、示例 payload
http://vuln.site/fetch.php?url=http://127.0.0.1:22/
http://vuln.site/fetch.php?url=file:///etc/passwd
http://vuln.site/fetch.php?url=http://169.254.169.254/latest/meta-data/
五、防御口诀
-
白名单域名/IP(禁止私有段)
-
禁止 30x 跟随或只允许跟随一次
-
协议限制:只允许 HTTPS,禁掉
file://
、gopher://
、dict://
等 -
返回内容类型校验(非图片即拒绝)
-
内网隔离+零信任,即使打到内网也拿不到敏感数据
-
统一走代理网关,便于审计与拦截
SSRF(Server-Side Request Forgery,服务器端请求伪造)的本质是利用服务器自身网络身份,去访问或操作攻击者无法直接触及的资源;PHP 中任何能发起网络 IO 的函数(curl_exec、file_get_contents、fsockopen、fopen、get_headers、SimpleXMLElement 等)只要接收用户可控的 URL,就可能成为漏洞触发点。这些函数默认支持多协议封装器(http://、https://、ftp://、file://、dict://、gopher://、ldap://),且对内网 IP 段、私有端口、重定向跳转毫无戒心,使攻击面迅速放大:
-
信息收集与资产扫描
通过循环提交内网地址(192.168.0.0/16、10.0.0.0/8、172.16.0.0/12)和常见端口(22、3306、6379、8080、9200),结合响应时间、错误差异或 banner 回显,可快速绘制出服务器背后拓扑与开放服务清单,绕过边界防火墙与 IPS 策略。 -
本地与云端敏感数据窃取
-
file:///etc/passwd、file:///proc/self/environ 可直接读取系统文件;
-
http://169.254.169.254/latest/meta-data/(AWS)、http://100.100.100.200/latest/meta-data/(阿里云)、http://metadata.google.internal/(GCP)可拿到 AccessKey、SecretKey、内网账号、角色 ARN,实现云下向云上的横向移动。
-
-
内网应用攻击与权限提升
利用 SSRF 打内网未鉴权或弱口令后台,如 Jenkins、Spark、Hadoop YARN、Docker API、Kubernetes API Server,可触发未授权 RCE;配合 dict:// 或 gopher:// 还能直接向 Redis、MySQL、Memcached、SMTP、POP3 发送原始协议命令,完成写 webshell、计划任务、SSH 公钥或反弹 Shell。 -
大文件/慢连接 DoS
让服务器去下载 10 GB 的大文件或建立永不关闭的 TCP 慢连接,可迅速占满 PHP-FPM / Curl 连接池,造成业务拒绝服务。 -
协议升级与多跳利用
-
dict://127.0.0.1:6379/info 可探测 Redis 是否存在;
-
gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a 可清空数据库;
-
通过 302 重定向链 http→gopher→jdbc/ldap,可打到更深层的内网服务。
-
-
防御体系
对所有外部 URL 采用白名单域名+IP 双重校验:先解析拿到真实 IP,再判断是否在私有段;禁用 30x 跟随或限制跳转次数;关闭不必要的封装器(–disable-url-fopen-includes
编译参数或在 php.ini 设allow_url_fopen = Off
、allow_url_include = Off
);统一走正向代理出口,便于审计与拦截;对返回内容做类型与大小检查,禁止返回 text/html 以外类型落入业务缓存;云环境利用元数据保护 Token(IMDSv2)防止一键读取。最终目标是:即使代码里存在“能发请求的函数”,也只能打到允许的目标,无法触达内部敏感资产。
类别 | 项 | 说明/示例 |
---|---|---|
核心 本质 | 借服务器网络身份访问攻击者无法直达的资源 | 把服务端变成“代理跳板” |
敏感函数 | curl_exec() | 最常用,可设多协议、跟随重定向 |
file_get_contents() | 只要给 http://、ftp:// 就发完整 GET;file:// 可读本地文件 | |
fsockopen() / pfsockopen() | 裸 TCP/UDP,可手工构造任意协议报文 | |
其他 | fopen()、get_headers()、SimpleXMLElement、include/require(allow_url_include=On 时) | |
支持协议 | http/https、ftp/file、dict、gopher、ldap、ssh、smtp 等 | 通过 stream wrapper 统一打开 |
典型攻击场景 | 内网端口/服务扫描 | ?url=http://192.168.1.1:22 |
本地文件窃取 | ?url=file:///etc/passwd | |
云元数据读取 | ?url=http://169.254.169.254/latest/meta-data/ | |
内网未授权接口 | ?url=http://10.0.0.3:8080/jenkins/script | |
协议升级打 Redis | gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a | |
DoS 大文件 | 让服务器下载 GB 级文件或慢连接占满 PHP-FPM | |
防御 | 白名单域名+IP+端口 | 先解析→再判私有段→再判端口 |
禁用或限制 30x 重定向 | 防止一次跳入内网 | |
协议/封装器过滤 | php.ini 关闭 allow_url_fopen/include,编译去掉 –with-url-wrappers | |
内网段黑名单 | 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、127.0.0.0/8、169.254.0.0/16 | |
统一正向代理出口 | 所有外联流量走代理,方便审计与拦截 | |
6返回内容校验 | 非图片/非文本即拒绝,大小限制,防止缓存投毒 |
六.实验
用pikachu的ssrf来演示,url后去探测3306端口
把 SSRF 当成“协议升级器”来用 —— 先用平台允许的
dict://
去探本地端口,一旦返回 MySQL 协议乱码(Got packets out of order
),就证明 3306 端口存活且运行 MySQL;整个过程不需要出网、不需要暴力扫描,只靠服务器自己发出的那条“字典协议”请求完成端口嗅探。表明-----dict 协议虽被设计成“在线字典”,但在 SSRF 场景下可瞬间变成内网端口扫描器;服务器对 URL 协议未做白名单过滤,任意协议+任意端口都能直连;回显差异(正常报错 vs 协议乱码)足以判断服务存活,无需回显真实内容即可推断开放端口,为后续转 gopher 打 Redis/MySQL 提供精确目标。
把“文件包含”转成“任意文件读取”——用 file://
协议+绝对路径,把服务器上任一文本拉回来当回显。给出 ssrf_fgc.php?file=
,且用 file_get_contents()
实现,天然支持 file://
封装器。
-
构造 payload
目标文本是 Apache 安装说明D:/Apache24/README.txt
,直接拼路径:
file:///D:/Apache24/README.txt
(三斜杠:file://
+ 盘符根目录/
) -
发送请求
http://localhost/pikachu/vul/ssrf/ssrf_fgc.php?file=file:///D:/Apache24/README.txt
-
验证结果
页面回显出现 “APACHE INSTALLATION OVERVIEW...” 大段文字 → 读取成功,flag 或下一步提示通常就藏在里面;若无回显,换.txt
、.log
、.conf
等可读文本即可。 -
延伸
换掉盘符和文件名就能批量读取系统任意文件;若过滤file://
,可转用http://127.0.0.1/...
走本地 Web 目录,或继续用gopher/dict
打内网服务。
ile_get_contents 支持 PHP 内置封装器 php://filter,把 resource 指向当前目录下的 ssrf.php 并用 base64 编码输出,即可绕过“直接显示 PHP 源码被解析”的问题;浏览器得到的将是整段 base64 字符串,解码后即可还原成明文源码,完成任意文件(含 PHP 自身)读取,无需知道绝对路径也无需 file:// 协议。
http://localhost/pikachu/vul/ssrf/ssrf_fgc.php?file=php://filter/read=convert.base64-encode/resource=ssrf.php
拿到64明文去解码Base64 Decode and Encode - Online