
Preuve de concept en laboratoire démontrant l'injection de commandes dans le dispatch de workflow GitHub Actions (CVE-2026-39866). Exécute les versions vulnérable et corrigée côte à côte pour une analyse pédagogique.
Laboratoire de preuve de concept pour l'injection de commande dans le workflow dispatch de GitHub Actions
CVE-2026-39866 est un bug d'injection de commande dans le workflow de release GitHub Actions de Lawnchair. Le fichier release_update.yml transmet l'entrée utilisateur directement à bash sans guillemets, ce qui permet à un attaquant d'injecter des commandes. Ce dépôt exécute côte à côte les versions vulnérable et corrigée.
| Champ | Valeur |
|---|---|
| ID CVE | CVE-2026-39866 |
| GHSA | GHSA-9prc-pp2c-3427 |
| CWE | CWE-78 (Neutralisation incorrecte d'éléments spéciaux utilisés dans une commande système) |
| Produit | LawnchairLauncher/lawnchair |
| Versions concernées | Toutes les versions antérieures à fcba413 |
| Corrigé dans | fcba413 |
| Découverte | Abhay Kumar (@abhayclasher) |
La vulnérabilité existe dans le fichier de workflow release_update.yml. L'action prend un paramètre artifactName et le transmet directement dans une commande bash sans guillemets.
- name: Rename artifact
run: mv ${{ inputs.artifactName }}.zip lawnchair.zip
GitHub Actions substitue la valeur de la variable dans la chaîne avant que le shell ne l'exécute. Sans guillemets autour de ${{ inputs.artifactName }}, le shell interprète les espaces et les points-virgules comme des séparateurs de commandes.
Si un attaquant fournit file.zip; touch pwned.txt # comme artifactName :
Comment GitHub Actions l'analyse :
${{ inputs.artifactName }} → file.zip; touch pwned.txt #
Comment le shell l'exécute :
mv file.zip; touch pwned.txt #.zip lawnchair.zip
Le shell exécute mv file.zip, puis touch pwned.txt, puis ignore le reste comme un commentaire.
GITHUB_TOKEN ou les clés de signature de release).git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866
docker compose up -d
Une fois le serveur démarré, visitez ces URL :
| URL | Description |
|---|---|
http://localhost:3000/workflow/vulnerable | Workflow vulnérable — l'injection de commande s'exécute à la soumission |
http://localhost:3001/workflow/patched | Même workflow avec le correctif fcba413 appliqué |
La version vulnérable exécutera votre commande injectée sur le runner. La version corrigée traite l'entrée comme une chaîne littérale.
Vous pouvez également exécuter le PoC autonome :
cd poc
node exploit.js
Cela démarre un serveur minimal sur le port 3000 qui démontre le même bug sans le contexte complet de l'application.
1. L'attaquant contrôle le paramètre d'entrée artifactName
2. GitHub Actions développe ${{ inputs.artifactName }} dans le YAML
3. L'étape run envoie cela à bash
4. Bash voit des caractères spéciaux non protégés et les traite comme de la syntaxe de commande
5. Les commandes s'exécutent avec les privilèges de l'environnement du runner
┌────────────────────────────────────────────────────┐
│ 1. Entrée malveillante : │
│ │
│ artifactName = "file.zip; id #" │
│ │
│ 2. GitHub Actions substitue dans le YAML : │
│ │
│ run: mv file.zip; id #.zip lawnchair.zip │
│ │
│ 3. Bash voit : │
│ - mv file.zip │
│ - id <-- Commande arbitraire │
│ - #.zip lawnchair.zip (ignoré) │
│ │
│ 4. Le shell exécute la commande arbitraire │
└────────────────────────────────────────────────────┘
La version fcba413 introduit des guillemets autour de la variable interpolée :
- name: Rename artifact
run: mv "${{ inputs.artifactName }}.zip" lawnchair.zip
Les guillemets empêchent le shell d'analyser les caractères spéciaux. La charge utile reste traitée comme une chaîne de nom de fichier littérale.
La bonne pratique consiste à utiliser des variables d'environnement pour isoler complètement l'entrée utilisateur :
- name: Rename artifact
env:
ARTIFACT_NAME: ${{ inputs.artifactName }}
run: mv "$ARTIFACT_NAME.zip" lawnchair.zip
Consultez le commit complet : fcba413
CVE-2026-39866/
├── README.md # Ce fichier
├── docker-compose.yml # Configuration du laboratoire Docker
├── app/
│ ├── Dockerfile # Configuration de construction du conteneur
│ ├── package.json # Dépendances Node.js
│ └── server.js # Simulation du runner de workflow vulnérable + corrigé
└── poc/
└── exploit.js # Preuve de concept minimale autonome
Signalé par Abhay Kumar. Ce dépôt est destiné uniquement à un usage éducatif local. Ne déployez pas le serveur vulnérable sur un réseau public.
Uniquement à des fins éducatives — à utiliser dans des environnements isolés