一、漏洞简介

1.1 漏洞背景

c-ares 是一个 C 语言的异步 DNS 解析库,被众多知名项目使用,包括 gRPC、Node.js、curl、Wireshark 等。gRPC 的 C++ 实现使用 c-ares 进行 DNS 解析。DNS 查询 ID 是用于匹配 DNS 请求和响应的 16 位标识符,如果该 ID 可预测,攻击者可以伪造 DNS 响应,实现 DNS 劫持攻击。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2020-7768
危害等级 HIGH / 7.5
漏洞类型 c-ares DNS 查询 ID 随机性不足
披露时间 2020-11-11
影响组件 gRPC
属性
CVE 编号 CVE-2020-7768
危害等级 中危 (Medium)
CVSS 评分 5.9 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N)
CWE 编号 CWE-338 (Use of Cryptographically Weak Pseudo-Random Number Generator)
影响组件 c-ares
GHSA 编号 GHSA-8r8p-23f3-64c2

补充核验信息:公开时间:2020-11-11;NVD 评分:7.5(HIGH);CWE:CWE-1321。

二、影响范围

2.1 受影响的版本

  • c-ares < 1.19.1
  • 使用受影响版本 c-ares 的 gRPC C++ 版本

2.2 不受影响的版本

  • c-ares >= 1.19.1

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. 系统中 /dev/urandomRtlGenRandom() 不可用
  2. 攻击者能够监听并响应受害者的 DNS 查询
  3. 攻击者能够预测 DNS 查询 ID

三、漏洞详情与原理解析

3.1 漏洞触发机制

当系统缺乏加密安全的随机数源时,c-ares 回退使用 rand() 函数生成 DNS 查询 ID:

  1. 未使用 CSPRNG:当 /dev/urandom 或 Windows 的 RtlGenRandom() 不可用时,使用标准 rand() 函数
  2. 未初始化种子rand() 函数未通过 srand() 进行种子初始化,产生可预测的输出
  3. 弱 RC4 实现:随机数输入被送入一个非标准的 RC4 实现,强度弱于原始 RC4
  4. 未检测现代 CSPRNG:未尝试使用广泛可用的 arc4random() 等现代系统提供的 CSPRNG

攻击者可以: 1. 观察受害者的 DNS 查询模式 2. 预测下一个 DNS 查询 ID 3. 发送伪造的 DNS 响应 4. 将受害者引导至恶意服务器

3.2 源码层面的根因分析(结合源码与补丁对比)

漏洞代码位置src/lib/ares_init.csrc/lib/ares_rand.c

问题代码片段(修复前):

// 当 /dev/urandom 不可用时使用 rand()
static void randomize_id(unsigned short *id) {
    *id = (unsigned short)(rand() % 65536);  // 可预测的随机数
}

修复补丁(c-ares 1.19.1):

// 优先使用 arc4random()
#ifdef HAVE_ARC4RANDOM
static void randomize_id(unsigned short *id) {
    *id = (unsigned short)arc4random();
}
#else
// 回退到 /dev/urandom 或 RtlGenRandom()
static void randomize_id(unsigned short *id) {
    if (read_from_urandom(id, sizeof(*id)) != ARES_SUCCESS) {
        // 最后手段:使用 srand() 初始化的 rand()
        srand((unsigned int)time(NULL) ^ getpid());
        *id = (unsigned short)(rand() % 65536);
    }
}
#endif

修复要点: 1. 检测并优先使用 arc4random() 2. 使用 /dev/urandomRtlGenRandom() 作为回退 3. 使用标准 RC4 算法替代非标准实现 4. 在使用 rand() 时通过 srand() 初始化种子

四、漏洞复现(可选)

4.1 环境搭建

# 编译受影响版本的 c-ares
git clone https://github.com/c-ares/c-ares.git
cd c-ares
git checkout cares-1_19_0  # 受影响版本
mkdir build && cd build
cmake ..
make

4.2 PoC 演示与测试过程

#!/usr/bin/env python3
"""
DNS 查询 ID 预测 PoC
演示当 /dev/urandom 不可用时,DNS 查询 ID 的可预测性
"""
import subprocess
import re

def predict_dns_id(seed):
    """模拟 c-ares 的 ID 生成算法(当使用未初始化的 rand())"""
    # 标准 rand() 实现通常是线性同余生成器
    return (seed * 1103515245 + 12345) % 65536

# 观察多个 DNS 查询的 ID 模式
# 在受影响的系统上,这些 ID 会表现出可预测的模式
print("检测 DNS 查询 ID 可预测性...")
print("注意:此 PoC 仅用于教育目的")

五、修复建议与缓解措施

5.1 官方版本升级建议

组件 修复版本
c-ares >= 1.19.1
gRPC C++ 升级到使用 c-ares >= 1.19.1 的版本
Node.js >= 相关安全补丁版本

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

  1. 确保随机数源可用: ```bash # 检查 /dev/urandom 是否可用 ls -la /dev/urandom

# 如有必要,确保设备文件存在 mknod -m 444 /dev/urandom c 1 9 ```

  1. 网络层防护
  2. 部署 DNSSEC 防止 DNS 欺骗
  3. 使用 DNS over HTTPS (DoH) 或 DNS over TLS (DoT)
  4. 配置防火墙规则限制 DNS 响应来源

  5. 配置 DNS 服务器白名单yaml # gRPC channel 配置 grpc: dns_resolver: allowed_servers: - "8.8.8.8" - "8.8.4.4"

六、参考信息 / 参考链接

6.1 官方安全通告

6.2 其他技术参考资料