flawopen.com/Teardowns/cve-2024-33066-qualcomm-wlan-log-redirection
CVE-2024-33066:Qualcomm WLANログのメモリ破損
CVE-2024-33066は、Qualcomm WLAN Resource Managerが呼び出し側指定のパスへログを転送する際にメモリを破損する可能性があります。2024年10月に修正されました。
誰でも電話で「これからは宿泊者名簿のコピーをこの住所に送って」と頼めるホテルのフロントを想像してください。フロント係は言われたとおりに小さなカードへ書き写しますが、文字がカードからはみ出して机にまで書かれても気にしません。そして書かれた先へコピーを送ります。支配人の金庫でさえもです。修正は、資料室の引き出しを示す短いラベルだけを受け付けることです。
主要な概念と専門用語
WLAN Resource Manager- Qualcomm の
CVE-2024-33066の公告で名指しされた Wi-Fi コンポーネント。ルーター、メッシュシステム、アクセスポイントに使われる Qualcomm のネットワーク用プラットフォームで動作します。 IPQ / QCN チップセット- Qualcomm の Wi-Fi ネットワーク向け SoC ファミリー。影響を受ける一覧には 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:ネットワーク経由で到達可能、攻撃条件の複雑さは低、権限もユーザー操作も不要で、3 つの特性すべてへの影響が高い(スコア 9.8)。
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-20
根本原因の分析 (Root Cause)
Qualcomm の公告は、WLAN Resource Manager で "while redirecting log file to any file location with any file name" の際にメモリ破損が起きたと述べているだけです(CWE-20)。ログのリダイレクト要求とともに渡される宛先が、長さも指す場所も確かめられないまま使われていました。以下のコードはその入力検証の欠落をモデル化した説明用のもので、Qualcomm のソースコードではありません。
ステップ・バイ・ステップの攻撃フロー
1. ログのリダイレクト要求が届く
WLAN Resource Manager は、ログ出力を別の場所へ送る要求を受け付けます。CVSS ベクトル(AV:N/PR:N/UI:N)は、この欠陥を権限もユーザー操作もなくネットワークから到達できるものと評価しています。
2. どんな場所・どんな名前も受け付ける
Qualcomm の説明によれば、リダイレクトはどんな場所へも、どんなファイル名でも行えました。要求された宛先について、長さも指す場所も確認されていませんでした。
3. 未検証の入力がメモリを壊す
以下のモデルでは、固定長のパスバッファより長い名前がバッファをあふれさせ(strcpy)、設定ファイルを含むどんなパスでも書き込み用に開けます。Qualcomm は自社コードでどちらが起きたかを公表していません。
4. 影響と現状
CVSS 3.1 は機密性・完全性・可用性への影響をすべて高と評価しています(9.8)。Qualcomm は 2024 年 10 月の公告で修正を機器メーカーに提供し、CISA のトリアージでは既知の悪用は記録されていません。
ソースコード比較:脆弱 vs 堅牢化
脆弱性のクラスを示す説明用モデルで、Qualcomm のコードではありません。強化版のハンドラーは、使う前に 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 チップセットを使うルーターやアクセスポイントを洗い出し、ファームウェアに Qualcomm の 2024 年 10 月の修正が入っていることを確認し、管理・診断インターフェースは管理用ネットワークに限定する。