FatFs 漏洞导致一张 SD 卡控制数百万嵌入式设备?

作者:championsky | 发布时间: | 更新时间:


一张 SD 卡就能接管设备?FatFs 漏洞到底是怎么回事

最近嵌入式安全圈有个很值得关注的漏洞事件:

FatFs 被曝出 7 个漏洞,其中最严重的一个可能让攻击者通过一张恶意 SD 卡接管设备。

听起来有点离谱。

一张卡,怎么就能控制设备?

这篇我用大白话讲清楚。


先说 FatFs 是什么

FatFs 不是一个产品,也不是一个 App。

它是一段很小的文件系统代码。

它的作用很简单:

让嵌入式设备能读写 SD 卡、U 盘里的 FAT / exFAT 文件系统。

你可以把它理解成:

设备插入 SD 卡后,系统需要有人帮它看懂:

这张卡里有哪些文件? 文件叫什么名字? 文件有多大? 文件内容从哪里开始? 目录在哪里?

FatFs 就是干这个活的。


runZero 研究人员披露了 7 个 FatFs 相关 CVE,影响包括 Espressif ESP-IDF、STM32Cube middleware、Zephyr RTOS、MicroPython、ArduPilot、RT-Thread、Mbed、Samsung TizenRT、SWUpdate 等生态。

为什么它影响这么大?

因为 FatFs 太好用了。

它免费。 体积小。 容易移植。 适合小芯片。 很多 RTOS 和 MCU 项目都能直接塞进去。

所以大量设备直接把 FatFs 复制进固件里:

无人机。 相机。 3D 打印机。 开发板。 智能插座。 工业控制器。 硬件钱包。 ESP32 设备。 STM32 项目。 MicroPython。 ArduPilot。

问题就在这里。

如果一个普通 App 有 bug,影响的是一个 App。

但如果 FatFs 这种基础组件有 bug,影响的是成千上万个把它复制进去的设备。

这就是典型的供应链风险。


一张 SD 卡为什么能攻击设备?

正常人看 SD 卡,会觉得它只是存文件的。

但对设备来说,SD 卡不是“文件”。

SD 卡首先是一堆原始数据。

设备插进去以后,会先解析它的文件系统结构。

也就是说,设备要相信卡上写的一堆元数据:

这个分区在哪里。 FAT 表在哪里。 数据区从哪里开始。 目录有多少项。 文件名多长。 文件大小是多少。

这些信息都写在卡里。



如果攻击者故意做一张“坏卡”,里面的文件系统结构不是正常的,而是精心构造的,设备一插上就会自动解析。

漏洞就可能在解析这一刻触发。

所以它不是“打开某个文件才中招”。

很多设备是:

插卡 → 自动挂载 → 自动解析 → 漏洞触发。

这就是为什么物理攻击场景里,一张卡就可能足够。


七个 CVE 编号,记录了 FatFs 项目中的多个漏洞,其 CVSS 等级从中等到高不等


研究结果按重要性排序

本文记录的七项发现大致按攻击者主观价值排序。

mount_volume() 函数中出现 FAT32 整数溢出

(CVSS 7.6,高危) ——这是主要问题:CVE-206-6682描述了核心挂载运算中的整数溢出漏洞,攻击者可以生成可控的文件大小元数据,下游代码可能将其误认为是读取长度。在实际系统中,这可能导致堆/栈溢出并执行代码。就实际攻击者价值和传递影响而言,这是最严重的漏洞之一。

链接:

此处内容已隐藏,请评论后刷新页面查看.

f_getlabel() 中 exFAT 标签长度堆栈溢出

CVSS 7.6,高危)CVE-2026-6687描述了 exFAT 标签长度字段未充分限制的情况,导致调用者提供的标签缓冲区被写入过大的数据。当典型示例和生成的代码使用较小的堆栈缓冲区时,这个问题尤为严重。这是一种直接的内存损坏漏洞,在许多固件项目中都有暴露,尤其是在启用了 exFAT 的情况下。

链接:

此处内容已隐藏,请评论后刷新页面查看.

下游调用者中出现过长文件名溢出

(CVSS 7.6,高危)  ——启用长文件名 (LFN) 后,fno.fname 可能远大于许多调用方缓冲区的预期大小,如CVE-2026-6688所述。此类漏洞反复出现在集成函数中(例如 strcpy、sprintf 以及固定大小的名称/路径字段)。直接在 FatFs 中完全修复此漏洞较为棘手(因为利用此漏洞依赖于包装器将长文件名复制到容量不足的本地缓冲区),但可以通过修改 FatFs 来缓解此问题,例如使文件名长度以及截断/验证结果对调用方更加明确。

链接:

此处内容已隐藏,请评论后刷新页面查看.

无符号减法在脏缓存处理中的包装 

(CVSS 6.1,中等)  –  CVE-2026-6685描述了在碎片化卷上,算术回绕可能导致读/写路径中出现过时的脏缓存行为和越界内存效应的情况。这可能表现为静默损坏,而这正是运维人员最痛恨的漏洞类型:难以检测、容易误诊,并且在控制/数据记录工作负载中非常危险。

链接:

此处内容已隐藏,请评论后刷新页面查看.

exFAT 同步/写入路径中的除零错误

(CVSS 4.6,中等)CVE-2026-6683记录了 exFAT 同步/写入流程中一个除以零的漏洞,该漏洞可被精心构造的介质触发,并导致可靠的系统崩溃。在更新环境中,这可能成为一种有效的系统崩溃途径。虽然它通常更偏向于“拒绝服务攻击 (DoS)”而非“远程代码执行 (RCE)”,但考虑到 OTA 更新的影响,它仍然对系统可用性构成重大威胁。

链接:

https://www.cve.org/cverecord?id=CVE-2026-6683

超出 EOF 范围后未初始化的簇暴露

(CVSS 4.6,中等)CVE-2026-6686描述了一种未初始化集群暴露路径​​,其中文件扩展超过文件末尾 (EOF) 可能导致泄露先前已删除内容中的过期数据。这是一个信息泄露漏洞,在多阶段启动/更新和共享介质环境中会造成实际后果。

链接:

此处内容已隐藏,请评论后刷新页面查看.

GPT分区扫描循环拒绝服务攻击(R0.16之前版本)

(CVSS 4.6,中等)CVE-2026-6684涉及 GPT 条目计数滥用,可触发旧版本目录树中几乎无限的扫描。这会在受影响的 R0.16 之前的实现中造成严重的挂载时拒绝服务攻击 (DoS),尤其对启动路径影响巨大。但由于上游 R0.16 添加了 GPT 保护性验证,因此该漏洞在此排名靠后。现在,升级取决于实现者。

链接:

此处内容已隐藏,请评论后刷新页面查看.

最严重的漏洞:CVE-2026-6682

这次最严重的问题,是 FatFs 在挂载 FAT32 文件系统时的一个整数溢出。

听起来很抽象,我翻译成人话。

设备插入 SD 卡后,FatFs 要计算:

真正的数据区从哪里开始?

因为 FAT 文件系统前面有启动扇区、FAT 表、目录区,真正的文件内容在后面。

FatFs 里面有一行关键计算:

fasize *= fs->n_fats;

意思大概是:

FAT 表大小 × FAT 表数量 = FAT 区总大小

然后再根据这个结果计算数据区位置。

问题是,这两个数字都来自 SD 卡上的元数据。

换句话说:

攻击者可以控制这两个数字。



什么是整数溢出?

举个最简单的例子。

假设一个水杯最多只能装 100 毫升水。

你倒进去 120 毫升,它不会真的装下 120 毫升。

多出来的会溢出去。

计算机里的整数也一样。

每种整数类型都有最大值。

如果计算结果超过最大值,就可能发生回绕。

比如原文里的例子:

fasize = 0x80000001;
fasize *= 2;

数学上,这应该是一个很大的数字。

但在固定长度整数里,它可能回绕成一个很小的数。

这就叫整数溢出。


整数溢出为什么危险?

因为 FatFs 会拿这个错误结果继续往下算。

原本数据区应该在很后面。

但因为整数溢出,FatFs 算错了,以为数据区在另一个位置。

攻击者就可以提前在那个位置放好伪造的目录项、伪造的文件信息、伪造的文件大小。

然后 FatFs 会认真地相信这些假数据。

最危险的地方来了。

如果 FatFs 读到了一个假的巨大文件大小,下游程序又直接拿这个大小去读文件:

f_read(fp, buf, finfo.fsize, &br);

这里的意思是:

把文件读进 buf 这个缓冲区里,读取大小是 finfo.fsize。

如果 finfo.fsize 是攻击者伪造出来的巨大数字,而 buf 实际很小,就会发生缓冲区溢出。


什么是缓冲区溢出?

再用人话讲。

缓冲区就像一个盒子。

程序说:

这个盒子只能装 100 个字节。

但攻击者骗程序说:

这个文件有 5000 个字节,你都塞进去吧。

程序如果没检查,就真的往 100 字节的盒子里塞 5000 字节。

后面 4900 字节就会冲出去,覆盖旁边的内存。

而旁边的内存里可能有什么?

可能有变量。 可能有函数指针。 可能有返回地址。 可能有程序控制流程。

一旦攻击者能控制覆盖内容,就可能让设备执行攻击者准备好的代码。

这就是从“文件系统解析 bug”变成“设备被接管”的关键路径。


这条攻击链可以这样理解

整个过程大概是:

  1. 攻击者制作一张恶意 SD 卡

  2. 卡里的 FAT32 元数据被故意构造

  3. FatFs 插卡挂载时读取这些元数据

  4. 关键乘法发生整数溢出

  5. FatFs 算错数据区位置

  6. 攻击者在错误位置放入假文件条目

  7. FatFs 读到假的巨大文件大小

  8. 设备程序信任这个大小

  9. 小缓冲区被写爆

  10. 攻击者获得代码执行机会

一句话:

攻击者不是直接攻击程序,而是先骗文件系统,让文件系统帮他骗程序。


为什么小型设备更容易中招?

因为很多嵌入式设备防护很弱。

普通电脑和手机通常有:

ASLR。 内存保护。 权限隔离。 沙箱。 栈保护。 MMU。

这些东西可以提高利用难度。

但很多小芯片没有。

比如一些 MCU、RTOS 设备:

内存地址固定。 没有完整 MMU。 没有 ASLR。 没有强隔离。 程序和数据挤在一起。 崩溃日志也不完整。

所以同样一个缓冲区溢出,在 PC 上可能只是崩溃,在小设备上就可能变成稳定接管。

这就是嵌入式安全最恐怖的地方。

不是 bug 多高级。

而是环境太脆。


其他几个漏洞也很典型

这次不是只有一个洞,而是一组问题。

1. 磁盘标签太长导致溢出

SD 卡可以有卷标,也就是磁盘名字。

比如:

MY_SD_CARD

如果攻击者故意构造一个特别长的卷标,而程序用一个很小的缓冲区接收,就可能溢出。

这类 bug 很常见:

程序员以为名字不会太长。 攻击者偏偏给你一个超长名字。 程序没检查长度。 内存被写爆。


2. 文件名太长导致溢出

FAT/exFAT 支持长文件名。

有些系统示例代码却只准备了很小的缓冲区。

比如某些场景里缓冲区可能只有十几个字节。

如果攻击者构造一个超长文件名:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bin

程序如果直接复制进去,也会溢出。

这类问题的本质是:

上游库允许长文件名,下游代码却按短文件名处理。


3. 碎片化卡导致静默数据破坏

这个更阴险。

不是立刻崩溃。

而是数据悄悄错了。

比如日志写错。 配置读错。 文件内容被破坏。 设备行为异常但查不出原因。

这种 bug 对工业设备特别麻烦。

因为设备不一定死机,而是继续运行,但数据已经不可信。


4. 已删除数据可能泄露

有些漏洞可能让设备读到本来不该读到的旧数据。

比如已经删除过的文件残留。

这类问题不一定能接管设备,但可能泄露敏感信息:

配置文件。 日志。 密钥片段。 历史数据。 用户文件。


5. 更新过程除零导致砖机

还有一种是除零崩溃。

除零就是程序做了类似:

x / 0

这在程序里通常会直接崩。

如果这个问题发生在固件更新过程中,设备可能更新失败、卡死,甚至变砖。

这类漏洞不一定是接管设备,但可以造成拒绝服务。

在工业环境里,拒绝服务本身就很严重。


攻击方式一:物理攻击

这是最直观的。

攻击者准备一张恶意 SD 卡。

然后找机会插到设备里。

设备自动挂载。

漏洞触发。

这种场景适合哪些设备?

相机。 3D 打印机。 无人机。 工控终端。 公共终端。 ATM。 投票机。 硬件钱包。 带 SD 卡槽的 IoT 设备。

只要设备会自动读取外部存储,就有攻击面。

所以安全里有一句话:

物理访问往往等于高风险。


攻击方式二:远程更新链路

很多人以为这类漏洞必须插卡。

不一定。

如果设备从网络下载一个 FAT 镜像,或者下载固件包后交给 FatFs 解析,那攻击面就可能变成远程。

典型危险流程是:

设备通过 HTTP 下载更新包。 更新包没有先验签。 设备直接把镜像交给 FatFs 处理。 攻击者在中间人位置替换镜像。 设备解析恶意文件系统。 漏洞触发。

这就从“一张恶意 SD 卡”变成了“一个恶意更新镜像”。

所以固件更新必须先验证签名,再解析内容。

顺序不能反。


为什么这是供应链问题?

因为 FatFs 不是一个设备自己的代码。

它是一个基础组件。

很多项目直接复制:

ff.c
ff.h

然后一用就是几年。

这会带来一个问题:

上游修了,下游不一定知道。 下游知道了,也不一定升级。 升级了,也不一定所有设备都能 OTA。 有些设备已经卖出去几年,根本没人维护。

这就是嵌入式供应链的老毛病。

一个小库出问题,可能散落在大量产品里。

你甚至不知道自己家里的设备是不是用了它。


AI 是怎么参与漏洞发现的?

这次还有一个非常值得注意的点:

研究人员以前人工审计过 FatFs,也做过 fuzzing,但只发现了一些小问题。

后来他们用 AI 编码助手生成模糊测试工具,重新测试 FatFs,找到了更危险的漏洞。

这里要解释一下什么是 fuzzing。

Fuzzing 叫模糊测试。

简单说,就是疯狂给程序喂各种奇怪输入,看它会不会崩。

比如对 FatFs 来说,就是自动生成各种奇怪的 FAT 镜像:

字段超大。 字段为 0。 长度不一致。 目录项乱写。 文件名特别长。 分区表异常。

然后看 FatFs 会不会崩溃、越界、死循环。

AI 在这里的作用不是“魔法发现漏洞”。

它更像一个加速器:

帮你写测试框架。 帮你生成变异逻辑。 帮你补 harness。 帮你处理编译报错。 帮你快速迭代。

这意味着未来很多老旧 C 代码都会被重新翻出来审计。

以前没人愿意花时间看。

现在 AI + fuzzing 可以把成本打下来。


对开发者的建议

如果你是嵌入式开发者,先做几件事:

  1. 确认项目有没有用 FatFs

  2. 确认 FatFs 版本

  3. 尽量升级到 R0.16 或更新版本

  4. 不要直接信任 SD 卡里的文件大小

  5. 限制文件名长度

  6. 限制卷标长度

  7. 固件更新先验签,再解析

  8. 外部介质不要默认自动执行逻辑

  9. f_read() 这类调用做长度检查

  10. 尽量启用栈保护、MPU、内存边界检查

尤其是这种代码:

f_read(fp, buf, finfo.fsize, &br);

一定要小心。

正确思路应该是:

read_size = min(finfo.fsize, sizeof(buf));

也就是说:

文件说自己多大不重要。

你的缓冲区有多大才重要。


对普通用户的建议

普通用户不用恐慌,但要知道几个常识:

不要乱插陌生 SD 卡。 不要乱插陌生 U 盘。 硬件钱包只用官方建议的存储介质。 公共设备不要随便接入外部存储。 重要设备不要让陌生人短暂接触。 设备能更新就更新固件。

尤其是硬件钱包、无人机、工控设备、3D 打印机、相机这类会自动读卡的设备,要更谨慎。


这件事真正教会我们什么?

这次 FatFs 漏洞不是一个孤立事件。

它讲的是嵌入式安全里非常基础但经常被忽视的问题:

设备会相信外部输入。

SD 卡是外部输入。 U 盘是外部输入。 固件镜像是外部输入。 文件名是外部输入。 文件大小是外部输入。 分区表也是外部输入。

只要它来自攻击者能控制的地方,就不能直接信任。

这是安全开发第一课。


一句话总结

FatFs 这组漏洞最吓人的地方,不是“一张 SD 卡很神奇”。

而是:

大量嵌入式设备在插卡瞬间,就会自动相信卡里的文件系统元数据。

攻击者只要把这些元数据设计得足够畸形,就可能让设备算错位置、读错大小、写爆内存,最后把一张普通 SD 卡变成设备接管入口。

这就是黑客攻击里最经典的一句话:

你以为你在读取文件,其实你在解析攻击者给你的输入。

真正的安全,不是只防网络。

所有输入,都要防。