SmokedMeat est un framework de red team CI/CD qui aide à montrer comment une faille dans GitHub Actions peut devenir un vrai chemin d’attaque. Est-ce que ça vaut le coup ? Oui pour une équipe sécu qui veut démontrer un risque, mais pas comme petit scanner tranquille à lancer en cinq minutes.
SmokedMeat, c’est quoi ?
SmokedMeat se présente comme un Metasploit pour les pipelines CI/CD. L’idée est simple à comprendre, même si l’outil est ambitieux : analyser des workflows, trouver des injections ou des mauvaises pratiques, livrer un stager, recevoir un callback dans un serveur C2 appelé Kitchen, puis piloter un agent appelé Brisket depuis une interface terminal appelée Counter.
Le projet vient de BoostSecurity Labs et embarque poutine, leur scanner SAST spécialisé dans les pipelines de build. Le ton est très clair dans le README : usage autorisé uniquement, démonstration offensive, bruit assumé, pas de furtivité magique.
| Élément | Ce que j’ai observé |
|---|---|
| Langage | Go 1.26 |
| Interface opérateur | TUI Bubbletea, nommée Counter |
| Serveur | Kitchen, API HTTP avec WebSocket et graphe |
| Agent | Brisket, agent livré au runner CI |
| Scanner CI/CD | poutine intégré |
| Stockage | base locale Kitchen, type bbolt dans ce build |
Installation de SmokedMeat depuis les sources
Le chemin conseillé est make quickstart, qui demande Docker. Dans mon environnement de test, Docker n’était pas disponible. J’ai donc installé Go 1.26 dans un dossier temporaire, compilé les trois binaires et lancé l’infrastructure minimale à la main avec NATS.
Commandes réellement utilisées :
git clone https://github.com/boostsecurityio/smokedmeat /tmp/smokedmeat
cd /tmp/smokedmeat
go build -o /tmp/smokedmeat-bin/counter ./cmd/counter
go build -o /tmp/smokedmeat-bin/kitchen ./cmd/kitchen
go build -o /tmp/smokedmeat-bin/brisket ./cmd/brisket
Résultat : les trois binaires se compilent. Les binaires produits pesaient environ 43 Mo pour Counter, 68 Mo pour Kitchen et 52 Mo pour Brisket. J’ai aussi lancé des tests ciblés sur les parties poutine et Kitchen :
go test ./internal/poutine ./internal/kitchen -run 'Test.*(Analyze|DefaultBashPayload|Stager|Beacon)' -count=1
Sortie observée : les deux paquets testés passent, ok sur internal/poutine et internal/kitchen.
SmokedMeat à l’usage : analyse d’un workflow vulnérable
Pour éviter de jouer avec un dépôt public qui ne m’appartient pas, j’ai créé un dépôt Git local volontairement vulnérable. Le workflow GitHub Actions interpolait directement le titre et le body d’une pull request dans un run shell et dans actions/github-script.
Extrait du cas testé :
name: CI
on:
pull_request:
types: [opened, synchronize]
permissions: write-all
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: dangerous title interpolation
run: |
echo "PR title: ${{ github.event.pull_request.title }}"
- name: dangerous script
uses: actions/github-script@v7
with:
script: |
const body = `${{ github.event.pull_request.body }}`
console.log(body)
J’ai ensuite appelé le moteur local poutine intégré par SmokedMeat depuis un petit point d’entrée Go temporaire. Ce n’est pas juste un démarrage : l’outil a produit un JSON de findings.
La synthèse réelle était claire :
SUMMARY repos=1 findings=2 critical=0 high=2 medium=0 workflows=1 success=true
V001 high injection line=17 context=pr_body
V002 high injection line=12 context=pr_title
Ce que j’aime ici : SmokedMeat ne dit pas seulement « workflow suspect ». Il remonte la ligne, le contexte exploitable (pr_title, pr_body), la sévérité et le type de règle. Pour un audit, c’est le niveau d’information dont on a besoin pour expliquer le risque à un lead DevOps.
SmokedMeat avec Kitchen et Brisket
J’ai ensuite lancé Kitchen, le serveur C2 local, avec authentification par token et NATS en local. Le healthcheck répondait bien, et les routes protégées refusaient l’accès sans token.
nats-server -js -p 4222
AUTH_MODE=token AUTH_TOKEN=<token> KITCHEN_PORT=18080 \
NATS_URL=nats://127.0.0.1:4222 kitchen
Ensuite, j’ai enregistré un stager demo1 pour une session sess-demo. Kitchen a répondu avec un objet JSON contenant l’URL de callback, l’identifiant du stager, les métadonnées et son mode express.

Quand j’ai appelé l’URL de callback, Kitchen a généré un payload bash avec un AGENT_ID, un AGENT_TOKEN et une commande qui télécharge Brisket depuis /agent/brisket-linux-amd64. J’ai ensuite exécuté Brisket en mode express avec des variables CI simulées, dont un token GitHub factice et des variables sensibles factices.

Sortie utile :
INFO brisket agent express mode agent_id=agt_...
smokedmeat.upload.v1 callback_hash=... agent_hash=...
INFO express mode complete
Le callback fonctionne. En revanche, mon graphe /pantry est resté vide dans ce scénario local. C’est cohérent : je n’ai pas livré l’implant via une vraie pull request GitHub ni exécuté le flux complet dans un runner Actions vulnérable. Sans Docker et sans PAT GitHub, je n’ai pas validé le parcours public whooli de bout en bout.
Ce que j’aime dans SmokedMeat
D’abord, le modèle colle bien à la réalité CI/CD. Beaucoup d’outils s’arrêtent à « vous avez une injection ». SmokedMeat pense en chaîne : finding, payload, stager, callback, agent, loot, graphe.
Ensuite, le scanner embarqué est pertinent. Sur mon workflow volontairement fragile, il a trouvé exactement les deux interpolations dangereuses que j’attendais. Le résultat est exploitable pour un rapport client, pas seulement pour un log de dev.
J’aime aussi la séparation des rôles : Counter pour l’opérateur, Kitchen pour l’équipe serveur, Brisket pour l’agent. C’est plus lourd qu’un simple CLI, mais plus proche d’un exercice red team réel.
Les limites de SmokedMeat
La première limite est la friction. Le quickstart demande Docker, les scénarios publics demandent un token GitHub, et l’expérience complète suppose une vraie cible de test. Ce n’est pas un outil que je donnerais tel quel à une équipe non technique.
Deuxième limite : le produit est offensif par design. Même en labo, il faut être carré sur les autorisations. Le stager et l’agent sont faits pour exécuter du code dans un pipeline, donc le cadre légal et opérationnel doit être net.
Troisième limite : sans le flux complet GitHub Actions, une partie de la valeur reste théorique. J’ai validé le scanner local, la génération de stager, le callback Brisket et le serveur Kitchen. Je n’ai pas validé la création automatique de PR ni l’exploitation complète d’un runner GitHub public.
Est-ce que SmokedMeat marche vraiment ?
Oui, sur les briques que j’ai testées. Le build source fonctionne avec Go 1.26, les tests ciblés passent, le scanner trouve des injections réelles dans un workflow local, Kitchen enregistre un stager et Brisket sait rappeler le serveur en express.
Mon verdict reste nuancé : SmokedMeat marche, mais sa valeur maximale apparaît dans un vrai exercice CI/CD contrôlé, avec une organisation de test, un token GitHub adapté et Docker disponible. En mode local, il reste déjà utile pour montrer la qualité du moteur d’analyse et la mécanique de callback.
Faut-il adopter SmokedMeat ?
Pour une équipe red team, une équipe AppSec mature ou un freelance sécu qui fait des audits CI/CD, je dirais : essayer. L’outil peut aider à transformer une vulnérabilité abstraite en démonstration concrète.
Pour une équipe DevOps qui cherche seulement un scanner simple à brancher dans la CI, je dirais plutôt : passer, ou regarder poutine directement. SmokedMeat est un framework d’exercice offensif, pas un petit garde-fou de pipeline.
Mon verdict : à essayer en labo si vous voulez prouver l’impact d’une faille GitHub Actions. À éviter en production sans cadre d’autorisation strict.
FAQ
SmokedMeat remplace-t-il un scanner CI/CD classique ?
Non. Il va plus loin qu’un scanner, avec stager, agent et serveur C2. Pour du contrôle continu simple, un outil plus léger sera souvent préférable.
Peut-on utiliser SmokedMeat sans Docker ?
Partiellement, oui. J’ai compilé les binaires depuis les sources et lancé Kitchen avec NATS, puis testé le scanner local et Brisket. Le quickstart officiel et les scénarios complets restent pensés pour Docker.
SmokedMeat est-il dangereux ?
Oui, si on l’utilise hors cadre. Il est conçu pour démontrer des chemins d’attaque CI/CD. Il faut l’utiliser uniquement sur des environnements détenus ou explicitement autorisés.

