L'union fait la force. Un duo de chercheurs en sécurité a réussi à infiltrer l'infrastructure de développement de PyTorch en utilisant des techniques qui exploitent des configurations non sécurisées dans les flux de travail Github Actions. Leur attaque a été divulguée dans le cadre d'une démarche de hacking éthique au développeur principal de PyTorch, Meta AI, mais d'autres sociétés de développement de logiciels utilisant GitHub Actions ont probablement fait les mêmes erreurs, s'exposant potentiellement à des attaques sur leur cycle de développement logiciel. « Notre méthode d'exploitation a permis de télécharger des versions malveillantes de PyTorch sur GitHub, de télécharger des versions sur AWS, d'ajouter potentiellement du code à la branche principale du dépôt, de créer des portes dérobées dans les dépendances de PyTorch - la liste est longue », a déclaré le chercheur en sécurité John Stawinski dans un article détaillé sur l'attaque publié sur son blog personnel. « En bref, c'était mauvais. Très mauvais ».

John Stawinski a conçu l'attaque avec son collègue Adnan Khan. Tous deux travaillent comme ingénieurs en sécurité pour la société de cybersécurité Praetorian et, l'été dernier, ils ont commencé à étudier et à développer une classe d'exploits inédite pour les plateformes d'intégration et de livraison continues (CI/CD). L'une de leurs premières cibles a été GitHub Actions, un service CI/CD pour automatiser la conception et le test du code logiciel en définissant des workflows s'exécutant automatiquement à l'intérieur de conteneurs sur l'infrastructure GitHub ou sur celle de l'utilisateur. Adnan Khan a initialement trouvé une vulnérabilité critique qui aurait pu conduire à l'empoisonnement des images officielles des runners du service Actions. Les « runners » sont les machines virtuelles qui exécutent les actions de construction définies dans les flux de travail de GitHub Actions. Après avoir signalé cette faille à la filiale de Microsoft et reçu une prime de 20 000 $, Adnan Khan s'est rendu compte que le problème principal qu'il avait découvert était systémique et que des milliers d'autres dépôts étaient probablement concernés.

L'exécution risquée d'actions GitHub auto-hébergées

Depuis, Adnan Khan et John Stawinski ont trouvé des vulnérabilités dans les référentiels logiciels et l'infrastructure de développement de grandes entreprises et de projets logiciels et ont récolté des centaines de milliers de dollars de récompenses dans le cadre de programmes de bug bounty. Parmi leurs « victimes » figurent Microsoft Deepspeed, une application Cloudflare, la bibliothèque d'apprentissage automatique TensorFlow, les portefeuilles de crypto-monnaie et les nœuds de plusieurs blockchains, ainsi que PyTorch, l'un des frameworks d'apprentissage automatique open source les plus utilisés. Il a été développé à l'origine par Meta AI, une filiale de Meta (anciennement Facebook), mais son développement est désormais régi par la Fondation PyTorch, une société indépendante agissant sous l'égide de la Fondation Linux. « Nous avons commencé par découvrir toutes les nuances de l'exploitation d'Actions, en exécutant des outils, des tactiques et des procédures (TTP) qui n'avaient jamais été vus auparavant dans la nature », a expliqué John Stawinski dans un billet de blog au début du mois. « Au fur et à mesure que nos recherches avançaient, nous avons fait évoluer nos TTP pour attaquer plusieurs plateformes CI/CD, notamment GitHub Actions, Buildkite, Jenkins et CircleCI ».

GitHub propose différents types de « runners » préconfigurés (Windows, Linux et macOS) s'exécutant directement sur l'infrastructure de GitHub et utilisés pour tester et créer des applications pour ces systèmes d'exploitation. Cependant, les utilisateurs ont également la possibilité de déployer l'agent de conception Actions sur leurs propres infrastructures et de le lier à leur entreprise et à leurs dépôts GitHub. Ces solutions sont connues sous le nom de runners auto-hébergés et apportent plusieurs avantages tels que l'exécution de différentes versions de systèmes d'exploitation et de combinaisons de matériel, ainsi que des outils logiciels supplémentaires que les runners hébergés de GitHub ne fournissent pas. Compte tenu de cette flexibilité, il n'est pas surprenant que de nombreux projets et sociétés choisissent de déployer des runners auto-hébergés. C'était également le cas pour PyTorch, qui utilise largement les agents de construction hébergés par GitHub et les agents de construction auto-hébergés. Le groupe possède plus de 70 fichiers de workloads différents dans ses référentiels et en exécute généralement plus de 10 par heure.

Des paramètres Github par défaut qui n'aident pas

Les flux d'actions sont définis dans des fichiers .yml qui contiennent des instructions en syntaxe YAML sur les commandes à exécuter et sur les exécutants. Ces workflows sont déclenchés automatiquement sur différents événements - par exemple, pull_request (PR) - lorsque quelqu'un propose une modification de code pour une branche du référentiel. C'est utile parce que le flux de travail se déclenche et peut exécuter par exemple une série de tests sur le code avant même qu'un réviseur humain ne l'examine et ne décide de le fusionner. « Le fait que certains paramètres par défaut de GitHub ne soient pas très sûrs ne facilite pas les choses », indique John Stawinski. « Par défaut, lorsqu'un runner auto-hébergé est attaché à un dépôt, tous les workflows de ce dépôt peuvent utiliser ce runner. Ce paramètre s'applique également aux flux de travail issus des requêtes pull pour un fork. Rappelez-vous que n'importe qui peut soumettre une demande de fork pull requests à un dépôt GitHub public. Oui, même vous. Le résultat de ces paramètres est que, par défaut, n'importe quel contributeur du dépôt peut exécuter du code sur le runner auto-hébergé en soumettant un PR malveillant ».

Une demande de fork signifie que quelqu'un a créé une copie personnelle de ce dépôt, a travaillé dessus, et essaie maintenant de fusionner les changements. Il s'agit d'une pratique courante, car les contributeurs travaillent souvent sur leurs propres forks avant de soumettre les modifications au dépôt principal pour approbation. Du point de vue de GitHub, un contributeur est toute personne dont les demandes d'extraction ont été fusionnées dans la branche et le paramètre par défaut pour l'exécution du flux de travail est d'exécuter automatiquement les demandes d'extraction du fork des anciens contributeurs. Cela signifie que si quelqu'un a déjà eu un fork pull requests fusionné, les workflows s'exécuteront automatiquement pour tous les prochains. Ce paramètre peut être modifié pour exiger l'approbation avant d'exécuter les workflows sur tous ses PR que le propriétaire soit un ancien contributeur ou non.

Utiliser le gestionnaire d'actions GitHub comme un cheval de Troie

« En consultant l'historique des demandes d'extraction, nous avons trouvé plusieurs PR de contributeurs précédents qui ont déclenché des flux de travail pull_request sans nécessiter d'approbation », d'après les chercheurs. « Cela indique que le référentiel n'exigeait pas l'approbation du workload pour les PR du fork des contributeurs précédents. Bingo ». Ainsi, un attaquant devrait d'abord devenir un contributeur en soumettant un fork PR légitime qui est fusionné, puis il pourrait abuser de son nouveau privilège pour en créer un fork et écrire un fichier de workload malveillant à l'intérieur, puis faire une demande de pull. Ainsi, ce flux corrompu serait automatiquement exécuté sur le Github Actions auto-hébergé de l'entreprise. Cela peut sembler compliqué mais en définitif ce n'est pas le cas. Il n'est en effet pas nécessaire d'ajouter de nouvelles fonctionnalités à un projet pour devenir contributeur, les chercheurs ayant obtenu ce statut pour PyTorch en trouvant une coquille dans un fichier de documentation et en faisant un PR pour la corriger. Une fois leur correction grammaticale mineure acceptée, ils ont alors eu la possibilité d'exécuter des flux de travail malveillants.

Un autre comportement par défaut des runners Actions auto-hébergés est qu'ils ne sont pas éphémères, pas réinitialisés et pas non plus effacés une fois qu'un workload est achevé. « Cela signifie que le workload malveillant peut démarrer un processus en arrière-plan qui continuera à s'exécuter après la fin du travail, et que les modifications apportées aux fichiers (tels que les programmes sur le chemin, etc.) persisteront au-delà du flux de travail actuel », ont déclaré les chercheurs. « Cela signifie également que les workloads futurs s'exécuteront sur ce même runner ». Il s'agit donc d'une bonne cible pour déployer un cheval de Troie qui se connecte aux attaquants et recueille toutes les informations sensibles exposées par les futures exécutions du flux de travail. Mais qu'utiliser comme trojan qui ne serait pas détecté par les produits antivirus ou dont les communications ne seraient pas bloquées ? L'agent runner Actions lui-même, ou plutôt une autre instance de cet agent non liée à une entreprise utilisant PyTorch mais une instance GitHub contrôlée par les attaquants. « Notre technique Runner on Runner (RoR) utilise les mêmes serveurs pour C2 que le runner existant, et le seul binaire que nous laissons tomber est le binaire officiel de l'agent d'exécution GitHub qui est déjà en cours d'exécution sur le système. Au-revoir les protections EDR et pare-feu », a déclaré John Stawinski.

Le Graal de l'extraction de jetons d'accès sensibles

Jusqu'à cette étape, les attaquants ont réussi à faire tourner un programme trojan très furtif dans une machine qui fait partie de l'infrastructure de développement de l'organisation et qui est utilisée pour exécuter des tâches sensibles dans le cadre de son pipeline CI/CD. L'étape suivante est la post-exploitation : il s'agit d'essayer d'exfiltrer des données sensibles et de les faire pivoter vers d'autres parties de l'infrastructure. Les flux de travail comprennent souvent des jetons d'accès à GitHub lui-même ou à d'autres services tiers. Ceux-ci sont nécessaires pour que les tâches définies dans le workload s'exécutent correctement. Par exemple, l'agent de construction a besoin de privilèges de lecture pour vérifier d'abord le référentiel et peut également avoir besoin d'un accès en écriture pour publier le binaire résultant en tant que nouvelle version ou pour modifier les versions existantes. Ces jetons sont stockés sur le système de fichiers du runner à différents endroits comme le fichier de configuration .git ou dans des variables d'environnement et peuvent évidemment être lus par le « trojan » furtif qui s'exécute avec les privilèges root. Certains, comme GITHUB_TOKEN, sont éphémères et ne sont valables que pendant l'exécution du flux de travail, mais les chercheurs ont trouvé des moyens de prolonger leur durée de vie. Même s'ils n'avaient pas trouvé ces méthodes, de nouveaux workloads avec des tokens nouvellement générés peuvent être exécutés en permanence sur un dépôt très actif comme PyTorch, leur potentiel en réserve est donc conséquent.

« Le dépôt PyTorch utilisait des secrets GitHub pour permettre aux runners d'accéder à des systèmes sensibles pendant le processus de publication automatisé », a déclaré John Stawinski. « Le référentiel utilisait de nombreux secrets, notamment plusieurs jeux de clés AWS et des jetons d'accès personnels GitHub (PAT) ». Les PAT disposent souvent de nombreux privilèges et constituent une cible attrayante pour les attaquants, mais dans ce cas, ils ont été utilisés dans le cadre de workloads ne s'exécutant pas sur le runner auto-hébergé compromis. Cependant, les chercheurs ont trouvé des moyens d'utiliser les jetons GitHub éphémères qu'ils ont pu collecter pour placer du code malveillant dans des flux de travail s'exécutant sur d'autres runners et contenaient ces PAT. « Il s'avère que vous ne pouvez pas utiliser un GITHUB_TOKEN pour modifier les fichiers de flux de travail », selon John Stawinski. « Cependant, nous avons découvert plusieurs solutions de contournement exotiques pour ajouter du code malveillant à un flux de travail en utilisant un GITHUB_TOKEN. Dans ce scénario, weekly.yml faisait appel à un autre workload utilisant un script en dehors du répertoire .github/workflows. Nous pouvons ajouter notre code à ce script dans notre branche. Ensuite, nous pourrions déclencher ce flux de travail sur notre branche, ce qui exécuterait notre code malveillant. Si cela vous semble déroutant, ne vous inquiétez pas, c'est également le cas pour la plupart des programmes de récompense des bugs ».

En d'autres termes, même si un attaquant ne peut pas modifier un workflow directement, il peut être en mesure de modifier un script externe appelé par ce flux de travail afin d'obtenir de cette façon son code malveillant. Les dépôts et les flux de travail CI/CD peuvent devenir assez complexes avec de nombreuses interdépendances, de sorte que de tels petits oublis ne sont pas rares. Même sans les PAT, le seul GITHUB_TOKEN doté de privilèges en écriture aurait été suffisant pour empoisonner les versions de PyTorch sur GitHub, et les clés AWS extraites séparément auraient pu être utilisées pour ouvrir une porte dérobée sur les versions de PyTorch hébergées sur le compte AWS de l'entreprise. « Il y avait d'autres ensembles de clés AWS, des PAT GitHub et diverses informations d'identification que nous aurions pu voler, mais nous pensions que nous avions une démonstration claire de l'impact à ce stade », ont fait savoir les chercheurs. « Compte tenu de la nature critique de la vulnérabilité, nous voulions soumettre le rapport dès que possible avant que l'un des 3 500 contributeurs de PyTorch ne décide de conclure un accord avec un acteur malveillant étranger ».

Des actions pour atténuer les risques liés aux flux de travail CI/CD

Les éditeurs de logiciels peuvent tirer de nombreux enseignements de cette attaque : des risques associés à l'exécution de runners d'actions GitHub auto-hébergés dans des configurations par défaut aux workflows exécutant des scripts en dehors de leur répertoire. Ou encore ceux liés aux jetons d'accès à privilèges et aux applications légitimes transformées en chevaux de Troie comme d'autres chercheurs l'ont aussi trouvé avec l'agent AWS System Manager et la solution SSO et de gestion des terminaux de Google pour Windows. « La sécurisation et la protection des runners relèvent de la responsabilité des utilisateurs finaux, et non de GitHub. C'est pourquoi GitHub recommande de ne pas utiliser de runners auto-hébergés sur des dépôts publics », a indiqué John Stawinski. « Apparemment, tout le monde n'écoute pas GitHub, y compris GitHub ». 

Cependant, si les runners auto-hébergés sont nécessaires, les sociétés devraient au moins envisager de changer le paramètre par défaut de « require approval for first-time contributors » en « require approval for all outside collaborators ». C'est également une bonne idée de rendre les runners auto-hébergés éphémères et d'exécuter les workflows à partir des fork PR uniquement sur les runners hébergés sur GitHub. Ce n'est pas la première fois que l'utilisation non sécurisée des fonctionnalités de GitHub Actions génère des risques pour la sécurité de la chaîne d'approvisionnement des logiciels. D'autres services et plateformes CI/CD ont également présenté leurs propres vulnérabilités et des configurations par défaut non sécurisées. « Les problèmes liés à ces voies d'attaque ne sont pas propres à PyTorch », ont expliqué les chercheurs. « Ils ne sont pas non plus propres aux dépôts ML ou même à GitHub. Nous avons démontré à plusieurs reprises les faiblesses de la chaîne d'approvisionnement en exploitant les vulnérabilités de CI/CD dans les organisations technologiques les plus avancées du monde sur plusieurs plateformes CI/CD, et celles-ci ne représentent qu'un petit sous-ensemble de la plus grande surface d'attaque ».