PJSIP DNS 压缩指针堆越界读取(远程拒绝服务)
pjlib-util/src/pjlib-util/dns.c 中的 get_name_len() 和 get_name() 通过 pj_memcpy(&offset, p, 2) 读取一个 2 字节的 DNS 压缩指针,但未检查这两个字节是否都位于数据包缓冲区之内。当 0xC0 字节(压缩指针标记)恰好是 DNS 响应的最后一个字节时,memcpy 会读取超出堆分配边界的一个字节。
单个 20 字节的精心构造的 DNS 响应即可触发越界读取,并使任何通过网络控制的 DNS 服务器解析域名的 PJSIP 应用程序(包括 Asterisk 和 FreeSWITCH)崩溃。
ASan 确认:heap-buffer-overflow READ of size 2, 0 bytes after 20-byte region,发生在 get_name_len() 中。
这是自 2022 年以来 pjproject 的 dns.c 中第三个 DNS 解析器 CVE(此前为 CVE-2022-24793 和 CVE-2023-27585)。CVE-2022-24793 的修复在整个解析器中添加了长度检查,但遗漏了压缩指针路径。
受影响版本:pjproject <= 2.16。已在 pjproject 2.17 中修复(提交 5311aee)。
# 独立 ASan 测试工具(无需安装 pjproject):
./run.sh
# 或手动编译并运行:
gcc -fsanitize=address,undefined -g -O0 harness.c -o harness
./harness
# 网络 PoC —— 恶意 DNS 服务器:
python3 poc.py --port 5353
# 然后将 PJSIP 应用程序指向该恶意服务器:
# pjsua --nameserver 127.0.0.1:5353 sip:[email protected]
# Docker(从源码构建带 ASan 的 pjproject 2.16):
docker compose build
docker compose run harness
gcc本项目根据 GNU GPLv3 许可证发布。
本项目仅供防御性安全研究、教育和授权测试使用。未经所有者明确许可,请勿将此代码用于任何系统或服务。
未经授权的使用可能违反适用法律。作者不授予对第三方系统进行测试的许可,且不对滥用行为负责。
有关保修和责任条款,请参阅 LICENSE 文件。
| 文件 | 描述 |
|---|
harness.c | 独立 ASan 测试工具 —— 使用与 pjproject 完全相同的类型重新实现存在漏洞的 get_name_len() |
harness_pjlib.c | 调用真实 pj_dns_parse_packet() 的替代测试工具(用于 Docker) |
poc.py | 恶意 DNS 服务器 —— 对任何查询均响应精心构造的 20 字节数据包 |
run.sh | 构建并运行 ASan 测试工具 |
Dockerfile | 从源码构建带 ASan 的 pjproject 2.16 |
docker-compose.yml | 用于 pjlib 测试工具的 Docker 配置 |
proof_output.txt | 实际验证的完整 ASan 输出 |