Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
Kestra-cve-2026-53576 — Reproduction de bout en bout et détection multicouche de CVE-2026-53576, la RCE non authentifiée dans Kestra — poussée au-delà du PoC de base pour montrer comment une mauvaise configuration courante du socket Docker transforme un accès root au conteneur en compromission complète de l'hôte. | Kitploit
Outils/GitHubGitHub/atlasvector/kestra-cve-2026-53576
Escalade de PrivilègesSécurité des ConteneursMécanismes de PersistanceAnalyse des VulnérabilitésExploitationPost-ExploitationTests d'IntrusionArticles et Recherche

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →

À propos

Reproduction de bout en bout et détection multicouche de CVE-2026-53576, la RCE non authentifiée dans Kestra — poussée au-delà du PoC de base pour montrer comment une mauvaise configuration courante du socket Docker transforme un accès root au conteneur en compromission complète de l'hôte.

Red Teaming
Réponse aux Incidents
Labs et Pratique
GitHubatlasvector/kestra-cve-2026-53576

Kestra-cve-2026-53576

Voir le dépôt
il y a 2 joursPas encore vérifié
Partager

RCE non authentifiée dans Kestra via un contournement d'authentification par chemin suffixé (CVE-2026-53576)

Table des matières

  • Résumé exécutif
  • 1. Résumé
  • 2. Versions affectées / corrigées
  • 3. Environnement de test et vérification préalable
  • 4. Cause racine
  • 5. Simulation d'attaque
  • 6. Ingénierie de détection
  • 7. Cartographie ATT&CK
  • 8. Remédiation et durcissement
  • 9. Références

Résumé exécutif

Kestra, une plateforme open-source d'orchestration de workflows, a livré un filtre d'authentification qui décidait si une requête nécessitait des identifiants en vérifiant si l'URL se terminait par /configs - une vérification destinée à exposer un seul endpoint public inoffensif.

Parce que la vérification ne portait que sur la fin de la chaîne, toute requête dont l'URL se terminait de cette manière contournait entièrement l'authentification, y compris les endpoints qui créent et exécutent du code arbitraire. Le résultat : quiconque peut atteindre une instance Kestra vulnérable sur le réseau peut exécuter des commandes en tant que root sans aucun identifiant, sans hameçonnage, sans devinette de mot de passe, sans étape d'élévation de privilèges requise.

Dans cet exercice, cette unique faille a été exploitée de bout en bout contre une instance de laboratoire auto-hébergée :

  • Exécution de code non authentifiée.
  • Découverte d'un socket Docker exposé.
  • Élévation vers un accès root complet sur l'hôte sous-jacent.
  • Deux mécanismes de persistance indépendants (une clé SSH backdoor et un beacon planifié).

Chaque étape a été capturée face à la télémétrie défensive (IDS réseau, sécurité d'exécution hôte, audit Linux et journaux système).

Impact métier si non corrigé : compromission complète de l'hôte exécutant Kestra, pas seulement de l'application - et par extension de tout ce qui est accessible depuis cet hôte.

Correctif : mettre à niveau vers Kestra 1.0.45 / 1.3.21 ou ultérieur (le correctif seul ferme la vulnérabilité principale ; les étapes d'élévation et de persistance nécessitent un correctif distinct et indépendant - ne montez pas le socket Docker dans les conteneurs applicatifs, voir Section 8).

1. Résumé

Ce rapport documente CVE-2026-53576, une vulnérabilité d'exécution de code à distance non authentifiée de score CVSS 10.0 dans Kestra ≤1.3.20, causée par un contournement d'authentification par suffixe de chemin (AuthenticationFilter.java, endsWith("/configs")).

Un attaquant qui nomme le namespace et l'ID d'un workflow configs peut créer et exécuter des commandes shell arbitraires sans identifiants, en tant que root, à l'intérieur du conteneur Kestra. Corrigé dans 1.0.45 et 1.3.21. Le même bug a également été signalé indépendamment sous CVE-2026-49869, qui est le numéro listé dans le catalogue KEV de la CISA lié à une exploitation réelle en conditions opérationnelles. Les deux doivent être cités ensemble, car les sources publiques et les scanners peuvent référencer l'un ou l'autre.

2. Versions affectées / corrigées

VulnérableKestra ≤ 1.3.20 (et pré-1.0.45 sur la branche 1.0.x)
Corrigée1.0.45 / 1.3.21+
CVECVE-2026-53576
Avis jumeauCVE-2026-49869 - même bug, listé dans le KEV de la CISA (en conditions opérationnelles)

Chronologie

DateÉvénement
2026-06-02Publication de Kestra 1.3.21 ; le changelog mentionne un « contournement d'authentification potentiel dans le filtre d'authentification »
2026-06-03Publication de Kestra 1.0.45 sur la branche 1.0.x
2026-09-02CVE-2026-49869 (l'avis jumeau) ajouté au catalogue KEV de la CISA

3. Environnement de test et vérification préalable

Homelab auto-hébergé : - Hôte Debian (nom d'hôte docker, noyau 6.12.107+deb13-amd64), - Docker Engine 29.8.1. - Kestra déployé via l'image officielle kestra/kestra:v1.3.20, accessible via un reverse proxy Caddy à kestra.int.atlasvec.com. - Pile de télémétrie testée : Falco (runtime/eBPF, niveau hôte), - Suricata 8.0.3 IDS (miroir SPAN passif), redirigeant vers Splunk.

Avant de toucher quoi que ce soit, j'ai confirmé que la cible était réellement vulnérable et que la pile de télémétrie était active. J'ai également pris un instantané de l'état pré-attaque afin de pouvoir revenir en arrière une fois terminé.

Kestra exécutant la version vulnérable : 01-kestra-version-v1.3.20

Le conteneur lui-même, démarré et accessible : 06-docker-kestra-running

Les unités de Falco chargées sur l'hôte : 02-falco-units-one-active

Falco émettant activement des événements (mode eBPF moderne) : 03-falco-modern-bpf-active-emitting

Le service Suricata actif : 04-suricata-systemctl-active

Et son eve.json écrivant des événements en direct : 05-suricata-eve-json-live

4. Cause racine

Il s'agit de deux erreurs qui s'alignent, pas d'une seule.

Premièrement, le filtre d'authentification décide si une requête peut contourner l'authentification en faisant correspondre la fin du chemin de la requête :```java // AuthenticationFilter.java — the vulnerable check, confirmed against the self-hosted v1.3.20 source if (path.endsWith("/configs")) { // treated as a public, unauthenticated path }

root@kitploit:~
Ce contrôle existe pour exposer un seul endpoint inoffensif, la configuration de l'instance publique. Comme `endsWith` ne lit que la fin de la chaîne, il ne peut pas distinguer ce chemin sûr de tout autre chemin qui se termine de la même manière.

Deuxièmement, le routeur de Kestra continue de dispatcher ces chemins similaires vers leurs véritables handlers sensibles. `/api/v1/main/flows/configs` atteint le handler de création de flow. `/api/v1/main/executions/configs/configs` atteint le handler d'exécution. Le filtre laisse passer les deux comme publics, et le routeur les exécute quand même. Nommez le namespace et l'ID d'un flow `configs`, et un endpoint exécutant du code se termine désormais par `/configs`, héritant ainsi du laissez-passer gratuit du chemin public.

La paire de requêtes capturée le montre clairement. `GET /api/v1/main/flows/search` sans identifiants renvoie `401`. `POST /api/v1/main/flows/configs` avec le même en-tête d'authentification vide renvoie `200` et stocke le flow (voir §5.1).

## 5. Simulation d'attaque

> Chaque étape ci-dessous est capturée avec sa propre capture d'écran. Tout s'exécute dans mon propre homelab isolé contre une instance Kestra auto-hébergée derrière le reverse proxy.

### 5.1 Étape 1 - contournement d'authentification -> root à l'intérieur du conteneur Kestra

**Contrôle :** `GET /api/v1/main/flows/search` sans identifiants → `401 Unauthorized`. 
Confirme que l'authentification est appliquée sur une route normale.
![01-burp-control-401](https://assets.kitploit.com/production/public/readmes/56348/2a6d266e96b2b4e09bc32c0ccf3609159eaf5bacdc204989d5572211e616e6ff/8a8bab993fd7ba08656d03ad85822001996be4c391de87c535751cc9e14542a1-display-v1.webp)
_____

**Plant :** Envoi de `POST /api/v1/main/flows/configs` sans en-tête d'authentification. Le corps est un YAML de flow dont le `namespace` et le `flow id` sont tous deux `configs`, contenant une tâche Commands qui utilise le Process task runner. Le serveur renvoie `200 OK` et stocke le flow. Le suffixe `/configs` sur une route autrement protégée est ce qui déclenche le contournement **(voir Section 4).**
![02-burp-plant-flow-200](https://assets.kitploit.com/production/public/readmes/56348/5d2a2db2d70e9d60e7136c765a7a85f4d8b684ea945da5b84e84d85a2b05a151/1ee6d8f7efd58d22ef41b536f32acbbdd417d68ac030ed28e2d06f9d6d4ccb93-display-v1.webp)
**Listen & Wait** : Démarrage d'un simple listener avec nc à des fins de test, afin de capturer le reverse shell.

![03-kali-listener-4444](https://assets.kitploit.com/production/public/readmes/56348/88d7d3516aaeef900bbb9b372d9006323a3d7391078b693124ee24710da557ac/aa9806454d3742b7e958611a5c6ed2d3dfb3072eec3c14e4bb3f050a41220803-display-v1.webp)




**Trigger / Execution** `POST /api/v1/main/executions/configs/configs`, sans en-tête d'authentification → `200 OK`, nouvelle exécution créée.
![04-burp-execute-200](https://assets.kitploit.com/production/public/readmes/56348/e70d1cd38f3825dca36c25e722bdfcf4a7614b1402fe1b6d0ccebdf992738bbe/9a394373a293d22ef55e3b6ee32eae6991feac73a5ccbc7909536bdf3435172d-display-v1.webp)

**BaaaaM :** Nous avons obtenu le shell, mais rappelez-vous - nous sommes « isolés », `root` **MAIS** à l'intérieur du conteneur Docker Kestra, donc nous « ne devrions pas » pouvoir nous échapper d'ici. « **enfin, c'est ce que je pensais** »
![05-kali-revshell-container](https://assets.kitploit.com/production/public/readmes/56348/97257430abd2415f37f12c790a738d73e12f0c2509c193b1b7d24545453a1a70/9880399e445d4b8b602c1f381bbd8523a83c8c3c097b9a919e2b1871c7f1129e-display-v1.webp)


La commande de la tâche s'exécute en tant qu'enfant direct du processus JVM Kestra, confirmé via l'arbre de processus côté hôte, et se termine avec succès selon le propre journal d'exécution de Kestra.

**Tableau de bord des logs de Kestra**
![06-kestra-ui-pwn-log](https://assets.kitploit.com/production/public/readmes/56348/b447d0d268daa119b1d64a9d7d95a588b80a7cb228613b81a6bfd69777aea7bb/467a61936f8779b533cc83a297d651957e17f9d00241ff97cc7640df917450fe-display-v1.webp)


**Arbre de processus de Kestra**
![07-kestra-process-tree](https://assets.kitploit.com/production/public/readmes/56348/a7e20df484e1c5759fca4f5e403bc280ffc45b03b47dae22804321bb8398858c/0a0e30aa6c0537adf8ddd204453c8d51e808de2d5ff764fc5ad8f551cf059665-display-v1.webp)

**Résultat :** exécution de code à distance non authentifiée en tant que root, à l'intérieur du conteneur Kestra. C'est la preuve complète et autonome de **CVE-2026-53576**.


### 5.2 Étape 2 - escalade via le socket Docker exposé (spécifique au homelab, ne fait pas partie du CVE lui-même)

Depuis le shell de l'Étape 1, `/var/run/docker.sock` a été trouvé monté dans le conteneur - un pattern Docker-outside-of-Docker (DooD), courant lorsque le task runner `Docker` de Kestra est configuré, puisqu'il a besoin d'un daemon avec lequel communiquer. 

Atteindre ce socket est équivalent à un **root** hôte : l'API Docker permet à un client de configurer des bind mounts hôtes arbitraires et des namespaces PID/réseau hôtes pour les conteneurs qu'il lance, et le daemon derrière le socket s'exécute déjà en tant que root sur l'hôte. 

Ce n'est pas un exploit de kernel ou d'évasion de conteneur - c'est l'API Docker qui fait ce pour quoi elle est conçue, atteinte depuis un endroit d'où elle n'aurait pas dû être accessible.

En utilisant le socket, j'ai créé un conteneur frère avec le système de fichiers hôte monté et les namespaces PID et réseau hôtes, puis je l'ai démarré.

**Note :** une seconde variante, plus discrète, est également apparue lors des tests, bien que je ne l'aie pas capturée séparément. Au lieu de toujours créer un nouveau conteneur frère, le même accès au socket permet d'exécuter `POST /containers/{id}/exec` contre un conteneur déjà en cours d'exécution, de sorte qu'il n'y a aucun événement de création de conteneur. Sur cet hôte Docker, j'ai à la fois Kestra et Portainer, dont l'un ou l'autre pourrait être utilisé pour la même évasion. Cela compte pour la détection : toute règle axée sur la création d'un nouveau conteneur privilégié manque cette variante.

Confirmation de root et découverte du socket présent, sans restriction :
![08-docker-socket-present](https://assets.kitploit.com/production/public/readmes/56348/d2a34fd7db05bb2966a540410dbc8e76cfec416a545786349372984902b128f9/ca3ce180476010ee482b60c42687751f27a072aa2c0b78fbb6400b8406cee5e5-display-v1.webp)

Vérification que le daemon Docker est réellement joignable via le socket :
![09-docker-api-version](https://assets.kitploit.com/production/public/readmes/56348/7ce4365a9c48db827aa3c6e53c73622bef0ee7a99f530b5e37a065601b3892b0/d6debd41ca945cd5812add55df46cc5dd2f7bbaba1c88965f438ee7d5c9a4bcc-display-v1.webp)

Liste des images déjà locales, pour ne pas avoir à en tirer une :
![10-docker-images-enum](https://assets.kitploit.com/production/public/readmes/56348/aa1ff3ff76e92437899afc7b603925eeb53838f99f4d8586df69f8a96ff23622/34cd26942854c4c4bf59135beb54cac278aaad304faac4a3f5b64bec9e1a84de-display-v1.webp)

Première tentative : un conteneur privilégié avec le système de fichiers hôte monté :
![11-docker-create-privileged](https://assets.kitploit.com/production/public/readmes/56348/38f4f42a486e3f46f4d798e131b3eb3bd58a7e7507b7a9e88845ed52ee32bd29/673b8ab3de93e21c6fbe23b343c4cd7fc4ddac11554ad56c0d656e2ae46467dd-display-v1.webp)

Deuxième tentative, cette fois en ajoutant les namespaces PID et réseau hôtes :
![12-docker-create-hostns](https://assets.kitploit.com/production/public/readmes/56348/f2a5c239adbabc9b3cef7f24d2ff60b49765bfec2ec231520d18edbcaec597cb/c387405779506f4d5e8f30715d359550b69d2c1a899525b293e8b69ce61c6e73-display-v1.webp)

Troisième tentative, en chrootant directement dans le système de fichiers hôte monté :
![13-docker-create-chroot](https://assets.kitploit.com/production/public/readmes/56348/dc38f66a291764a224f2982d22b9ca3cb37bdb31d39c48344b8030448f452e41/23519a1a158885f8b9b5691ffab18ef6eb1b5c989af00bf7c4caf6fe58ee7176-display-v1.webp)

Listener en place et en attente sur le port que le shell d'évasion rappellera :
![14-host-listener-4491](https://assets.kitploit.com/production/public/readmes/56348/37b558192ffff02366b4d219e2393d1f9fe468e82555c90a236e2cf1bdd35aa7/116c772ff659ec661cad22e023a6179eb8ffd1ebf409624b4832a27a4e947626-display-v1.webp)

Démarrage du conteneur :
![15-docker-container-start](https://assets.kitploit.com/production/public/readmes/56348/277f7899c2fbb23d86a1918621f5efe79c635bd84af584811e98bdf37b7ab253/3f02d6a19613662df1b32fb8fa1bdbb03043f6b232844d2de3a23f552873103e-display-v1.webp)


**Root, mais cette fois c'est l'hôte, pas le conteneur :**
![16-host-root-id](https://assets.kitploit.com/production/public/readmes/56348/0a77d4e6c239354c2c79737c51f492005cf129af8c5a595614c1005c0a125405/02ed259073555446cd67e2ce2ab86c7408fb9c2e7864ce3daac8bc50fbd2db36-display-v1.webp)

Version du kernel correspondant au véritable hôte, pas à la vue isolée d'un conteneur :
![17-host-root-uname](https://assets.kitploit.com/production/public/readmes/56348/9f059686a831bfb8b5196412b9fd10492e8aeeb211dcb7d45fc98b3b6c0f999c/2c2dbd97e8c41b21085d9230d3b2ffb74f76c693947d9831007cff197fe3647d-display-v1.webp)


Passage à un shell interactif approprié pour le reste de la session :
![18-host-pty-upgrade](https://assets.kitploit.com/production/public/readmes/56348/34894e77c38df22cbbd807a14fd14b8e49aadc6f894d990e4022bc5b85978b62/0f81e4ed6fa6dfef8867eed194e7e4cdf12dc025b00c5763b2884530199c15d7-display-v1.webp)

### 5.3 Étape 3 - persistance, confirmée en action

Depuis le shell root hôte, j'ai mis en place deux mécanismes de persistance indépendants, 

**Premièrement**, une clé SSH. J'ai vérifié qui avait déjà accès :
![19-host-authkeys-enum](https://assets.kitploit.com/production/public/readmes/56348/553f3a2b399361ce652e836fab2525686519f44dee9d94d8b7c9d45b1b939d44/0a2c697dc0f216566dba1b4cb38f134ce23ff7b57bc40dc87032016a558e2668-display-v1.webp)

Puis vérifié la propre configuration de sshd pour tout ce qui pourrait bloquer une nouvelle clé :
![20-host-sshd-config-enum](https://assets.kitploit.com/production/public/readmes/56348/a25dd7b5a8376e48cee6c75e64264fa973def1db09a9d7ea520b705e32021ef3/adbfee0a5f27442b43fa318a36ef281d5abc1d998c367f2e5978b11d8282677d-display-v1.webp)

Généré une paire de clés sur la machine attaquante :
![21-attacker-keygen](https://assets.kitploit.com/production/public/readmes/56348/4b560189f5ab37c619d941dfedadd77a4fa8a49420aa720eafd6fccc44f7f734/f90cf5f5b1ebb76fe1505b063ef844e17c9c7b8e9f21a2f92213eb36f9428dec-display-v1.webp)

Confirmé que sshd était réellement en écoute :
![22-host-sshd-active](https://assets.kitploit.com/production/public/readmes/56348/bd0ee4ae0a24f08b096c16eff66fd4ff87efccd92f1c1b509078d24f65af0e0f/78518194b025287cdf07b81a3335ea8b02239fcca6cbd2f32b507e28cc908cf9-display-v1.webp)

Déposé la clé publique dans le `authorized_keys` de root :
![23-host-authkeys-implant](https://assets.kitploit.com/production/public/readmes/56348/e2faba217e62433f6dcd9777fa85c8dcd24858ab1c6a2f5d1293515d744003bc/0c1ca3973a85e0ade63514eaa9ad4f3d0fd63f047dc7be19b112058f24632cfd-display-v1.webp)

Et connecté avec la clé privée correspondante :
![24-ssh-root-login-proof](https://assets.kitploit.com/production/public/readmes/56348/14b698fb9eae32aff09e6ec7d179ecf1c2ef665d7b87be8d6f9fac956e68db2b/a2ced84629d34de099a598e38f71ba54411a890eaadb50a2dc8dd8dc099a6459-display-v1.webp)

**PS :** C'est un accès root indépendant de la chaîne RCE d'origine. Même si Kestra est corrigé, cette clé fonctionne toujours.

**Deuxièmement**, une balise cron. J'ai déposé un callback au niveau root configuré pour s'exécuter à intervalle régulier, puis testé avec un nouveau listener pour voir s'il se déclenchait réellement selon le planning :
![25-host-cron-persist](https://assets.kitploit.com/production/public/readmes/56348/3dfa92072c55dd052127ef69943a6c43a855534420b286e0dbd62ce833dc441f/7a75828de1e323b44a4c792a2eed2713cf656433685deef7931026f815b2dd98-display-v1.webp)

**Il s'est déclenché. Un second point d'ancrage sur l'hôte, indépendant à la fois du RCE et de la clé SSH.**


### 5.4 Chronologie de la kill-chain vérifiée.

La chronologie ci-dessous est différente : elle est entièrement reconstruite à partir de télémétrie indépendante côté défenseur (IDS réseau + sécurité runtime hôte), extraite et validée en direct contre de vraies données Splunk.

**RCE primaire - le réseau détecte la livraison, l'hôte confirme l'exécution, à 103 secondes d'intervalle :**

| Heure (EDT)  | Couche             | Événement                                                                                    |
| ------------ | ------------------ | -------------------------------------------------------------------------------------------- |
| 02:46:09.778 | Réseau (Suricata)  | La signature ET publique pour ce CVE exact se déclenche                                      |
| 02:47:52.277 | Hôte (Falco)       | Reverse shell confirmé à l'intérieur du conteneur Kestra, commande exacte et ID de conteneur capturés |

**Escalade - le réseau et l'hôte corroborent chacun indépendamment une partie différente du même événement :**

| Heure (EDT)  | Couche             | Événement                                                                                                                          |
| ------------ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| 03:48:55.503 | Hôte (Falco)       | Reverse shell depuis le conteneur d'escalade                                                                                       |
| 03:51:57.360 | Réseau (Suricata)  | Flux TCP **et** une alerte basée sur le contenu - la chaîne littérale `uid=0(root)` a été vue quittant l'hôte en clair lors de l'exécution de `id` |


## 6. Ingénierie de détection

J'ai construit les détections contre de la télémétrie réellement capturée, puis testé chacune contre un replay. La matrice montre où la couverture existait avant que je commence et où elle n'existait pas.

### 6.0 Matrice de couverture des détections

| Étape | Réseau (Suricata) | Hôte (Falco) | Hôte (auditd/journald) | Statut |
| --- | --- | --- | --- | --- |
| Requête d'exploit initiale | La signature ET publique se déclenche | Aucune visibilité au niveau applicatif | - | Couvert (préexistant) |
| Exécution RCE | - | Règle native, taguée T1059 | - | Couvert (préexistant) |
| Escalade via socket Docker (la technique) | - | Lacune : ne capture que le shell résultant, pas les appels API d'abus du socket | Les enregistrements EXECVE capturent les appels exacts `curl --unix-socket` | Lacune trouvée, comblée avec auditd + une règle Falco personnalisée |
| Confirmation de l'impact de l'escalade | Alerte de contenu sur la sortie `id` divulguée | Même règle générique de shell | - | Couvert (préexistant, multi-couches) |
| Persistance SSH | - | - | journald : cycle de vie PAM complet plus empreinte de clé | Couvert (hôte uniquement) |
| Persistance cron | - | - | journald et linux_audit, deux sources, cadence exacte de 5 min | Couvert (hôte uniquement) |

La lacune est l'escalade via socket Docker. Les règles par défaut de Falco capturent le shell qu'un conteneur compromis lance, mais rien dans l'ensemble stable par défaut ne signale un processus atteignant `/var/run/docker.sock` pour piloter directement l'API Docker. Les règles qui toucheraient les lancements de conteneurs privilégiés, `Launch Privileged Container` et `Launch Sensitive Mount Container`, sont livrées aux niveaux de maturité `incubating` et `sandbox`, donc l'ensemble de règles par défaut ne les charge pas non plus. J'ai comblé la lacune avec une recherche de corrélation auditd (Détection 03) et une règle Falco personnalisée (§6.3).

### 6.1 Splunk - cinq recherches de corrélation, déployées et planifiées

Les cinq s'exécutent en direct dans Splunk (`*/5 * * * *`, suivi des alertes activé, throttling par champ), pas seulement écrites et laissées sous forme de texte. Le SPL complet, les notes exactes sur les faux positifs, la sévérité, les tags MITRE et les runbooks de réponse pour chacune sont dans `detections/splunk/*.spl`. Le tableau de statut ci-dessous est le résultat testé en direct pour chacune, y compris un vrai bug que j'ai trouvé et corrigé.

| #   | Détection                                                  | MITRE                | Statut                                                                                                                              |
| --- | ---------------------------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| 01  | Signature d'exploit réseau (consomme l'alerte Suricata ET) | T1190                | **Déclenchée** - replay historique confirmé, aucun nouvel événement depuis (hors fenêtre d'analyse actuelle)                                             |
| 02  | Exécution RCE hôte (règle Falco de redirection vers le réseau)        | T1059                | **Déclenchée** - replay historique confirmé                                                                                             |
| 03  | Abus du socket Docker (auditd, le combleur de lacune)               | T1610, T1611         | **Déclenchée sur le planificateur en direct lui-même** - `triggered_alert_count: 1` confirmé sur la vraie exécution `*/5 * * * *`, pas seulement un test manuel |
| 04  | Persistance par connexion root SSH                                 | T1098.004, T1021.004 | **Déclenchée** - replay historique confirmé                                                                                             |
| 05  | Persistance par balise cron                                    | T1053.003            | **Déployée avec un bug que j'ai trouvé et corrigé en direct.** La fenêtre de dispatch `-10m` ne contenait que deux des trois buckets de 5 minutes nécessaires au seuil `distinct_intervals >= 3`, donc elle ne pouvait jamais se déclencher malgré la balise toujours active dans les données réelles. Élargie à `-30m` et redéployée - et le planificateur l'a déclenchée 53 fois depuis, donc le correctif tient. |

La répartition des rôles de la Détection 03 est délibérée : plutôt que de réimplémenter la propre logique de signature de Suricata en SPL, ou d'essayer de faire capturer par Falco une technique au niveau du socket pour laquelle il n'a pas été conçu, la couche SIEM ici consomme l'enregistrement EXECVE d'auditd des appels exacts `curl --unix-socket` - la source de télémétrie qui s'est avérée la plus fiable pour cette technique spécifique.

1. **Détection 01** - déclenchement de la signature ET (alerte `suricata:eve`) :```spl
index=main sourcetype=suricata:eve event_type=alert
  alert.signature="ET WEB_SPECIFIC_APPS Kestra Unauthenticated Remote Code Execution (CVE-2026-53576)"
| stats count min(_time) as first_seen max(_time) as last_seen
        values(src_ip) as source_ips
  by dest_ip dest_port
| eval first_seen=strftime(first_seen,"%Y-%m-%d %H:%M:%S")
| eval last_seen=strftime(last_seen,"%Y-%m-%d %H:%M:%S")

det01-network-et-signature

  1. Détection 02 - la règle de redirection vers le réseau de Falco, détaillée par conteneur. L'IP source passe de l'adresse bridge du conteneur Kestra (172.17.0.3, RCE primaire sur le port 4489) à l'hôte lui-même (172.66.66.67, escalade sur le port 4491) - l'escalade conteneur-vers-hôte rendue visible - tandis que la destination reste constante : l'écouteur de l'attaquant à 172.66.66.125 : det02-falco-redirect-revshell

  2. Détection 03 - enregistrements EXECVE d'auditd de curl --unix-socket docker.sock : det03-auditd-docker-sock

  3. Détection 04 - connexion SSH root dans journald, empreinte de clé capturée : det04-ssh-root-login

  4. Détection 05 - la balise cron en fichier caché (/usr/local/bin/.sysmon) s'exécutant en root selon une cadence exacte de 5 minutes : 79 intervalles distincts sur ~6,5 heures (04:30-11:00 EDT). Le seuil distinct_intervals >= 3 est ce qui distingue une balise périodique d'une tâche cron ponctuelle : det05-cron-beacon

Les cinq déployées comme recherches enregistrées planifiées (*/5 * * * *) : saved-searches-scheduled

Preuve que le planificateur cron les exécute réellement, et pas seulement qu'elles existent : les cinq se sont exécutées 54 fois. La Détection 03 s'est déclenchée une fois (l'abus de la socket docker, 06:35 EDT) et la Détection 05 s'est déclenchée 53 fois (la balise cron toujours active). Les Détections 01/02/04 affichent 0 déclenchement car ce sont des événements historiques ponctuels (requête d'exploit, RCE, connexion SSH) qui sont sortis de la fenêtre de rétrospection -10m, alors qu'une instance active ou récurrente alerterait encore : scheduler-triggered-alert

6.2 Sigma - règle portable de création de processus hôte

Le motif de reverse-shell /dev/tcp/ - utilisé à la fois dans le RCE primaire et dans le rappel d'escalade. SigmaHQ fournit une règle pour cela : "Suspicious Reverse Shell Command Line" (id 738d9bcf-6999-4fdb-b4ac-3033037db8ab, par Florian Roth chez Nextron Systems), et l'un de ses mots-clés est bash -i >& /dev/tcp/ - exactement ce qui est apparu dans notre capture.

J'ai donc récupéré cette règle sans modification (detections/sigma/lnx_shell_susp_rev_shells.yml) et vérifié son mot-clé par rapport au même événement Falco déjà capturé par la Détection 02. Sigma ne se "déclenche" pas tout seul - c'est une signature portable, pas un moteur en cours d'exécution - mais je peux montrer son mot-clé s'appliquant à de la télémétrie réelle de cette exécution plutôt qu'à un exemple théorique.

Règle publique SigmaHQ "Suspicious Reverse Shell Command Line" - son mot-clé bash -i >& /dev/tcp/ appliqué au même événement réel que la Détection 02 :

sigma-devtcp-match-event

6.3 Falco - règle personnalisée comblant la lacune de la socket docker

detections/falco/docker_socket_abuse.yaml - déployée sur l'instance Falco en production et confirmée en déclenchement sur un rejeu réel, 2026-09-24T10:27:29Z.

Le jeu de règles par défaut de Falco ne couvre pas cela. Détecter un processus accédant à /var/run/docker.sock n'est pas une idée nouvelle - les propres exemples Falco de Sysdig le font en surveillant open_write sur le chemin de la socket - mais aucune règle de ce type n'existe dans l'ensemble livré/par défaut (le projet a une demande ouverte pour une telle règle, falcosecurity/falco #2940), et la seule règle par défaut adjacente ne détecte que les CLI docker/kubectl, pas un curl --unix-socket brut.

Le hic : cette approche standard open_write-sur-la-socket ne s'est pas déclenchée dans cette configuration - avec modern_bpf et un miroir passif, la socket est atteinte via connect(), pas open(). Je fais donc correspondre la ligne de commande du processus au moment de sa création (spawned_process + proc.cmdline contains "docker.sock"), la même classe d'événements que Falco gère déjà de manière fiable ici. Elle se déclenche deux fois par appel - le wrapper shell et la feuille curl - avec le contexte complet :``` priority=Critical, container_id=d417e027d444, image=kestra/kestra:v1.3.20, user=root, cmdline="curl -s --unix-socket /var/run/docker.sock http://localhost/version", tags=[T1610, T1611]

root@kitploit:~
Règle Falco personnalisée déclenchée - « Unexpected Process Accessing Docker Socket » (T1610/T1611) :
![det17-falco-custom-docker-sock](https://assets.kitploit.com/production/public/readmes/56348/6fa1b11a7c8c6a3c8619ac95a2950cf1f326df0ef1651324aa0b4f80db389a12/8ac8f1a2c5184a84ba2af0c9c85c3112506e19c62b0734105e5b74c90e4d8078-display-v1.webp)

Règles Falco standard déclenchées - l'ensemble par défaut ne contient aucune règle docker-socket, c'est là la lacune :
![falco-rule-inventory](https://assets.kitploit.com/production/public/readmes/56348/5da667ac881b32f6bc633e58b4d00a08ea22cf3a5000b3ae8522cb3937b051a4/3fe1201158632dc6aef5257bf3620e83e087814f3da0108f7d2ef04fd5088ec9-display-v1.webp)

### 6.4 Suricata - couverture publique existante, règle personnalisée différée

La couche réseau de l'exploit principal est déjà couverte par une signature publique Emerging Threats, `ET WEB_SPECIFIC_APPS Kestra Unauthenticated Remote Code Execution (CVE-2026-53576)`, qui s'est déclenchée sur du trafic réel durant la phase 2.

Ci-dessous figure le même contournement observé au niveau de la couche réseau, et j'ai construit la requête à partir du motif de vulnérabilité. Chaque requête dont le chemin se termine par `/configs` sur un endpoint `flows` ou `executions` a renvoyé `200`, tandis qu'une route normale (`/flows/search`) a renvoyé le `401` attendu.

La colonne `Attacker IP (XFF)` correspond à l'origine HTTP réelle, lue depuis l'en-tête `X-Forwarded-For` : `10.10.10.106`, mon Mac exécutant Burp. Le `src_ip` propre à Suricata ne voit que le saut du reverse-proxy (`172.66.66.1`). Il s'agit d'une machine différente de la box Kali `172.66.66.125` qui a ensuite capté les reverse shells et effectué la connexion SSH, donc l'exploit HTTP et les callbacks provenaient d'hôtes distincts.

![suricata-raw-post-http](https://assets.kitploit.com/production/public/readmes/56348/d713d35f0d858af8dd5d477a9929ed6d07e55bce5e9e43946dfe1fb1cca73699/975155c6fe8a39a0de715497a7e5a659b17641a7ed4ed2d7f4d7bfcd02842398-display-v1.webp)

### 6.5 Tableau de bord

`cve_2026_53576_kill_chain` est déployé sur Splunk et contient : les KPI principaux (couches de détection, techniques ATT&CK, détections déployées, mécanismes de persistance), un graphique à colonnes de la chronologie d'attaque coloré par couche de télémétrie, une carte de kill-chain MITRE ATT&CK avec un décompte de preuves en direct par étape, la chronologie corrélée réseau-et-hôte de la §5.4, la couverture de détection par recherche enregistrée planifiée, la répartition de la technique docker-socket, et une table d'indicateurs de compromission extraite en direct des logs.

- KPI, le graphique de chronologie d'attaque, et le début de la carte ATT&CK :
![dashboard-kill-chain-1](https://assets.kitploit.com/production/public/readmes/56348/e0fe4ac39189ed9b3c4741ab5c8fb3276484a482dcb476b11e14381700647afd/5f586685c93346bd1df629ea4092a5a61146d0a32c2d258dfba620b3dd984161-display-v1.webp)

- Le reste de la carte ATT&CK, la kill-chain corrélée, la couverture de détection à côté de la répartition docker-socket, et la table IOC :
![dashboard-kill-chain-2](https://assets.kitploit.com/production/public/readmes/56348/f4479575da20da3a4028a0abaac1c3b565c5b034582b157730599a15601e195f/8b18bbbdd05d8955c38f457d71029f3b2be53b409959a7560c0f8f08f038cbaf-display-v1.webp)


## 7. Mapping ATT&CK

| Tactic               | Technique                                                 | Evidence                                                                                                |
| -------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Initial Access       | T1190 - Exploit Public-Facing Application                 | Control/plant/execute requests (§5.1); Suricata ET signature fire, §5.4                                 |
| Execution            | T1059.004 - Command and Scripting Interpreter: Unix Shell | Kestra `Commands` task, host process tree (`07-kestra-process-tree.png`); Falco rule, §6.1 Detection 02 |
| Command and Control  | T1095 - Non-Application Layer Protocol                    | Raw `/dev/tcp/` TCP reverse shell - no application-layer C2 framing used                                |
| Discovery            | T1613 - Container and Resource Discovery                  | Docker image enumeration via the socket (`10-docker-images-enum.png`)                                   |
| Privilege Escalation | T1610 - Deploy Container                                  | Sibling container create/start via the Docker API (`11`–`15`); auditd EXECVE records, §6.1 Detection 03 |
| Privilege Escalation | T1611 - Escape to Host                                    | Host-root confirmation, hostname/kernel match (`16`, `17`)                                              |
| Persistence          | T1098.004 - Account Manipulation: SSH Authorized Keys     | Key implant to `/root/.ssh/authorized_keys` (`23`)                                                      |
| Lateral Movement     | T1021.004 - Remote Services: SSH                          | Successful key-based root login (`24`); journald, §6.1 Detection 04                                     |
| Persistence          | T1053.003 - Scheduled Task/Job: Cron                      | Cron beacon, confirmed firing on schedule (`25`); §6.1 Detection 05                                     |

## 8. Remédiation & durcissement

**Correctif principal.** Mettre à niveau vers Kestra 1.0.45 (branche 1.0.x) ou 1.3.21+ (branche 1.3.x). Cela suffit à lui seul à fermer le contournement d'authentification - l'étape 1 de cet exercice - et c'est
le seul correctif qui ne nécessite aucun contrôle compensatoire supplémentaire une fois appliqué. Voir [GHSA-2q47-568g-9h4f](https://github.com/kestra-io/kestra/security/advisories/GHSA-2q47-568g-9h4f).

**Si un patch immédiat n'est pas possible**, contrôles compensatoires, par ordre d'impact :

1. **Application via reverse-proxy** - comme le filtre vulnérable n'échoue que sur le suffixe de chemin brut, un reverse proxy devant Kestra (Caddy, dans ce lab) peut appliquer indépendamment l'authentification sur tout chemin correspondant à `/api/v1/*/(flows|executions)/.*` quelle que soit sa terminaison, fermant le contournement à une couche que le bug applicatif ne peut atteindre.
   
2. **Restreindre le montage du socket Docker** - cela ferme entièrement les étapes 2–3, indépendamment du patch Kestra. Ne pas monter en bind `/var/run/docker.sock` dans le conteneur Kestra. Si le runner de tâche `Docker` est réellement nécessaire, utiliser un proxy de socket à portée limitée (par ex. `docker-socket-proxy`) qui autorise uniquement des appels API spécifiques au lieu d'accorder un accès complet au démon - un accès complet au socket équivaut à un root sur l'hôte, comme démontré en §5.2.
   
3. **Durcissement SSH** - le premier mécanisme de la chaîne de persistance dépendait du fait que root soit joignable via SSH par clé. `PermitRootLogin no` (ou exiger un bastion/MFA pour root) aurait bloqué ce chemin de persistance spécifique indépendamment du RCE initial - à faire indépendamment de ce CVE.
   
4. **Couverture de détection intérimaire** - les recherches de corrélation Splunk et la règle Falco personnalisée, toutes déployées et vérifiées durant cet exercice, fournissent la couverture intérimaire pour les étapes de cette chaîne spécifique pendant que le patch est planifié.

**Hygiène post-incident**, si ce schéma est découvert déjà exploité : traiter comme une compromission complète de l'hôte, pas seulement comme une compromission applicative - faire tourner chaque identifiant et secret auxquels l'instance Kestra avait accès (entrées du KV store, identifiants de connexion pour les systèmes en aval), pas seulement l'accès propre à Kestra.

**Statut in-the-wild.** `CVE-2026-49869`, l'avis jumeau pour ce même bug, est listé dans le catalogue KEV de la CISA et lié à des campagnes observées de crypto-mining et de vol d'identifiants cloud. `CVE-2026-53576` (ce rapport) n'a pas de fiche KEV propre, mais il s'agit de la même vulnérabilité. Les outils de gestion des vulnérabilités qui ne suivent qu'un seul des deux numéros CVE peuvent sous-déclarer l'exposition.


## 9. Références

- Kestra Security Advisory - [GHSA-2q47-568g-9h4f](https://github.com/kestra-io/kestra/security/advisories/GHSA-2q47-568g-9h4f) (CVE-2026-53576, source principale de ce rapport)
- Avis jumeau - [GHSA-5vc5-wxxq-3fjx](https://github.com/kestra-io/kestra/security/advisories/GHSA-5vc5-wxxq-3fjx) (CVE-2026-49869, bug identique, listé au KEV de la CISA)
- CVE.org - [CVE-2026-53576](https://www.cve.org/CVERecord?id=CVE-2026-53576), [CVE-2026-49869](https://www.cve.org/CVERecord?id=CVE-2026-49869)
- CISA Known Exploited Vulnerabilities Catalog - https://www.cisa.gov/known-exploited-vulnerabilities-catalog (entrée CVE-2026-49869, ajoutée le 2026-09-02)
- Versions corrigées, toutes deux confirmées existantes et taguées - [v1.3.21](https://github.com/kestra-io/kestra/releases/tag/v1.3.21) (2026-06-02, le changelog mentionne explicitement « potential authentication bypass in the authentication filter »), [v1.0.45](https://github.com/kestra-io/kestra/releases/tag/v1.0.45) (2026-06-03)
- Technique d'escalade via le socket Docker (§5.2) - [HackTricks: Docker Breakout / Privilege Escalation](https://hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html), [Trail of Bits: Understanding Docker Container Escapes](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/), [MindPatch: Docker Escape](https://www.mindpatch.net/posts/docker-escape/)
- Règle Sigma (§6.2) - la règle publique que j'ai utilisée plutôt que d'écrire la mienne : [SigmaHQ « Suspicious Reverse Shell Command Line »](https://github.com/SigmaHQ/sigma/blob/master/rules/linux/builtin/lnx_shell_susp_rev_shells.yml) (id `738d9bcf-6999-4fdb-b4ac-3033037db8ab`, Florian Roth / Nextron Systems), utilisée sous la [Detection Rule License 1.1](https://github.com/SigmaHQ/sigma/blob/master/LICENSE.Detection.Rules.md)
- Règle Falco (§6.3) - la lacune docker-socket fait l'objet d'une demande ouverte en amont ([falcosecurity/falco #2940](https://github.com/falcosecurity/falco/issues/2940)) ; la règle personnalisée suit le modèle des [exemples Docker + Falco de Sysdig](https://www.sysdig.com/blog/docker-falco-security)
Télécharger l’outil
2026-09-22 au 09-24Reproduction en laboratoire, construction de la détection et validation multicouche