flawopen.com/Teardowns/cve-2024-33066-qualcomm-wlan-log-redirection
CVE-2024-33066:高通 WLAN Resource Manager 日志重定向内存损坏
CVE-2024-33066 会在高通 WLAN Resource Manager 将日志重定向到调用方指定路径时造成内存损坏。高通于 2024 年 10 月发布修复。
想象一家酒店的前台,任何人打电话来都可以说:“以后把住客登记簿的副本寄到这个地址。”前台把对方口述的内容抄在一张小卡片上,哪怕字写出了卡片、写到了桌面上,然后照着上面写的地方寄出副本,连经理的保险柜也不例外。修复办法是只接受一个简短的标签,用来指明档案室里的某个抽屉。
核心概念与专有名词
WLAN Resource Manager- 高通
CVE-2024-33066公告中提到的 Wi-Fi 组件。它运行在高通的网络平台上,这些平台用于路由器、Mesh 系统和无线接入点。 IPQ / QCN 芯片组- 高通的 Wi-Fi 网络片上系统系列。受影响列表包括 IPQ5332、IPQ8074A、IPQ9574、QCN9000 以及 Immersive Home 平台,并非手机芯片。
CWE-20- 输入校验不当:软件信任输入,却没有检查它是否具备代码所依赖的属性,例如最大长度或允许的目录。
相对目录打开- 使用
openat()基于一个目录描述符、以不含 '/' 的纯文件名打开文件,这样结果就不可能落在该目录之外。 CVSS 3.1 向量- AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H:可经网络访问、复杂度低、无需权限和用户交互,对三项属性的影响都很高(评分 9.8)。
框架映射
- 本指南所述弱点的 CWE 参考: CWE-20
根本原因剖析 (Root Cause)
高通的公告只说明 WLAN Resource Manager 在“while redirecting log file to any file location with any file name”时发生内存损坏(CWE-20)。随日志重定向请求提供的目标,在使用前既没有检查长度,也没有检查指向哪里。下面的代码模拟了这一输入校验缺口,仅用于示意,并非高通源代码。
攻击执行流程分解
1. 收到日志重定向请求
WLAN Resource Manager 接受把日志输出发送到别处的请求。CVSS 向量(AV:N/PR:N/UI:N)将该缺陷评定为无需权限和用户交互即可经网络触达。
2. 接受任意位置和任意文件名
根据高通的描述,重定向可以指向任意位置、使用任意文件名:请求中的目标既没有做长度检查,也没有检查它指向哪里。
3. 未经检查的输入破坏内存
在下面的模型中,比固定路径缓冲区更长的名称会使其溢出(strcpy),而且任何路径(包括配置文件)都可以被打开写入。高通没有公布其代码中发生的是哪一种情况。
4. 影响与现状
CVSS 3.1 将对机密性、完整性和可用性的影响都评为高(9.8)。高通已在 2024 年 10 月的公告中把修复交付给设备厂商;CISA 的分级记录显示没有已知的利用。
源代码对比:漏洞与安全实现
这是该漏洞类别的示意模型,并非高通代码。加固后的处理函数在使用输入前先用 strnlen 限定长度,只接受来自固定字符集的短文件名(不含 '/',也不能以 '.' 开头),并用 openat() 相对于启动时选定的日志目录打开它,同时加上 O_NOFOLLOW,防止预先放置的符号链接改变写入目标。调用方可以选择文件名,但永远不能选择位置;过长的输入会被拒绝,而不是被复制。
#include <fcntl.h>
#include <string.h>
#define LOG_PATH_MAX 64
struct rm_log_cfg {
char path[LOG_PATH_MAX];
int fd;
};
/* 处理“重定向日志”请求;`requested` 来自请求正文。 */
int rm_set_log_destination(struct rm_log_cfg *cfg, const char *requested)
{
strcpy(cfg->path, requested); /* 未检查长度:过长的输入会越过 path[] 的边界 */
cfg->fd = open(cfg->path, O_WRONLY | O_CREAT | O_APPEND, 0644); /* 任意位置 */
return cfg->fd < 0 ? -1 : 0;
}
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define LOG_NAME_MAX 32
struct rm_log_cfg {
int dir_fd; /* 固定的日志目录,在启动时打开一次 */
char name[LOG_NAME_MAX];
int fd;
};
static int valid_log_name(const char *name, size_t len)
{
if (len == 0 || len >= LOG_NAME_MAX || name[0] == '.')
return 0;
for (size_t i = 0; i < len; i++) {
char c = name[i];
int ok = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
c == '-' || c == '_' || c == '.';
if (!ok)
return 0; /* 拒绝 '/',因此名称无法离开 dir_fd */
}
return 1;
}
int rm_set_log_destination(struct rm_log_cfg *cfg, const char *requested)
{
size_t len = strnlen(requested, LOG_NAME_MAX);
if (!valid_log_name(requested, len))
return -EINVAL;
int fd = openat(cfg->dir_fd, requested,
O_WRONLY | O_CREAT | O_APPEND | O_NOFOLLOW | O_CLOEXEC, 0600);
if (fd < 0)
return -errno;
memcpy(cfg->name, requested, len + 1);
if (cfg->fd >= 0)
close(cfg->fd);
cfg->fd = fd;
return 0;
}
工程与系统安全加固清单
- ✓在 CI 中用 64 个字符的名称、'../etc/passwd'、'/tmp/x'、'.hidden' 和含有 '/' 的名称调用 rm_set_log_destination,断言每次都返回 -EINVAL 且不创建文件。
- ✓当新增的 strcpy 或 sprintf 调用把请求字段写入固定大小缓冲区时让 CI 失败(例如用 semgrep 或 clang-tidy 规则),并要求使用 strnlen 或 snprintf 这类显式上限。
- ✓对每个可由调用方选择的输出文件,都用
openat()基于启动时打开的目录描述符解析,并传入 O_NOFOLLOW,确保任何文件名都无法逃出该目录。 - ✓清点基于 IPQ/QCN 芯片组的路由器和接入点,确认其固件已包含高通 2024 年 10 月的修复,并把管理和诊断接口限制在管理网络中。