SSRF由浅入深
引言
SSRF(Server-Side Request Forgery,服务器端请求伪造)是一类由攻击者构造请求,并由目标服务器执行的安全漏洞。攻击者通过精心构造的 URL,使服务器以自身身份向指定的地址发起请求,从而实现对目标系统的探测、访问或攻击。
在许多 Web 应用中,服务器通常会提供从指定地址获取资源的功能,例如读取远程图片、抓取网页内容或访问第三方接口等。如果这些功能未对用户输入的 URL 做严格校验,就可能被攻击者利用,构造特殊的请求指向服务器所在的内网地址。由于这些内网资源通常无法从外网直接访问,因此 SSRF 能够成为突破边界防护的一个重要手段。
简单来说,SSRF 的本质是攻击者借助存在漏洞的服务器,伪装成服务器自身,去访问攻击者原本无法直接接触的内部资源或服务。这类攻击在现代架构中尤为危险,尤其是在云环境、微服务、Kubernetes 集群和各种开放 API 场景中,可能带来敏感数据泄露、内网横向移动甚至远程代码执行等严重后果。
SSRF 漏洞原理
SSRF 漏洞的本质,是攻击者利用存在漏洞的服务器作为跳板,向原本无法直接访问的目标地址发起请求。在实际场景中,攻击者可能希望访问服务器 B 上的某个服务,但由于服务器 B 位于内网或受防火墙保护,外部网络是无法直接访问的。
如果此时服务器 A 存在 SSRF 漏洞,例如其提供了从 URL 中获取资源的功能,攻击者就可以构造一个特殊的请求地址,将其指向服务器 B,并提交给服务器 A。服务器 A 在没有对请求目标做充分校验的情况下,会以自身身份向服务器 B 发起请求。这样,攻击者就间接实现了对内网主机 B 的访问。
换句话说,攻击者并不直接访问目标服务,而是通过中间的受害服务器(A)来完成对目标(B)的探测、访问甚至攻击,从而绕过网络边界的安全限制。这种方式不仅能访问内网服务,还可能用于探测端口开放状态、访问云元数据接口,甚至执行远程命令。
攻击路径
示例
漏洞场景:某网站有一个在线加载功能可以把指定的远程文章加载到本地,链接如下:
http://www.xxx.com/article.php?url=https://blog.csdn.net/qq_43531669/article/details/112498646
假如系统没有对url参数进行任何的检查,就可以构造其他的请求,例如:
http://www.xxx.com/article.php?url=http://127.0.0.1:22
http://www.xxx.com/article.php?url=file:///etc/passwd
http://www.xxx.com/article.php?url=dict://127.0.0.1:22/data:data2 (dict可以向服务端口请求data data2)
http://www.xxx.com/article.php?url=gopher://127.0.0.1:2233/_test (向2233端口发送数据test,同样可以发送POST请求)
...
漏洞形成原理
许多网站为了实现功能,会提供从其他服务器获取数据的接口。例如通过指定的 URL 加载图片、下载文件或读取内容等。当这些接口未对目标地址进行严格校验时,就可能被攻击者利用,从而实现 SSRF。
SSRF 的本质是利用存在漏洞的 Web 应用作为代理,向远程或本地服务器发起请求。由于请求是由服务端发出,因此攻击者可借此访问那些本应只能由内网访问的资源。
该漏洞通常是由于服务端缺乏对目标地址的过滤和访问控制导致的。攻击者可利用该漏洞获取内部系统的信息,或进一步发起内网攻击。
漏洞的危害
- 对外网、服务器所在内网、本地进行端口扫描
- 向内部任意主机的任意端口发送payload来攻击内网服务
- DOS攻击(请求大文件,始终保持连接Keep-Alive Always)
- 攻击内网的web应用,如直接SQL注入、XSS攻击等
- 利用file、gopher、dict协议读取本地文件、执行命令等
- 可以无视网站CDN
内网服务防御相对外网服务来说一般会较弱,甚至部分内网服务为了运维方便并没有对内网的访问设置权限验证,所以存在SSRF时,通常会造成较大的危害。
SSRF 攻击
漏洞利用
产生漏洞的函数
根据后端所使用函数的不同,SSRF 漏洞的影响范围和利用方式也有所差异。在 PHP 中,以下函数如果使用不当,可能导致 SSRF 漏洞:
file_get_contents()
fsockopen()
curl_exec()
file_get_contents()
该函数用于将整个文件内容读入一个字符串中,是读取文件内容的首选方法之一。
例如,以下代码会输出 test.txt
文件中的内容:
<?php
echo file_get_contents("test.txt");
?>
fsockopen()
fsockopen()
函数用于打开一个网络连接,可用于向用户指定的 URL 发起请求(例如获取文件或 HTML 内容)。若传入的地址参数未做限制,也可能被用来发起 SSRF 请求。
curl_exec()
该函数用于执行一个 cURL 会话。当使用 curl_exec()
向外部资源发起请求时,若未对 URL 做严格校验,同样可能被攻击者利用实现 SSRF。
cURL 支持的协议包括但不限于:
http
/https
ftp
/ftps
file
gopher
dict
ldap
telnet
smtp
等
这意味着攻击者可通过构造带有特定协议的 URL 发起跨协议攻击,进一步扩大 SSRF 的危害范围。
漏洞检测
漏洞验证
因为SSRF漏洞是构造服务器发送请求的安全漏洞,所以我们可以通过抓包分析发送的请求是否是由服务器端发送的来判断是否存在SSRF漏洞。
在页面源码中查找访问的资源地址,如果该资源地址类型为http://www.xxx.com/a.php?image=地址
就可能存在SSRF漏洞。
漏洞的可能出现点
(1) 分享功能:通过URL地址分享文章等,例如如下地址:
http://share.xxx.com/index.php?url=http://www.xxx.com
通过url参数的获取来实现点击链接的时候跳到指定的分享文章。如果在此功能中没有对目标地址的范围做过滤与限制则就存在着SSRF漏洞。
(2)图片加载/下载:通过URL地址加载或下载图片:
http://image.xxx.com/image.php?image=http://www.xxx.com
图片加载存在于很多的编辑器中,编辑器上传图片处加载设定好的远程服务器上的图片地址,如果没对加载的参数做限制可能造成SSRF。
(3)图片/文章收藏功能:
http://title.xxx.com/title?title=http://title.xxx.com/xxx
例如 title参数是文章的标题地址,代表了一个文章的地址链接,如果收藏功能采用了此种形式保存文章,则在没有限制参数的形式下可能存在SSRF。
(4)转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览。
(5)在线翻译:给网址翻译对应网页的内容。
(6)邮件系统:比如接收邮件服务器地址。
(7)利用参数中的关键字查找:
关键字:
share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain...
总的来说,需要从远程服务器请求资源的网站都有可能存在SSRF漏洞。
SSRF攻防
绕过方法
绕过域名限制
有些功能限制只能访问某个固定域名,如 http://www.xxx.com
,可使用以下方法绕过:
使用 @
符号绕过
URL 格式:
[协议]://[用户名]:[密码]@[服务器地址]:[端口]/路径?参数#片段
示例:
http://www.xxx.com@evil.com # 实际请求的是 evil.com
http://www.aaa.com@www.bbb.com@ccc.com
在不同解析器中效果不同:
PHP parse_url()
:识别为 ccc.com
libcurl:识别为
bbb.com
绕过内网 IP 限制
有些系统禁止访问内网地址,如 127.0.0.1
、10.0.0.0/8
等,常见绕过方式包括:
1. 使用短网址
例如 https://t.cn/xxx
、https://bit.ly/xxx
等短链重定向。
2. 使用特殊解析域名(xip.io / xip.name)
xip.io
和 xip.name
支持将子域名自动解析为对应 IP 地址:
127.0.0.1.xip.io
www.10.0.0.2.xip.io
mysite.10.0.0.3.xip.name
foo.bar.192.168.1.1.xip.io
3. 进制绕过
表达方式 | 示例 | 实际地址 |
---|---|---|
十进制整数 | http://2130706433 | 127.0.0.1 |
十六进制 | http://0x7f.0x00.0x00.0x01 或 http://0x7f000001 | 127.0.0.1 |
八进制 | http://0177.0.0.1 | 127.0.0.1 |
八进制溢出 | http://265.0.0.3 | 10.0.0.3 |
混合进制 | http://0x0A.00.00.03 | 10.0.0.3 |
4. 使用 IPv6 简写
http://[::] # 等价于 http://127.0.0.1
5. 替换句号 .
为类似字符
http://127。0。0。1 # 全角句号
6. 添加端口号
http://127.0.0.1:8080
绕过协议限制
某些系统只允许访问以 http://
或 https://
开头的 URL,可尝试以下方式:
利用 302 跳转
请求一个合法地址,由其重定向至非法地址:
http://example.com/redirect?url=http://127.0.0.1
使用短地址服务
短地址可隐藏实际协议、IP 等。
正则限制绕过示例(进制)
源代码示例:
if (preg_match('#^https?://#i', $handler) !== 1) {die("Wrong scheme! You can only use http or https!");
} else if (preg_match('#^https?://10.0.0.3#i', $handler) === 1) {die("Restricted area!");
}
被阻止的请求:
http://10.0.0.3
可尝试的绕过方式:
http://0x0A000003 # 十六进制
http://012.0.0.3 # 八进制
http://2130706433 # 十进制
http://0x0A.0x00.0x00.0x03 # 混合进制
绕过方法对照表
绕过目标 | 绕过方式 |
---|---|
域名限制 | @ 符号、短网址、跳转地址 |
内网 IP 限制 | 进制转换、IPv6、xip.io 域名、句号替换、端口变换 |
协议限制 | 302 跳转、短地址伪装 |
防御措施
禁止 URL 跳转
- 禁止用户控制的 URL 发生 302/301 跳转。
- 对重定向逻辑进行校验,避免跳转到非预期地址。
禁用危险协议
- 拒绝解析非 HTTP 协议,例如:
file://
:本地文件读取gopher://
:可构造任意 TCP payloaddict://
:同样可用于投送任意数据包
- 只允许
http://
和https://
协议头通过。
限制端口范围
- 限制仅允许请求标准 HTTP/HTTPS 端口:
- 常见端口:80、443、8080
- 拒绝对内网常用端口访问,例如:
- Redis:6379
- MySQL:3306
- 内网 Web 应用:7001、8888 等
统一错误信息输出
- 不泄露服务端请求的详细错误信息(如:连接被拒绝、响应超时等)。
- 所有错误返回统一格式,防止攻击者据此判断端口开放状态或地址是否有效。
地址过滤与白名单校验
- 使用 白名单机制:只允许访问可信 URL/域名。
- 检查目标 IP 是否属于以下范围(常见内网地址):
127.0.0.0/8
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
169.254.0.0/16
- 使用 IP 解析和反向解析校验,避免利用域名跳转或 DNS 投毒绕过。
参考文献
-
SSRF漏洞原理解析[通俗易懂]
https://cloud.tencent.com/developer/article/2103240
-
SSRF漏洞原理攻击与防御(超详细总结)
https://blog.csdn.net/qq_43378996/article/details/124050308