FFmpeg中的二十一个零日漏洞
摘要
depthfirst的自主安全代理在FFmpeg中发现了21个零日漏洞,其中几个已潜伏了15到20年,概念验证演示了远程代码执行的可能性。这些发现凸显了AI驱动的安全代理在发现之前谷歌和Anthropic的深入分析都未能发现的严重漏洞方面的能力。
暂无内容
查看缓存全文
缓存时间:
2026/06/12 23:57
# FFmpeg 中的 21 个零日漏洞 | depthfirst
来源:https://depthfirst.com/research/21-zero-days-in-ffmpeg
**TLDR:depthfirst 的生产级自主安全代理在 FFmpeg 中发现了 21 个零日漏洞,而在此之前 Google 和 Anthropic 已进行了密集的安全分析。我们的代理不仅限于理论分析,还能生成具体、可复现的 PoC 输入来确认其发现,且成本仅为后者的一小部分(1000 美元 vs. 10000 美元)。其中一些漏洞已经潜伏了 15 到 20 年。我们探讨了这些问题的可利用性,并开发了一个 PoC,展示了远程代码执行(RCE)利用原语。**
FFmpeg 是世界上最广泛部署的软件之一。从我们日常使用的浏览器到支撑大型流媒体平台的基础设施,它默默地处理着各种媒体。作为一个经常解析复杂、不可信媒体的库,它在安全方面至关重要,也是零点击攻击的主要目标。
深入 FFmpeg 的仓库,挑战的真正规模便显现出来:它规模庞大,包含约 150 万行高度优化的 C 代码,专门用于解析数百种复杂的媒体格式。此外,它已经承受了超过二十年的持续模糊测试和手动审计。最近,Google 的 Big Sleep 团队披露了 FFmpeg 中的 13 个漏洞(https://goo.gle/bigsleep)。不久之后,Anthropic 使用他们的 Mythos 模型扫描了 FFmpeg,并成功发现了一些安全问题(https://red.anthropic.com/2026/mythos-preview/)。这些里程碑表明,先进模型越来越能够推理密集、加固的 C 代码。
随着这些近期的努力,在 FFmpeg 中寻找漏洞变得越来越困难。在 depthfirst,我们构建了一个能对大型代码库进行深度扫描的代理系统。在这里发现漏洞是对我们安全系统能力的衡量。虽然我们无法使用 Mythos,但我们想知道,仅凭我们现有的模型能走多远。我们能否重新发现 Big Sleep 和 Mythos 已找到的漏洞?更重要的是,我们能否找到他们完全错过的任何新的关键漏洞?
## depthfirst 的安全代理
编码代理和安全代理可能使用相同的基础模型,但它们的操作目标截然不同。编码代理通常是交互式的:人类给它一个任务,目标是编写代码,而不是关注边缘情况和对抗性输入。安全代理的目标则更窄、更具针对性。它不会试图编写有用的应用程序代码,而是试图在没有具体指令的情况下,在现有系统中发现真实、可利用的安全问题。
这改变了代理的形态。安全代理必须首先对代码库进行威胁建模:理解其架构,识别暴露的解析器和协议处理器,并映射攻击者控制的输入可以进入系统的位置。然后,它直接审计攻击面代码,跟踪相关组件的数据流,而不是将仓库视为文件的扁平集合。此外,一个实用的安全代理需要护栏,防止它编造缺失的条件、过度声称理论上的漏洞,或泛滥的误报。它必须检查攻击者是否真正控制了正确的输入,漏洞路径是否可达,以及可疑的缺陷是否可以复现。必要时,它应识别或生成适当的测试工具来与目标组件交互,并具体测试这些假设。
在 depthfirst,我们专门的安全代理深入分析代码,并行分支以测试各种假设。它们跟踪执行路径,验证攻击者是否控制了正确的输入,并确定数据流是否真正到达了易受攻击的接收点。关键在于,这个过程的结果不仅仅是理论报告或模糊的警告。系统会自动定位确切的安全问题,并提供可复现的具体输入,通过执行来确认漏洞。这确保了每个提交的发现都是真实的、可达的且可操作的。
## 发现
总的来说,我们的代理发现了 21 个零日漏洞,涵盖了从 TS 解复用器到 VP9 解码器的组件,总成本约为 ***1000 美元***(仅为 Anthropic 使用 Mythos 花费的 10%(https://red.anthropic.com/2026/mythos-preview/))。其中八个问题已被分配了 CVE 编号:
- **CVE-2026-39210(堆缓冲区溢出):** 于 2010 年引入 TS 解复用器,在读取两个字节之前缺少长度边界检查。
- **CVE-2026-39211(整数溢出):** 于 2010 年在 swscale 重构期间引入,通过一个没有上限的大小因子公式,允许用户控制的参数触发任意大的缩放。
- **CVE-2026-39212(栈溢出):** 2025 年 7 月在 `ffmpeg_opt.c` 中出现的一次近期回归,预设文件可能导致选项解析递归调用而无深度限制。
- **CVE-2026-39213(堆缓冲区溢出):** 于 2023 年在 yuv4mpegenc 的 rawvideo 输入路径中引入,未针对数据包大小验证尺寸。
- **CVE-2026-39214(栈缓冲区溢出):** 于 2003 年在原始 SDT 实现中引入,该 bug 在写入服务条目时不跟踪剩余空间。它潜伏了 23 年。
- **CVE-2026-39215(堆缓冲区溢出):** 于 2012 年在 `update_mb_info()` 中引入,一个逻辑错误允许后续调用写入分配缓冲区末尾之后的 12 个字节。
- **CVE-2026-39216(堆缓冲区溢出):** 于 2012 年在 `img2enc.c` 中引入,原因是将一个安全的色度大小替换为基于维度的无界大小。
- **CVE-2026-39217(堆缓冲区溢出):** 2025 年 3 月在 VP9 解码器中出现的一次近期回归,重构后的大小更新函数导致 tile 线程缓冲区缺少必要的重新分配。
- **CVE-2026-39218(堆缓冲区溢出):** 于 2017 年在 DASH 解复用器中引入,未能拒绝负的持续时间值,导致片段数组索引变为负数。
其余问题已修复,但我们尚未获得 CVE 标识符。这里我们使用内部跟踪 ID 引用它们:
- **DFVULN-127(堆缓冲区溢出):** 在 RTP AV1 解包器(`rtpdec_av1.c`)中,`av1_handle_packet()` 在跳过 Temporal Delimiter OBU 时会按 `obu_size` 推进输出写入位置,但不分配匹配的空间,因此下一个 OBU 会被写入远超缓冲区边界的位置。该缺陷自 2024 年首次添加 AV1 RTP 解包器后便存在。
- **DFVULN-126(堆缓冲区溢出):** 在 swscale 图代码(`graph.c`)中,`run_legacy_unscaled()` 处理隔行 YUV420P→NV12 转换时出错:`get_field()` 将平面行距加倍,导致 `ff_copyPlane` 的连续 memcpy 溢出目标 Y 平面 576 字节。于 2024 年随 swscale 新的动态缩放 API 引入。
- **DFVULN-125(栈缓冲区溢出):** 在 RTP JPEG 解包器(`rtpdec_jpeg.c`)中,`jpeg_create_header()` 在 1024 字节的栈缓冲区中构建量化表部分;精心构造的数据包中 `qtable_len >= 1024` 会完全填满该缓冲区,然后一个尾随的 `AV_WB16` 会写入缓冲区末尾之后的两个字节。这是 2012 年的一个回归:JPEG 解包器添加几天后,一次重构将受限制的表计数替换为无界的 `qtable_len / 64`,允许足够多的量化表来溢出固定缓冲区。
- **DFVULN-124(堆缓冲区溢出):** 在 AVIF 叠加路径(`ffmpeg_demux.c`)中,`istg_parse_tile_grid()` 未能拒绝带有零 tile 条目的 `dimg` 引用;无符号回绕导致对单字节堆分配进行越界读取。于 2025 年添加自动 HEIF tile 合并时引入。
- **DFVULN-123(整数溢出):** 在 RTP LATM 解包器(`rtpdec_latm.c`)中,`latm_parse_packet()` 执行有符号 32 位加法时溢出,绕过了其边界检查,使 `memcpy` 读取堆缓冲区末尾之外约 1 GB 的数据。自 2010 年添加 MP4A-LATM 解包器后便存在。
- **DFVULN-122(堆缓冲区溢出):** 在 RTP MPEG-4 解包器(`rtpdec_mpeg4.c`)中,`aac_parse_packet()` 接受 AU-headers-length 为 0,这会导致分配一个字节,然后将其作为四字节字段读取,而不检查是否存在任何 AU 头部。自 2005 年添加 MPEG4-AAC RTP 支持后便存在——这是最古老的漏洞,已潜伏超过二十年。
- **DFVULN-121(堆缓冲区下溢):** 在 CAF 解复用器(`cafdec.c`)中,`read_seek()` 直接使用 `av_index_search_timestamp()` 的返回值作为数组索引,而未检查是否为 -1;精心构造的文件会使所有索引时间戳为负,因此一个 seek 操作会索引到 `index_entries[-1]`。自 2009 年添加 CAF 解复用器后便存在。
- **DFVULN-120(整数下溢):** 在 AVI 解复用器(`avidec.c`)中,`ff_read_riff_info()` 被调用时使用 `size - 4`,而未验证 `size >= 4`;大小为 0 的 LIST 块会下溢到约 4 GB,绕过边界检查并触发约 2 GB 的分配(DoS)。于 2011 年泛化 RIFF INFO-tag 解析时引入,将一个有界调用替换为易下溢的 `size - 4`。
- **DFVULN-119(堆缓冲区溢出):** 在选项解析器(`ffmpeg_opt.c`)中,`opt_map()` 包含一个多余的增量,导致将链接标签错误解析为文件索引,并存储流索引为 -1;随后的负映射循环会读取 `AVStream**` 数组之前的内容。2025 年的一个回归,在流组匹配助手扩展了 `-map` 解析时引入。
- **DFVULN-118(堆缓冲区溢出):** 在 RTSP 服务器路径(`rtspdec.c`)中,`rtsp_read_announce()` 将负的 `Content-Length` 视为有效;一个远程的 `ANNOUNCE` 请求携带 `Content-Length: -1` 会导致在 `sdp[-1]` 处发生越界写入。2021 年的一个回归,移除了硬编码的 SDP 大小上限,同时也去掉了上界检查。
- **DFVULN-117(堆缓冲区溢出):** 在 RTMP 客户端(`rtmpproto.c`)中,`rtmp_calc_swfhash()` 检查的是 `in_size < 3` 而非 `in_size < 8`,允许 `memcpy` 从一个仅分配了 3 个字节的缓冲区读取 8 个字节。自 2012 年添加自动 SWFVerification 哈希后便存在。
- **DFVULN-116(堆缓冲区溢出):** 在 RTSP SDP 解析(`rtsp.c`)中,`sdp_parse_line()` 对空字符串计算 `strlen(control_url) - 1`,将 `size_t` 回绕为 `SIZE_MAX`,并产生一个字节的缓冲区前读取。自 2010 年添加 SDP control-URI 处理后便存在。
发现一个漏洞是一回事;证明其可被利用是另一回事。要真正理解我们系统的能力,我们需要看一个具体的漏洞以及它是如何被发现的。
## 从跳过的帧标记到 PC 控制
在 21 个发现中,有一个特别突出:FFmpeg 的 AV1 RTP 解包器(`libavformat/rtpdec_av1.c`)中的堆缓冲区溢出。它可以从网络到达,无需特殊标志。受害者只需运行 `ffmpeg -i rtsp://attacker/stream`,这是最普通的命令,而一个仅 183 字节的数据包就足以重定向执行。
要理解它,我们首先需要一些关于 AV1 视频如何通过 RTP 传输的背景知识。当 FFmpeg 拉取一个 RTSP 流时,服务器将编码的视频作为一系列 RTP 数据包传送。AV1 将其比特流组织成 *OBU*(开放比特流单元)。RTP 有效载荷格式将这些 OBU 分割到不同的数据包中,FFmpeg 的解包器负责将它们重新拼接成一个干净的基本流。一种特殊的 OBU 类型是 *Temporal Delimiter*(TD),这是一个微小的标记,用于分隔一个时间单元(帧)与下一个。规范明确告诉解包器要“忽略并移除”它在有效载荷中看到的任何 TD。
这个看似无害的“忽略并移除”正是问题所在,也是我们的代理所聚焦的精确点。
## 根本原因
解包器增量地构建其输出数据包。一个名为 `pktpos` 的光标跟踪下一个字节将被写入 `pkt->data` 的位置,它从数据包的当前末尾开始:
```
// libavformat/rtpdec_av1.c:199
pktpos = pkt->size;
```
当代码循环处理数据包中的 OBU 元素时,它实际发出的每个字节之前都有一个匹配的 `av_grow_packet` 调用,该调用会扩大堆分配的内存块 `pkt->data`。整个例程所依赖的不变性很简单:**`pktpos` 绝不能超过 `pkt->data` 的已分配大小。** Temporal Delimiter 的处理打破了这一不变性:
```
// libavformat/rtpdec_av1.c:250
if ((obu_type == AV1_OBU_TEMPORAL_DELIMITER) ||
(obu_type == AV1_OBU_TILE_LIST)) {
pktpos += obu_size; // 推进输出光标...
rem_pkt_size -= obu_size; // ...以及输入计数器
obu_cnt++;
continue; // 但从未分配内存,也从未推进 buf_ptr
}
```
当一个 TD 被跳过时,`pktpos` 被攻击者声明的 `obu_size` 向前推进,然而**并没有分配任何内存**来支持这个推进。更糟糕的是,输入指针 `buf_ptr` **没有**越过 TD 的字节。这个单一的 `continue` 导致了两个不同的问题:
1. **写入光标现在被污染了。** 在跳过了一个 `obu_size = 148` 的 TD 后,`pktpos` 等于 148,但 `pkt->data` 仍然是未分配的(或者远小于 148 字节)。
2. **攻击者控制着写入那里的内容。** 由于 `buf_ptr` 从未前进,下一个循环迭代会重新解析 TD 自身的字节——其头部字节被重新读取为一个新的 OBU 长度,而其有效载荷成为那个伪造的 OBU 的内容。最终将落在被污染偏移处的数据完全由攻击者提供。
在下一次迭代中,循环到达一个正常的 OBU,并根据该 OBU 的大小增长数据包:
```
// libavformat/rtpdec_av1.c:296
if ((result = av_grow_packet(pkt, output_size)) < 0)
return result;
...
// libavformat/rtpdec_av1.c:304 / 336 — 写入从 pkt->data[pktpos] 开始
pkt->data[pktpos++] = *buf_ptr++ | AV1F_OBU_HAS_SIZE_FIELD;
...
memcpy(pkt->data + pktpos, buf_ptr, obu_payload_size);
```
对于一个伪造的 17 字节 OBU,`av_grow_packet` 分配了一个 81 字节的缓冲区(17 字节加上 FFmpeg 的 64 字节输入填充)。但写入从 `pkt->data[148]` 开始,这比分配的内存末尾还多出 67 字节。这是一个具有完全受控偏移量**和**完全受控内容的堆缓冲区溢出,这几乎是内存破坏漏洞所能提供的最强原语。
## 利用
只有缓冲区之后存在值得破坏的东西时,受控的溢出才有用。在这里,FFmpeg 自己的分配器为我们提供了一个完美的目标。
当 `av_grow_packet` 分配数据包的数据缓冲区时,它会通过 `av_buffer_alloc`,该函数依次执行三个堆分配:数据缓冲区本身、一个 `AVBuffer` 记账结构体,以及一个 `AVBufferRef`。由于 FFmpeg 通过 `posix_memalign` 以 64 字节对齐分配所有内容,我们的 81 字节数据缓冲区占据一个 128 字节的块,而 `AVBuffer` 结构体紧接着它落下。该结构体包含一个函数指针:
```
// libavutil/buffer_internal.h
struct AVBuffer {
uint8_t *data; // +0
size_t size; // +8
atomic_uint refcount; // +16 (4字节 + 4填充)
void (*free)(void *opaque, uint8_t *data); // +24 ← 目标
void *opaque; // +32
...
};
```
从数据缓冲区起始处开始计算,`AVBuffer.free` 指针位于偏移量 **152**。这是 FFmpeg 为释放缓冲区内存而调用的回调函数——这正是我们要用溢出瞄准的精确点。
这个计算是经过精心调整的。当 TD 的 `obu_size = 148` 时,写入从 `pkt->data[148]` 开始。TD 的头部字节 `0x10` 被重新解释为长度 16,从而产生一个伪造的 16 字节 OBU,其头部和有效载荷从偏移量 148 开始被写入:
```
// libavutil/buffer_internal.h
相似文章
Reddit r/AI_Agents
来自depthfirst的自主AI代理在FFmpeg中发现了21个零日漏洞,其中包括一个可通过单个183字节数据包实现的网络可达远程代码执行漏洞,仅花费1000美元的计算成本;这一发现凸显了自动化漏洞发现与漏洞修复之间的差距。
Lobsters Hottest
Mozilla 利用 Anthropic 的 Claude Mythos Preview AI,在 Firefox 150 中找出并修复了 271 个零日漏洞,标志着网络安全格局的重大转变:AI 让防御者首次能够彻底甩开攻击者。
Hacker News Top
一个匿名GitHub账户发布了一大批针对多个流行软件包中未公开零日漏洞的概念验证利用代码,涉及软件包括7zip、Docker、Firefox、FFmpeg、Ghidra、libssh2、Nmap、PHP和VLC。
Anthropic Research
Anthropic的Frontier Red Team评估了大型语言模型如何加速N-day漏洞的利用,发现Claude Mythos Preview能够自主构建针对18个Firefox补丁中的8个以及21个Windows内核补丁中的8个的有效漏洞利用,强调了补丁间隙期间威胁的增加。
Hacker News Top
本文详细介绍了AI代理在Epsilon(一个用Go编写的小型WASM运行时)中发现的超过20个安全漏洞,其中包括多个沙箱逃逸漏洞,允许恶意模块突破隔离。