Un ventilateur qui s’emballe au salon : le mineur secret tourne en tâche de fond
Dans le coin d’un salon silencieux, les ventilateurs d’un boîtier multimédia d’ordinaire discret se sont mis à tourner à plein régime toute la nuit. L’ordinateur ne servait d’ordinaire qu’à diffuser des films en 4K avec une charge processeur minimale. En consultant le tableau de bord, son propriétaire a découvert que le CPU était bloqué à 100 % d’utilisation depuis plusieurs jours.
Deux processus inconnus consommaient sans répit la puissance de la machine : l’un se faisait passer pour un proxy réseau sous le nom myawesomeproxy, tandis que l’autre minait activement de la cryptomonnaie sous l’étiquette mylovelyminer. L’administrateur n’avait pourtant installé aucun logiciel suspect et l’interface de gestion était protégée par un mot de passe robuste et unique.
L’origine de l’intrusion se situait au cœur de Plex Media Server. Des chercheurs en sécurité ont dévoilé une chaîne d’exécution de code à distance non authentifiée baptisée plex2shell (CVE-2026-96652, -96654, -96655, -96656), touchant la version 1.43.2 et les versions antérieures. Un attaquant sur internet pouvait prendre le contrôle total de la machine sans aucun identifiant, au moyen d’une série de requêtes HTTP spécialement forgées.
Figure : Journal de honeypot montrant la récupération automatisée de charges malveillantes par un script d’attaque. Source : BoredHackerBlog.
Tromper le serveur pour qu’il s’attaque lui-même via un rappel non validé
Nombre de services locaux reposent sur un postulat tacite : les requêtes provenant de localhost sont fiables par définition. Afin d’alléger la charge de contrôle interne, Plex appliquait des règles de filtrage très permissives pour le trafic en boucle locale. Cette confiance implicite est devenue la faille motrice de l’attaque : il suffisait de pousser le serveur à s’attaquer lui-même pour faire tomber ses défenses.
La première brèche se situait dans l’interface de suivi /player/timeline, destinée à remonter la progression de lecture. Le protocole prévoyait la présence d’un en-tête X-Plex-Token, mais le code serveur se contentait d’appeler la méthode hasParam(). Celle-ci ne vérifiait que l’existence formelle de l’en-tête sans en inspecter la validité ; une chaîne arbitraire suffisait à franchir l’étape.
Le contrôle passé, le serveur renvoie les notifications d’abonnement aux événements vers l’adresse du lecteur. Le code assemblait l’URL de rappel via boost::format("%1%://%2%:%3%"). Or, le paramètre protocol ne faisait l’objet d’aucune validation de format ni d’aucun filtrage.
L’attaquant a inséré dans ce paramètre une URL complète ciblant l’adresse de bouclage. Plex a immédiatement émis une requête HTTP POST vers son propre port d’administration interne. Au niveau de la couche réseau, l’adresse source est devenue 127.0.0.1, conférant à l’attaquant le statut privilégié d’émetteur local de confiance.
Un test de préfixe défaillant livre les jetons d’administration
Sous cette identité locale, l’attaquant s’est tourné vers l’API interne de gestion des extensions /system/agents/*. Détectant une origine locale, l’interface désactivait l’authentification. Dans le script agentservice.py, une liste d’autorisation avait bien été mise en place pour la fonction searchOne.
Toutefois, cette fonction utilisait le décorateur de synchronisation @lock. Ce décorateur omettait de propager la valeur de retour de la fonction sous-jacente inner(). Lorsqu’une règle d’exclusion déclenchait une sortie anticipée pour bloquer l’accès, le résultat était purement ignoré, rendant le mécanisme de protection totalement inopérant.
Parallèlement, le paramètre identifier manquait de nettoyage : un point d’interrogation ? permettait d’injecter des paramètres de requête, tandis qu’un dièse # tronquait le chemin /messaging/... par défaut. La deuxième pièce du puzzle était en place.
L’attaquant a ensuite visé les privilèges administrateur généraux. Le framework de plugins de Plex intégrait une fonction pratique : toute requête sortante vers une adresse commençant par 127.0.0.1 recevait automatiquement les jetons administratifs local-*. Cependant, le framework se basait sur une simple comparaison de début de chaîne sans délimiter le nom d’hôte. Une cible comme 127.0.0.1.evil.com validait le test.
Grâce au service de DNS générique sslip.io, l’attaquant a formé le domaine 127.0.0.1.10-0-0-9.sslip.io. Ce nom respectait la règle de préfixe tout en dirigeant le trafic, via la résolution DNS publique, vers le serveur de l’attaquant à l’adresse 10.0.0.9. Plex a validé la règle et transmis spontanément le jeton administrateur, scellant la troisième faiblesse.
Figure : Journal en temps réel enregistré par des chercheurs lors des scans et tentatives d’exploitation depuis des adresses IP malveillantes. Source : BoredHackerBlog.
Du code malveillant dissimulé dans les pixels : détournement du transcodeur
Muni du jeton administrateur, l’attaquant pouvait modifier les préférences du serveur. Plex exploite un transcodeur interne pour adapter les flux vidéo aux différents appareils. Le composant x264 sous-jacent disposait d’options de débogage dans lesquelles l’attaquant a injecté : TranscoderH264Options=dump-yuv=<chemin>:qp=0.
Le paramètre dump-yuv fait appel en interne à fopen(chemin, "w") et enregistre directement les images brutes décodées dans le fichier spécifié. En fixant le paramètre de quantification qp=0, la compression sans perte est activée, préservant exactement la valeur de chaque pixel. L’attaquant avait préparé en amont une bibliothèque partagée ELF minimale (.so) contenant un constructeur d’initialisation système.
Les octets binaires de cette bibliothèque ont été insérés un à un dans le plan de luminance (plan Y) de la toute première image d’une vidéo MP4. Le fichier obtenu formait une vidéo H.264 tout à fait lisible et d’apparence normale.
L’attaquant a ensuite lancé une requête de transcodage avec son jeton administrateur en faisant pointer le paramètre path vers une liste de lecture item.xml distante. Plex a rapatrié la vidéo hôte et lancé le transcodage. Dès le décodage de la première image, la routine de débogage s’est déclenchée. La bibliothèque malveillante dissimulée au cœur des pixels a été écrite par le moteur de transcodage directement dans le répertoire système Codecs.
Absence de signatures numériques : exécution automatique et prise de contrôle
Déposer un fichier sur le disque ne suffit pas ; le faire charger par le système constitue l’objectif final. Le composant libavutil.so.58 intégré à Plex appelle la fonction avpriv_load_new_libs à chaque démarrage du transcodeur. Cette routine parcourt le dossier Codecs et appelle dlopen(path, RTLD_NOW) pour chaque fichier portant l’extension .so.
Ce processus omettait les vérifications élémentaires : aucun préfixe de nom n’était imposé, les droits d’exécution n’étaient pas contrôlés et aucune vérification de signature numérique n’était mise en œuvre. Même avec de simples permissions de lecture, la bibliothèque était immédiatement chargée en mémoire.
Conformément au standard ELF, dlopen exécute automatiquement les constructeurs définis dans DT_INIT_ARRAY avant toute résolution de symbole. Ce constructeur exécutait fork() et execve(), ouvrant instantanément un shell inversé avec les droits du service sous uid=1000(plex). La chaîne d’exploitation était bouclée.
Les modèles d’IA abaissent le coût de découverte des failles en périphérie
Le calendrier allant de la découverte à la publication a été particulièrement serré : signalée le 27 juillet 2026, la vulnérabilité a été validée le 4 août avec versement d’une prime par l’équipe Plex. Quatre identifiants CVE ont été attribués le 23 septembre, avant la parution des correctifs et des détails techniques le 16 octobre.
Dès la divulgation, des exploitations actives ont surgi sur le réseau. Des honeypots ont intercepté des balayages automatisés depuis les adresses 45.194.92.222, 91.92.40.202 et 176.65.139.228. Les charges virales étaient diffusées depuis le serveur 217.60.194.116.
Les spécimens analysés comprenaient une image conteneur d’empreinte 5712ef510481f15fe8b18fdd10b73026d57cb3d9d151878a692358b99a464441 et un binaire compressé avec UPX portant le hash 68938fb428f2bcfef4ede62f7b29cd0f15f37ed59e94462c099f2ca64e36baa7. Après décompression, le hash du binaire principal était b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49. Les pirates tiraient parti du fonctionnement continu des serveurs domestiques pour relayer du trafic et miner discrètement des cryptomonnaies.
La méthode mise en œuvre pour identifier cette faille retient également l’attention. L’auteur a détaillé un protocole de recherche assisté par de grands modèles linguistiques (LLM). L’étude a été divisée en reconnaissance, sélection, validation et assemblage de la chaîne, des agents autonomes étant chargés de réfuter systématiquement chaque hypothèse.
L’analyste humain conservait le pilotage stratégique de l’architecture et de l’exploitation, tandis que l’IA absorbait le travail fastidieux d’analyse du code. Le coût pour relier plusieurs défauts mineurs en une chaîne critique a chuté de manière spectaculaire, exposant en première ligne les appareils grand public situés en bordure de réseau.
Cette attaque ne reposait sur aucune corruption de mémoire complexe. Un en-tête non vérifié, un retour de fonction étouffé par un décorateur, une vérification de préfixe trop lâche et un chargeur de bibliothèques aveugle : chacun de ces défauts n’était qu’une simple inattention de développement.
L’hypothèse selon laquelle les flux locaux seraient dignes de confiance a articulé ces négligences en une porte dérobée sans authentification. Les exceptions conçues pour simplifier l’usage se sont révélées être le chemin le plus direct vers la compromission. Face aux scans automatisés déjà en cours, la mise à jour vers la version 1.43.3 ou ultérieure est indispensable pour empêcher ces serveurs de se transformer en mineurs clandestins.
Liens utiles :
- Rapport sur la vulnérabilité plex2shell sur zmain.info
- Analyse des attaques actives sur BoredHackerBlog
- Référentiel GitHub code-zm/plex2shell
- Référentiel GitHub BoredHackerBlog/plex2shell_pcap
- Discussion sur Hacker News (item?id=50152321)