FlawOpen

flawopen.com/Teardowns/cve-2026-49878-android-wpa-supplicant-oob-write

● CVE-2026-49878 · CVSS 7.2 · 高危
安全研究 · FlawOpen

CVE-2026-49878:Android Wi-Fi 越界写入

Android 2026 年 10 月安全公告将此 Wi-Fi 远程代码执行漏洞评为高危。AOSP 修复了两个按字节数而非元素数分配的 int 数组。记录说明需要系统权限。

💡 通俗易懂的原理解析 (ELI5)

想象一个货架每件物品要放一个盒子,管理员却只按每盒一厘米预留空间,盒子就会超出货架。在 CVE-2026-49878 中,wpa_supplicant 把整数数量当成字节数量分配,却写入完整整数。Google 补丁按每个元素的实际大小分配空间。

核心概念与专有名词

wpa_supplicant
Android Wi-Fi 组件,负责无线身份验证和相关 802.11 交互。
SCS 响应
Stream Classification Service 响应动作帧。只有存在待处理请求且 dialog token 匹配时才处理。
越界写入(CWE-787)
写入超出已分配内存区域。此处按字节分配,却将缓冲区作为 int 元素数组索引。
os_zalloc / os_calloc
wpa_supplicant 内存分配辅助函数。补丁传入元素数量和元素大小,以分配完整数组。
Android 安全补丁级别(SPL)
标识已安装安全更新级别的日期。Google 表示 2026-10-01 或更高版本修复公告问题;更新由设备厂商推送。

框架映射

  • 本指南所述弱点的 CWE 参考: CWE-787

根本原因剖析 (Root Cause)

在 wpas_handle_robust_av_scs_recv_action 中,scs_resp[0] 和 [1] 是 int * 数组。修复前,os_zalloc(num_active_scs) 只分配该数量的字节。代码按索引写入整数 ID 和状态,因此需要 num_active_scs * sizeof(int) 字节。待处理请求、匹配的 dialog token、计数和帧长检查限制输入,但没有修正分配单位。AOSP 补丁将两处调用改为 os_calloc(num_active_scs, sizeof(int))。不能仅凭越界写入推断攻击者可控的代码执行路径。

攻击执行流程分解

步骤 1

响应帧被分发到处理函数

Wi-Fi 路径将 Robust AV SCS Response 帧交给受影响处理函数。该路径面向网络,但公开资料未证明任意未认证发送方都能满足所有前提。

步骤 2

检查请求状态和边界

处理函数要求待处理请求和匹配 token,并校验数量。NVD 中 CISA-ADP 的向量为 AV:N/AC:L/PR:H/UI:N;应保留高权限前提。

步骤 3

整数写入超出字节分配空间

旧代码每条记录只分配一个字节,却写入 int 值。限制元素数量不会增加已分配字节数。

步骤 4

十月补丁修正分配方式

Google 修改为两个数组各分配 num_active_scs 个 sizeof(int) 元素。不要编造 payload,也不要声称 diff 证明了任意代码执行。

源代码对比:漏洞与安全实现

该 diff 是 AOSP 在提交 d131540379d631d7dcb09b0169e4462bc65abcca 中公开的代码修改,并非重建的伪代码。os_zalloc(n) 将 n 作为字节数;os_calloc(count, size) 按指定元素数量和大小分配并清零。修复同时调整两个数组,并保留分配失败时的清理逻辑。

存在漏洞的实现
/* AOSP 修复前:num_active_scs 表示元素数量,
 * 但传给 os_zalloc 时被当作字节数。 */
scs_resp[0] = (int *) os_zalloc(num_active_scs);
if (!scs_resp[0])
    return;

scs_resp[1] = (int *) os_zalloc(num_active_scs);
if (!scs_resp[1]) {
    os_free(scs_resp[0]);
    return;
}
加固后的安全修复
/* AOSP 修复:为每个条目分配一个完整的 int。 */
scs_resp[0] = os_calloc(num_active_scs, sizeof(int));
if (!scs_resp[0])
    return;

scs_resp[1] = os_calloc(num_active_scs, sizeof(int));
if (!scs_resp[1]) {
    os_free(scs_resp[0]);
    return;
}

工程与系统安全加固清单

参考来源