服务器端请求伪造(SSRF)漏洞与防护(协议层深度防御与旁路技术篇)
一、题目描述与概念深化
您列出的题目中已有多篇SSRF相关内容。本篇将聚焦于一个更深入、更易被忽视的维度:SSRF漏洞在利用非HTTP/HTTPS协议时的深度攻击手法,以及在此场景下的进阶防御策略。
传统SSRF防护通常聚焦于拦截对内部网络或危险端点的HTTP请求。然而,现代应用程序在处理用户提供的URL时,可能隐式支持或依赖底层库(如cURL、libwww-perl)解析更多协议(如file://、gopher://、dict://、tftp://、ldap://,甚至自定义协议处理器)。攻击者利用这些协议,不仅能访问内部服务,还能实施更复杂的攻击,如与无认证的内部服务交互、进行端口扫描、触发特定应用层协议行为以绕过防御,或通过协议特性进行信息泄露。本题目旨在系统剖析这些“旁路”技术,并构建协议层的纵深防御。
二、解题过程(漏洞利用技术循序剖析)
步骤1:理解基础协议滥用
SSRF利用的起点是应用程序接受用户输入的URL(或可构造为URL的字符串),并直接用于发起后端网络请求。除了常见的http(s)://,许多编程语言的网络库支持其他协议:
file://: 用于读取服务器本地文件。例如,file:///etc/passwd可尝试读取系统密码文件。gopher://: 一种古老的协议,但其数据包结构灵活,可被用于构造任意TCP数据包,攻击内网的Redis、Memcached、MySQL等服务(即“Gopher协议攻击”)。dict://: 字典协议,常用于查询字典服务器,也可用于向指定端口发送任意指令,探测服务。tftp://: 简单文件传输协议,可能用于内部文件操作或触发特定行为。
关键点: 攻击者发现SSRF入口后,首先会尝试使用这些协议前缀,观察服务器的响应差异或错误信息,以判断后端使用的网络库及其支持的能力。
步骤2:利用协议特性进行端口扫描与服务探测
当目标应用位于能够访问内网的环境(如容器、云服务器实例)时,SSRF可变为一个内网探测工具。
- 基本原理: 通过构造指向内网IP和不同端口的URL(如
http://192.168.1.1:22、dict://192.168.1.1:6379/info),根据响应时间、错误内容或状态码,判断端口是否开放及服务类型。 - 旁路技术: 如果应用对
http(s)请求有严格过滤(如禁止对内网IP发起请求),攻击者可能转而使用gopher://或dict://协议,因为这些协议的请求可能在过滤逻辑之外被处理。例如,一个只检查Host头的过滤器,对gopher://请求可能无效。 - 时间盲测: 利用请求响应的时间差(Time-based Blind SSRF)。通过向目标端口发送请求并测量服务器响应时间,开放端口(尤其是那些会建立连接但等待超时的服务)通常比关闭端口(立即连接拒绝)响应更慢。
步骤3:利用非HTTP协议攻击特定内部服务(以攻击Redis为例)
这是SSRF最危险的利用场景之一。假设内网存在一个无认证的Redis服务(192.168.0.10:6379),攻击者无法直接访问,但存在一个存在SSRF漏洞的Web应用。
- 目标: 通过SSRF向Redis发送命令,实现远程代码执行。
- 利用流程:
a. 协议选择: Redis使用纯文本协议。gopher://协议可以构造任意的TCP流。
b. 构造Payload: 攻击者需要构造一个符合Redis协议的命令。例如,想写入一个Web Shell,命令可能为:
flushall set shell "<?php @eval($_POST['cmd']);?>" config set dir /var/www/html config set dbfilename shell.php save
c. Gopher编码: 将上述命令按Redis协议格式(每行以\r\n结尾)进行URL编码,并嵌入Gopher URL。Gopher URL格式为gopher://<host>:<port>/_<TCP数据>,其中_后跟的数据会被直接发送。
d. 最终利用URL: 攻击者提交类似gopher://192.168.0.10:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$5%0d%0ashell%0d%0a$31%0d%0a...(编码后的完整命令)的URL给存在漏洞的应用。
e. 结果: 漏洞应用的后端使用支持Gopher协议的库(如旧版cURL)发起请求,将恶意命令包发送给内网Redis,成功写入Web Shell。
步骤4:绕过常见防护措施
- 黑名单/白名单过滤绕过:
- 使用进制或八进制IP表示: 如将
127.0.0.1转换为2130706433(十进制)、0x7f000001(十六进制URL编码为0x7f000001)或0177.0.0.01(八进制)。 - 利用DNS解析特性: 使用
localhost、localtest.me(解析到127.0.0.1)、或指向目标IP的短域名服务。 - 使用
@符号: URL语法中@用于分隔认证信息,如http://expected-host@evil-host:80/。拙劣的解析器可能只检查@之前的部分(expected-host),而实际请求发往evil-host。 - 利用重定向: 攻击者控制一个外部网站,该网站返回一个指向内网地址的
302重定向。如果漏洞应用跟随重定向,且过滤器只检查初始URL,则攻击成功。
- 使用进制或八进制IP表示: 如将
- 协议限制绕过:
- 利用URL解析差异: 某些库(如PHP的
parse_url和cURL的解析)对URL的解析可能存在差异,导致判断不一致。 - 使用
file://协议结合路径遍历: 如file:///etc/../../var/www/html/config.php。 - 利用
ftp://或tftp://协议: 这些协议可能被允许,且能用于与内部系统交互或泄露数据(如被动模式FTP可能泄露客户端IP/端口)。
- 利用URL解析差异: 某些库(如PHP的
三、防护策略(协议层纵深防御)
第1层:输入验证与标准化
- 严格白名单协议: 只允许业务必需的协议,如仅
https。在代码中明确拒绝file、gopher、dict、ftp等危险协议。 - URL规范化与解析一致性: 使用统一的、严格的URL解析库,提取
host、port、scheme。确保后续的过滤和请求发起使用相同的解析结果,避免因解析差异导致绕过。 - 禁止用户控制完整的URL: 如果可能,不要让用户提供完整的URL。改为提供选项(如“从以下图床列表选择”),或只允许用户提供路径部分,由服务器拼接固定的安全域名。
第2层:网络层访问控制
- 出站网络限制: 在服务器或容器级别,使用防火墙规则限制出站连接。只允许应用程序访问明确需要的外部白名单IP和端口,阻断所有到内网RFC 1918地址段(如
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)以及回环地址(127.0.0.0/8、::1)的连接。 - 使用网络命名空间或沙箱: 将执行网络请求的服务运行在独立的、网络受限的容器或沙箱环境中,该环境没有权限访问敏感的内网段。
第3层:应用层请求管理
- 使用中间代理或网关: 所有出站请求通过一个受控的代理服务器发出。该代理实施强安全策略:协议白名单、目标地址/端口白名单、不跟随重定向、设置超时等。
- 禁用危险库特性: 在使用的网络客户端库中,明确禁用对危险协议的支持。例如,在编译cURL时禁用不需要的协议,或在运行时通过库的配置选项禁用。
- 响应一致性处理: 确保无论后端请求成功与否,返回给用户的错误信息都是统一的、不泄露内部网络细节的通用消息。避免通过响应时间差泄露端口状态。
第4层:监控与响应
- 日志记录所有出站请求: 详细记录发起的每个请求的目标URL、协议、时间戳。监控异常模式,如大量对内部地址的请求、使用非标准协议。
- 部署网络层IDS/IPS: 在网络边界部署入侵检测/防御系统,识别异常的出站连接模式,特别是对内网服务特定端口(如Redis的6379)的连接尝试。
四、总结
SSRF漏洞的协议层攻击代表了其最隐蔽和危害最大的形式。防御的关键在于不信任任何用户输入的URL,并在协议支持、目标地址、出站网络三个层面实施默认拒绝的白名单策略。通过结合应用层的严格验证、系统层的网络隔离、以及持续的监控审计,才能有效遏制SSRF漏洞被利用进行深层次的内网渗透。在微服务与云原生架构下,对工作负载的网络边界进行明确定义和强制实施(如服务网格的mTLS和策略),是防御此类攻击的现代基石。