DNS重绑定攻击漏洞分析与修复方案

本文详细分析了SSRF过滤器中存在的DNS重绑定攻击漏洞,探讨了不同Ruby版本下的安全差异,并提供了完整的漏洞验证脚本和修复建议,涉及网络安全核心技术原理。

DNS重绑定攻击漏洞报告

漏洞概述

在SSRF过滤器中存在DNS重绑定漏洞。该库验证主机名的IP地址,但随后将主机名传递给Net::HTTP.start(),后者会执行自己的DNS查询。攻击者可以控制一个DNS服务器,在验证期间返回安全的公共IP,然后在Net::HTTP稍后解析时返回127.0.0.1。

技术细节

漏洞代码

1
2
Net::HTTP.start(uri.hostname, uri.port, **http_options) do |http|
# 当前使用uri.hostname会触发新的DNS查询!

攻击示例

攻击者设置evil.com的DNS以交替响应:

  • 第一次查询(验证期间)返回8.8.8.8
  • 第二次查询(Net::HTTP内部查找)返回127.0.0.1

结果:SsrfFilter.get('http://evil.com:6379')绕过所有保护并命中本地主机上的某些内部服务。

修复建议

推荐修复方案

直接连接到验证过的IP而不是主机名:

  • Net::HTTP.start(uri.hostname, ...)改为Net::HTTP.start(validated_ip, ...)
  • 手动设置Host头:headers['Host'] = uri.hostname
  • 使用ssl_hostname: uri.hostname进行正确的TLS证书验证

版本分析

v1.1.2(安全)

安全原因是第178行的代码:

1
uri.hostname = ip

这行代码在将URI对象传递给Net::HTTP之前修改了它,因此即使代码显示Net::HTTP.start(uri.hostname, ...),它实际上使用的是IP地址。

v1.3.0(回归错误)

当PR #73添加ipaddr:选项时,它同时移除了uri.hostname = ip行。现在Net::HTTP直接接收主机名,这允许DNS重绑定。

技术验证

ipaddr选项测试

1
2
3
Net::HTTP.start('google.com', 80, ipaddr: '8.8.8.8') { |h| h.get('/') }
# 成功并返回Google首页
# 证明它连接到了google.com的真实IP,而不是8.8.8.8

跨版本测试结果

  • Ruby 2.6.10:完全忽略ipaddr参数
  • Ruby 3.3.0:ipaddr参数正常工作并防止DNS解析

防御建议

建议采用深度防御方法,在保留ipaddr参数的同时恢复v1.1.2中的uri.hostname = ip行。这将提供两个独立的保护层:

  • 即使Ruby的ipaddr实现存在错误
  • 或者有人使用不同的HTTP客户端

主机名替换仍然可以防止DNS重绑定。这是一个最小的代码变更,能显著增强安全态势。

影响评估

该漏洞允许攻击者通过DNS重绑定技术绕过SSRF保护,访问内部服务,安全风险等级为严重(10.0)。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计