利用DNS缓存和TLS协议将受限SSRF变为通用SSRF
字数 2764 2025-08-18 11:35:42

利用DNS缓存和TLS协议将受限SSRF变为通用SSRF技术分析

前言

本文基于BlackHat 2020议题《When TLS Hacks You》的技术内容,详细分析如何结合DNS缓存和TLS会话恢复机制,将受限的SSRF漏洞转变为通用SSRF攻击的技术原理、实现方法和防御措施。

背景知识

SSRF概述

原理:服务端提供了从其他服务器获取数据的功能,但未对目标地址做严格过滤与限制,导致攻击者可传入任意地址让后端服务器发起请求。

危害

  • 内网/本地端口扫描,获取开放端口信息
  • 主机信息收集,Web应用指纹识别
  • 针对特定应用发送payload攻击(如Struts2)
  • 攻击内网和本地应用程序及服务
  • 穿越防火墙
  • 利用file协议读取本地文件

常用协议

协议名称 简介
Gopher 攻击内部应用的主力军
Dict 端口探测,版本信息收集
ftp 探测是否存在ftp服务
http 探测是否存在SSRF
file 读取本地文件

注:JDK 1.7后不再支持Gopher协议

防御手段

  • 禁止跳转
  • 过滤返回信息
  • 禁用不需要的协议
  • 设置URL白名单或限制内网IP
  • 限制请求端口为HTTP常用端口
  • 统一错误信息
  • 请求资源前先访问DNS服务器判断是否为内网IP

DNS重绑定技术

针对"请求资源前先访问DNS服务器判断是否为内网IP"这一防御手段的绕过技术。

TTL(Time-To-Live)

  • DNS记录在DNS服务器上的缓存时间
  • 数值越小,修改记录各地生效时间越快
  • 设置为0表示不缓存

DNS解析流程

  1. 检查浏览器缓存
  2. 检查本机系统缓存(如HOSTS文件)
  3. 向本地域名解析服务系统发起请求
  4. 递归查询运营商DNS → 根域名服务器 → 顶级域名服务器 → NS域名服务器
  5. NS返回IP地址给本地服务器,本地服务器缓存解析结果(TTL)
  6. 解析结果返回用户,建立TCP通信

DNS重绑定

  • 第一次DNS查询返回IP地址A
  • 第二次DNS查询返回不同于A的IP地址B
  • 利用检查逻辑与请求间隔的差异实现绕过

技术实现

实验设置

  1. DNS服务器配置

    • 设置域名的A记录及NS记录
    • 指定DNS服务器为攻击者搭建的服务器
  2. DNS服务代码

from twisted.internet import reactor, defer
from twisted.names import client, dns, error, server

record={}

class DynamicResolver(object):
    def _doDynamicResponse(self, query):
        name = query.name.name
        if name not in record or record[name]<1:
            ip="106.56.229.29" # 外网IP
        else:
            ip="127.0.0.1"
        if name not in record:
            record[name]=0
        record[name]+=1
        print name+" ===> "+ip
        answer = dns.RRHeader(
            name=name,
            type=dns.A,
            cls=dns.IN,
            ttl=0, # TTL设置为0
            payload=dns.Record_A(address=b'%s'%ip,ttl=0)
        )
        answers = [answer]
        authority = []
        additional = []
        return answers, authority, additional
    
    def query(self, query, timeout=None):
        return defer.succeed(self._doDynamicResponse(query))

def main():
    factory = server.DNSServerFactory(
        clients=[DynamicResolver(), client.Resolver(resolv='/etc/resolv.conf')]
    )
    protocol = dns.DNSDatagramProtocol(controller=factory)
    reactor.listenUDP(53, protocol)
    reactor.run()

if __name__ == '__main__':
    raise SystemExit(main())

TLS会话恢复机制

完整TLS握手

  1. Client发送ClientHello
  2. Server回复ServerHello
  3. Client回复最终确定的Key,Finished
  4. Server回复Finished
  5. 握手完毕,Client发送加密HTTP请求
  6. Server回复加密HTTP响应

花费2RTT(Round-Trip-Time)

优化方案

  1. Session ID

    • 服务端记住会话状态
    • 客户端发送带session id的client hello
    • 服务端返回之前存储的SSL会话
  2. Session Ticket

    • 客户端记住会话状态
    • 服务端记住用于加密返回给客户端的ticket的密钥
  3. PSK(Pre-Shared Key)

    • 首次建立会话时生成PSK
    • 服务器用ticket key加密PSK作为Session Ticket返回

注:session id只能存放32字节payload,session ticket能放更多字节

HTTPS协议实现(CURL)

在curl实现中只检查域名、端口和协议,未检查IP,因此可利用DNS重绑定绕过。

攻击方法

攻击条件

  1. 受限的SSRF漏洞
  2. 带外通信的TLS session
  3. 本地端口上运行的应用

实际漏洞案例

  1. Youtrack - CVE-2019-12852
  2. Nextcloud - 分享功能造成的SSRF,使用TLS重绑定攻击本地memcached

攻击面

  • OIDC discovery
  • Webpush
  • Webmention
  • Apple Pay Web
  • Wifi captive portals
  • SSDP
  • SVG conversion
  • URL-based XXE
  • Scraping
  • Webhooks
  • PDF renderers with images enabled

攻击流程(Demo: Phishing→CSRF→RCE)

  1. 攻击者制作钓鱼邮件,内容标签指向攻击者网站(如ssltest.jmaddux.com:11211)
  2. 受害者打开邮件,请求域名
  3. 客户端向攻击者DNS服务器请求解析
  4. DNS服务器返回正常TLS Server地址,TTL=0
  5. 客户端发送Client Hello
  6. 服务端返回Server Hello,在session id/ticket/psk字段设置payload
  7. 完成TLS握手
  8. 网站返回301跳转到ssltest.jmaddux.com:11211
  9. 由于TTL=0,客户端再次询问DNS
  10. DNS返回127.0.0.1
  11. 客户端使用带有payload的SSL会话缓存访问127.0.0.1:11211
  12. payload加载完成实现RCE

防御措施

技术层面

  1. 改变TLS缓存key值

    • 当前:(hostname, port)
    • 建议:(hostname, port, ip_addr)或(hostname, port, addr_type(ip_addr))
  2. 禁用带外通信时的TLS session resumption

    • libcurl: CURLOPT_SSL_SESSIONID_CACHE=false
    • Firefox: security.ssl.disable_session_identifiers=true
    • Tor browser: 默认禁用
    • Java, Nodejs, Chrome等: 无选项
  3. Web应用防御

    • 关注webhooks, apple pay等应用
    • 对出站请求设置代理监控(如smokescreen工具)
    • 阻止未经验证的内部TCP内容运行,特别是带换行符的内容

替代方案

  • 杜绝DNS重绑定:第一次解析后直接使用解析返回的IP替换域名访问URL

总结与思考

  1. 技术启示:

    • HTTPS本为防止中间人攻击的安全协议,却因优化算法成为攻击媒介
    • 安全开销与时间开销的平衡问题
  2. 关键结论:

    • TLS对SSRF攻击有重要利用价值
    • 需要持续关注最新技术手段以打破常规
    • 必须认真权衡TLS会话重用的利弊
  3. 研究方向:

    • 建设更好的测试基础设施
    • 开发Alternating DNS Server、Custom TLS等工具

参考资源

  1. TLS-poison GitHub项目
  2. PortSwigger相关报道
  3. Security Boulevard报道
  4. DEF CON演讲信息
  5. TLS会话恢复技术文档
利用DNS缓存和TLS协议将受限SSRF变为通用SSRF技术分析 前言 本文基于BlackHat 2020议题《When TLS Hacks You》的技术内容,详细分析如何结合DNS缓存和TLS会话恢复机制,将受限的SSRF漏洞转变为通用SSRF攻击的技术原理、实现方法和防御措施。 背景知识 SSRF概述 原理 :服务端提供了从其他服务器获取数据的功能,但未对目标地址做严格过滤与限制,导致攻击者可传入任意地址让后端服务器发起请求。 危害 : 内网/本地端口扫描,获取开放端口信息 主机信息收集,Web应用指纹识别 针对特定应用发送payload攻击(如Struts2) 攻击内网和本地应用程序及服务 穿越防火墙 利用file协议读取本地文件 常用协议 : | 协议名称 | 简介 | |---------|------| | Gopher | 攻击内部应用的主力军 | | Dict | 端口探测,版本信息收集 | | ftp | 探测是否存在ftp服务 | | http | 探测是否存在SSRF | | file | 读取本地文件 | 注:JDK 1.7后不再支持Gopher协议 防御手段 : 禁止跳转 过滤返回信息 禁用不需要的协议 设置URL白名单或限制内网IP 限制请求端口为HTTP常用端口 统一错误信息 请求资源前先访问DNS服务器判断是否为内网IP DNS重绑定技术 针对"请求资源前先访问DNS服务器判断是否为内网IP"这一防御手段的绕过技术。 TTL(Time-To-Live) : DNS记录在DNS服务器上的缓存时间 数值越小,修改记录各地生效时间越快 设置为0表示不缓存 DNS解析流程 : 检查浏览器缓存 检查本机系统缓存(如HOSTS文件) 向本地域名解析服务系统发起请求 递归查询运营商DNS → 根域名服务器 → 顶级域名服务器 → NS域名服务器 NS返回IP地址给本地服务器,本地服务器缓存解析结果(TTL) 解析结果返回用户,建立TCP通信 DNS重绑定 : 第一次DNS查询返回IP地址A 第二次DNS查询返回不同于A的IP地址B 利用检查逻辑与请求间隔的差异实现绕过 技术实现 实验设置 DNS服务器配置 : 设置域名的A记录及NS记录 指定DNS服务器为攻击者搭建的服务器 DNS服务代码 : TLS会话恢复机制 完整TLS握手 : Client发送ClientHello Server回复ServerHello Client回复最终确定的Key,Finished Server回复Finished 握手完毕,Client发送加密HTTP请求 Server回复加密HTTP响应 花费2RTT(Round-Trip-Time) 优化方案 : Session ID : 服务端记住会话状态 客户端发送带session id的client hello 服务端返回之前存储的SSL会话 Session Ticket : 客户端记住会话状态 服务端记住用于加密返回给客户端的ticket的密钥 PSK(Pre-Shared Key) : 首次建立会话时生成PSK 服务器用ticket key加密PSK作为Session Ticket返回 注:session id只能存放32字节payload,session ticket能放更多字节 HTTPS协议实现(CURL) 在curl实现中只检查域名、端口和协议,未检查IP,因此可利用DNS重绑定绕过。 攻击方法 攻击条件 受限的SSRF漏洞 带外通信的TLS session 本地端口上运行的应用 实际漏洞案例 Youtrack - CVE-2019-12852 Nextcloud - 分享功能造成的SSRF,使用TLS重绑定攻击本地memcached 攻击面 OIDC discovery Webpush Webmention Apple Pay Web Wifi captive portals SSDP SVG conversion URL-based XXE Scraping Webhooks PDF renderers with images enabled 攻击流程(Demo: Phishing→CSRF→RCE) 攻击者制作钓鱼邮件,内容标签指向攻击者网站(如ssltest.jmaddux.com:11211) 受害者打开邮件,请求域名 客户端向攻击者DNS服务器请求解析 DNS服务器返回正常TLS Server地址,TTL=0 客户端发送Client Hello 服务端返回Server Hello,在session id/ticket/psk字段设置payload 完成TLS握手 网站返回301跳转到ssltest.jmaddux.com:11211 由于TTL=0,客户端再次询问DNS DNS返回127.0.0.1 客户端使用带有payload的SSL会话缓存访问127.0.0.1:11211 payload加载完成实现RCE 防御措施 技术层面 改变TLS缓存key值 : 当前:(hostname, port) 建议:(hostname, port, ip_ addr)或(hostname, port, addr_ type(ip_ addr)) 禁用带外通信时的TLS session resumption : libcurl: CURLOPT_SSL_SESSIONID_CACHE=false Firefox: security.ssl.disable_session_identifiers=true Tor browser: 默认禁用 Java, Nodejs, Chrome等: 无选项 Web应用防御 : 关注webhooks, apple pay等应用 对出站请求设置代理监控(如smokescreen工具) 阻止未经验证的内部TCP内容运行,特别是带换行符的内容 替代方案 杜绝DNS重绑定:第一次解析后直接使用解析返回的IP替换域名访问URL 总结与思考 技术启示: HTTPS本为防止中间人攻击的安全协议,却因优化算法成为攻击媒介 安全开销与时间开销的平衡问题 关键结论: TLS对SSRF攻击有重要利用价值 需要持续关注最新技术手段以打破常规 必须认真权衡TLS会话重用的利弊 研究方向: 建设更好的测试基础设施 开发Alternating DNS Server、Custom TLS等工具 参考资源 TLS-poison GitHub项目 PortSwigger相关报道 Security Boulevard报道 DEF CON演讲信息 TLS会话恢复技术文档