一、漏洞简介¶
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 触发条件(如特定模块、特定配置、特定运行环境等)¶
- 系统中
/dev/urandom或RtlGenRandom()不可用 - 攻击者能够监听并响应受害者的 DNS 查询
- 攻击者能够预测 DNS 查询 ID
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
当系统缺乏加密安全的随机数源时,c-ares 回退使用 rand() 函数生成 DNS 查询 ID:
- 未使用 CSPRNG:当
/dev/urandom或 Windows 的RtlGenRandom()不可用时,使用标准rand()函数 - 未初始化种子:
rand()函数未通过srand()进行种子初始化,产生可预测的输出 - 弱 RC4 实现:随机数输入被送入一个非标准的 RC4 实现,强度弱于原始 RC4
- 未检测现代 CSPRNG:未尝试使用广泛可用的
arc4random()等现代系统提供的 CSPRNG
攻击者可以: 1. 观察受害者的 DNS 查询模式 2. 预测下一个 DNS 查询 ID 3. 发送伪造的 DNS 响应 4. 将受害者引导至恶意服务器
3.2 源码层面的根因分析(结合源码与补丁对比)¶
漏洞代码位置:src/lib/ares_init.c 和 src/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/urandom 或 RtlGenRandom() 作为回退
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 规则等)¶
- 确保随机数源可用: ```bash # 检查 /dev/urandom 是否可用 ls -la /dev/urandom
# 如有必要,确保设备文件存在 mknod -m 444 /dev/urandom c 1 9 ```
- 网络层防护:
- 部署 DNSSEC 防止 DNS 欺骗
- 使用 DNS over HTTPS (DoH) 或 DNS over TLS (DoT)
-
配置防火墙规则限制 DNS 响应来源
-
配置 DNS 服务器白名单:
yaml # gRPC channel 配置 grpc: dns_resolver: allowed_servers: - "8.8.8.8" - "8.8.4.4"