Élévation de privilèges dans le planificateur Cronicle (CVE-2026-85076)
Un non-administrateur Cronicle peut injecter des variables d'environnement via un événement et exécuter du code dans une autre tâche. Corrigé en 0.9.124.
En bref
- Un utilisateur Cronicle non administrateur disposant de privilèges sur les événements peut ajouter des clés non déclarées à
params. Le lanceur les transforme en variables d'environnement, ce qui permet de remplacerLD_PRELOAD,PATHetNODE_OPTIONS. - Cronicle avant 0.9.124 est concerné. La version 0.9.124 valide les clés de paramètres par rapport au schéma du plugin. Les versions suivantes, jusqu'à 0.9.144, ajoutent d'autres durcissements des remplacements et de l'API des tâches.
- Le dossier CVE de GitHub la note CVSS v4.0 9,0 Critique, l'avis du dépôt High. Aucune exploitation dans la nature n'est signalée, et la CVE n'est pas dans le catalogue KEV de la CISA au 7 novembre 2026.
- Mettez à jour vers 0.9.144. Si vous ne pouvez pas, réservez la création et l'exécution d'événements aux administrateurs de confiance et auditez les événements stockés ; voir « Comment corriger ? ».
Suis-je concerné ?
Vous êtes exposé uniquement lorsque toutes ces conditions sont réunies :
- Le serveur exécute Cronicle avant 0.9.124.
- L'attaquant dispose d'un compte Cronicle qui n'est pas administrateur.
- Ce compte peut créer, modifier ou exécuter des événements. L'avis nomme les privilèges
run_events,create_eventsetedit_events. - L'attaquant peut atteindre l'API ou l'interface web de Cronicle.
Cronicle est un planificateur de tâches multi-serveur avec une interface web. Il exécute les tâches en tant qu'utilisateur configuré pour le plugin ou l'événement. Un non-administrateur qui peut créer des événements peut déjà exécuter des commandes en tant que cet utilisateur.
Cette faille ajoute la possibilité de modifier l'environnement d'une tâche appartenant à un autre utilisateur ou plugin. Cela inclut les variables qui chargent du code avant le démarrage de la tâche.
| Branche | Versions concernées | Version corrigée |
|---|---|---|
| Cronicle | toutes les versions avant 0.9.124 | 0.9.124 (17 juillet 2026) ; 0.9.144 (7 novembre 2026) ajoute d'autres durcissements |
Le dossier CVE liste toutes les versions avant 0.9.124 comme concernées. La version 0.9.124 est la correction de cette CVE. Les versions 0.9.141 à 0.9.144 durcissent des chemins de remplacement d'événements et de tâches liés, donc exécutez 0.9.144.
Vérifiez la version en cours avec le script de contrôle :
/opt/cronicle/bin/control.sh version
Dans un conteneur, exécutez la même commande à l'intérieur :
docker exec <container> /opt/cronicle/bin/control.sh version
Toute version avant 0.9.124 est concernée.
Comment corriger ?
- Sauvegardez le répertoire de données et la configuration de Cronicle.
- Exécutez
/opt/cronicle/bin/control.sh upgrade 0.9.144sur chaque serveur. Sans argument de version, la commande met à jour vers la dernière version stable. La mise à jour conserve les données, les utilisateurs et la configuration. - Pour un conteneur, récupérez la nouvelle image et recréez le conteneur. Le simple téléchargement de l'image ne remplace pas un conteneur en cours d'exécution.
- Confirmez la nouvelle version avec
/opt/cronicle/bin/control.sh version. - Dans un cluster, répétez la mise à jour sur chaque serveur. Cronicle exige la même configuration sur tous les serveurs.
Si vous ne pouvez pas mettre à jour, réservez la création et l'exécution d'événements aux administrateurs de confiance. L'avis nomme run_events, create_events et edit_events comme les privilèges qui atteignent la faille. Retirer ces privilèges aux rôles non administrateurs ferme le chemin documenté, mais supprime aussi une fonctionnalité dont ces utilisateurs dépendent.
Auditez les événements stockés pour les clés de paramètres que le plugin sélectionné ne définit pas. Une clé comme ld_preload, node_options ou bash_env ne fait pas partie d'un schéma de plugin normal. L'avis ne publie aucun contournement du fournisseur au-delà de la mise à jour.
Quelle est la cause racine ?
Le lanceur de tâches copie chaque clé de l'objet params d'un événement dans l'environnement du processus enfant, et l'API des événements vérifie seulement que params est un objet. Rien ne compare les clés aux paramètres déclarés par le plugin.
Dans lib/job.js en 0.9.123, la tâche copie les paramètres de l'événement :
job.params = event.params || {};
Plus tard, au démarrage du processus enfant, le lanceur met chaque clé en majuscules et l'écrit dans l'environnement :
// add plugin params as env vars, expand $INLINE vars (unless escaped)
if (job.params) {
for (var key in job.params) {
child_opts.env[key.toUpperCase()] =
(''+job.params[key]).replace(/(\\?)\$(\w+)/g, function(m_all, m_g1, m_g2) {
return m_g1 ? ('$' + m_g2) : ((m_g2 in child_opts.env) ? child_opts.env[m_g2] : '');
});
}
}
L'API accepte l'objet événement après une simple vérification de type :
if (("params" in event) && (typeof(event.params) != 'object')) {
this.doError('api', "Malformed event parameter: params (must be object)", callback);
return false;
}
Les paramètres du plugin contrôlés par l'administrateur sont appliqués en premier. Les paramètres de l'événement sont appliqués après, donc une clé qui entre en collision avec une variable définie par l'administrateur l'emporte. Le lanceur ne vérifie pas que la clé appartient au plugin.
L'objet params est aussi stocké sur l'événement lorsqu'un attaquant le crée ou le modifie. L'injection devient donc persistante pour les événements que l'attaquant peut modifier. Pour run_event, les paramètres ne s'appliquent qu'à cette exécution.
Comment la faille est-elle exploitée ?
- L'attaquant se connecte avec un compte non administrateur qui peut créer, modifier ou exécuter des événements.
- L'attaquant crée un événement, ou modifie un événement existant, et ajoute une clé non déclarée à
params. L'exemple de l'avis utiliseld_preloadavec un chemin vers une bibliothèque partagée. - L'attaquant exécute l'événement, ou attend sa planification. Le lanceur met la clé en majuscules et l'écrit dans l'environnement enfant.
ld_preloaddevientLD_PRELOAD, et le chargeur dynamique charge la bibliothèque de l'attaquant avant le code de la tâche. - L'attaquant peut aussi utiliser
node_options,bash_envoupythonpath. L'exemple en deux étapes de l'avis écrit une charge utile Node.js avec un événement, puis exécute un second événement en tant qu'utilisateur plus privilégié avecnode_optionsdéfini à--require=/tmp/evil.js. - Le code injecté s'exécute avec l'utilisateur et les accès de la tâche cible. L'avis décrit l'impact comme une élévation de privilèges, une exécution de code arbitraire, un accès aux données ou une perturbation du service, selon le plugin et l'environnement d'exécution.
L'avis publie une requête qui crée un événement avec un paramètre supplémentaire. Adapté de l'avis, le corps de la requête a cette forme :
POST /api/app/create_event/v1
Content-Type: application/json
{
"session_id": "USER_SESSION_ID",
"title": "Legitimate Event",
"enabled": 1,
"category": "general",
"target": "allgrp",
"plugin": "shellplug",
"params": {
"script": "#!/bin/sh\necho hello",
"ld_preload": "/tmp/evil.so"
}
}
La clé ld_preload n'est pas définie par le plugin. Le lanceur la transforme quand même en LD_PRELOAD pour la tâche.
Comment se déroule l'attaque ?
Un non-administrateur ajoute une clé que le plugin n'a jamais définie. Le lanceur transforme cette clé en variable d'environnement, et la tâche cible charge le code de l'attaquant avant de démarrer.
Que peut faire un attaquant ?
Un non-administrateur disposant de privilèges sur les événements peut exécuter du code dans le contexte de la tâche d'un autre événement. L'impact dépend de l'utilisateur du plugin cible et de ce que la tâche peut atteindre.
Les conditions sont :
- L'attaquant dispose d'un compte Cronicle non administrateur.
- Le compte peut créer, modifier ou exécuter des événements.
- L'événement cible utilise un plugin qui s'exécute avec plus d'accès que l'attaquant, ou l'attaquant doit modifier l'environnement de cette tâche.
- L'attaquant peut atteindre l'API ou l'interface web.
- Pour une injection persistante, l'attaquant peut modifier l'événement cible. Pour une injection ponctuelle, l'attaquant peut l'exécuter avec des paramètres modifiés.
L'avis note la faille High et décrit l'impact comme une élévation de privilèges, une exécution de code arbitraire, un accès aux données ou une perturbation du service. Le dossier CVE de GitHub la note CVSS v4.0 9,0 Critique. La différence vient du modèle de notation et des prérequis supposés ; les deux sont indiqués ici.
La faille ne donne pas à elle seule le compte administrateur de Cronicle ni l'utilisateur root. Le code injecté s'exécute en tant qu'utilisateur configuré pour la tâche. Une tâche qui s'exécute en tant que root, ou un plugin qui démarre un processus root, donne cet accès à l'attaquant. L'avis ne démontre pas de chemin root spécifique.
Pourquoi le correctif fonctionne-t-il ?
Le correctif valide chaque clé de paramètre d'événement par rapport aux paramètres déclarés par le plugin et ne copie dans la tâche que les paramètres déclarés. Le correctif de Cronicle ajoute un assistant requireValidEventParams() et modifie le lanceur de tâches pour utiliser le schéma du plugin.
Le nouvel assistant dans lib/api.js :
requireValidEventParams: function(params, plugin, callback) {
// Only allow Event params that are defined by the admin-controlled Plugin.
// This prevents arbitrary param keys from becoming child environment variables.
var allowed_params = Object.create(null);
if (plugin.params) plugin.params.forEach( function(param) {
allowed_params[ param.id ] = true;
} );
for (var key in params) {
if (!Object.prototype.hasOwnProperty.call(params, key)) continue;
if (!allowed_params[key]) {
this.doError('event', "Unknown Plugin parameter: " + key, callback);
return false;
}
}
return true;
},
Le lanceur de tâches construit désormais job.params à partir du schéma du plugin :
- job.params = event.params || {};
job.now = event.now || Tools.timeNow(true);
job.event_title = event.title;
job.plugin_title = plugin.title;
...
- // plugin params may have changed outside of event,
- // so recopy missing / hidden ones
+ // Only allow params defined by the admin-controlled Plugin schema,
+ // so arbitrary Event / API keys cannot become child environment variables.
+ var supplied_params = Tools.isaHash(event.params) ? event.params : {};
+ job.params = {};
+
+ // Plugin params may have changed outside of the Event, so recopy
+ // missing values, and always use the trusted value for hidden params.
if (plugin.params) plugin.params.forEach( function(param) {
- if (!(param.id in job.params) || (param.type == 'hidden')) {
+ if (
+ (param.type != 'hidden') &&
+ Object.prototype.hasOwnProperty.call(supplied_params, param.id)
+ ) {
+ job.params[ param.id ] = supplied_params[ param.id ];
+ }
+ else {
job.params[ param.id ] = param.value;
}
} );
Après le correctif, une clé non déclarée est rejetée par l'API et n'est pas copiée dans l'environnement. Un paramètre de plugin masqué prend toujours la valeur du plugin, donc un événement ne peut pas le remplacer.
Les versions 0.9.141 à 0.9.144 ajoutent des corrections liées. La note de version 0.9.144 indique « Prevent script parameter overrides in run_event ». La version 0.9.143 limite les API de mise à jour des tâches à une liste stricte de propriétés. Ce sont des défenses en profondeur pour la même frontière de confiance.
Comment vérifier si j'ai été compromis ?
Commencez par la version en cours. Un serveur avant 0.9.124 est exposé.
/opt/cronicle/bin/control.sh version
Examinez la planification stockée pour les événements dont les paramètres contiennent des clés que le plugin ne définit pas. Le stockage conserve la planification dans le répertoire de données de Cronicle. Une petite vérification Node.js peut lister les clés :
node -e 'const fs=require("fs"),p="/opt/cronicle/data/global/schedule";for(const f of fs.readdirSync(p)){const j=JSON.parse(fs.readFileSync(p+"/"+f));for(const e of j.items||[]){const k=Object.keys(e.params||{});if(k.some(x=>/^(ld_preload|node_options|bash_env|pythonpath|path)$/i.test(x)))console.log(e.id,e.title,k.join(","));}}'
Une correspondance est une piste, pas une preuve. Un plugin peut légitimement définir un paramètre portant l'un de ces noms. Comparez les clés avec le schéma du plugin.
Examinez les journaux des tâches et le journal de Cronicle pour les environnements enfants ou les erreurs qui nomment une bibliothèque ou un require Node.js inattendu. Le lanceur journalise les options de démarrage enfant au niveau de débogage 9. Si la journalisation de débogage est activée, recherchez LD_PRELOAD, NODE_OPTIONS ou le chemin d'un fichier que vous ne reconnaissez pas.
grep -Ei 'LD_PRELOAD|NODE_OPTIONS|BASH_ENV|PYTHONPATH' /opt/cronicle/logs/*.log 2>/dev/null
Le fournisseur ne publie aucun indicateur de compromission. Une tentative échouée peut ne laisser aucune trace, et une tentative réussie peut ne laisser que la sortie de la tâche ou un événement modifié.
Chronologie
- 3 avril 2026 : l'avis antérieur GHSA-5j3v-cq96-xw6v pour l'injection via la sortie de tâche
update_eventest publié et corrigé en 0.9.111. - 17 juillet 2026 : le commit
da0228fdet la version 0.9.124 valident les paramètres d'événement par rapport au schéma du plugin. - 2 septembre 2026 : la CVE-2026-85076 est réservée.
- 4 septembre 2026 : l'avis GHSA-9w42-v8cj-68gq est publié.
- 5 novembre 2026 : le dossier CVE est publié à 20:28 UTC.
- 7 novembre 2026 : les versions 0.9.143 et 0.9.144 ajoutent d'autres durcissements des remplacements et de l'API des tâches.
FAQ
Qui peut exploiter cette faille ?
Un utilisateur Cronicle qui n'est pas administrateur mais peut créer, modifier ou exécuter des événements. L'avis nomme les privilèges run_events, create_events et edit_events. Un visiteur anonyme ne peut pas l'exploiter.
Donne-t-elle un accès root ?
Pas à elle seule. L'environnement injecté s'applique au processus enfant de la tâche, qui s'exécute en tant qu'utilisateur configuré pour le plugin ou l'événement. Une tâche configurée pour s'exécuter en tant que root donne root à l'attaquant. Une tâche qui s'exécute en tant qu'utilisateur non privilégié donne l'accès de cet utilisateur.
La version 0.9.124 suffit-elle ?
Elle corrige la CVE-2026-85076. Les versions 0.9.141 à 0.9.144 ajoutent d'autres durcissements des mêmes chemins de remplacement d'événements et de tâches, dont « Prevent script parameter overrides in run_event » en 0.9.144. Exécutez 0.9.144.
La faille a-t-elle été exploitée dans la nature ?
Aucune exploitation dans la nature n'est signalée au 7 novembre 2026, et la CVE n'est pas dans le catalogue KEV de la CISA. L'avis publie des exemples de requêtes, donc la technique est publique.
Que faire si je ne peux pas mettre à jour tout de suite ?
Retirez les privilèges run_events, create_events et edit_events des rôles non administrateurs. Auditez les événements stockés pour les clés de paramètres que le plugin ne définit pas. La restriction supprime une fonctionnalité pour ces utilisateurs, donc planifiez le changement avec eux.
Comment savoir quel plugin un événement utilise ?
L'enregistrement de l'événement a un champ plugin qui nomme l'identifiant du plugin. La définition du plugin liste ses paramètres. Comparez les clés de paramètres de l'événement avec cette liste. La commande de vérification de « Comment vérifier si j'ai été compromis ? » affiche l'identifiant, le titre et les clés de l'événement.
Références
- Avis GitHub GHSA-9w42-v8cj-68gq, publié le 4 septembre 2026, avec le code vulnérable, les étapes de reproduction et le correctif : https://github.com/jhuckaby/Cronicle/security/advisories/GHSA-9w42-v8cj-68gq (vérifié le 7 novembre 2026)
- Dossier CVE-2026-85076, publié le 5 novembre 2026, CVSS v4.0 9.0 et CWE-863/CWE-74 : https://www.cve.org/CVERecord?id=CVE-2026-85076 (vérifié le 7 novembre 2026)
- Commit Cronicle
da0228fd, « Harden event parameter validation and plugin handling », 17 juillet 2026 : https://github.com/jhuckaby/Cronicle/commit/da0228fdea9bc57c09876c00ea0feda70a0ef110 (vérifié le 7 novembre 2026) - Version Cronicle 0.9.124, 17 juillet 2026 : https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.124 (vérifié le 7 novembre 2026)
- Version Cronicle 0.9.144, 7 novembre 2026, avec la note « Prevent script parameter overrides in run_event » : https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.144 (vérifié le 7 novembre 2026)
- Version Cronicle 0.9.143, 7 novembre 2026, avec la liste blanche de l'API de mise à jour des tâches : https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.143 (vérifié le 7 novembre 2026)
- Cronicle
lib/job.jsen v0.9.123, la copie des paramètres vers l'environnement : https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/job.js#L537 (vérifié le 7 novembre 2026) - Cronicle
lib/api.jsen v0.9.123, la vérification de type des paramètres : https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/api.js#L252 (vérifié le 7 novembre 2026) - Cronicle
lib/job.jsen v0.9.124, la copie des paramètres selon le schéma : https://github.com/jhuckaby/Cronicle/blob/v0.9.124/lib/job.js#L227 (vérifié le 7 novembre 2026) - Documentation de ligne de commande de Cronicle,
/opt/cronicle/bin/control.sh versionetupgrade: https://github.com/jhuckaby/Cronicle/blob/v0.9.123/docs/CommandLine.md (vérifié le 7 novembre 2026) - GitHub,
jhuckaby/Cronicle, 5 875 étoiles et 509 forks : https://github.com/jhuckaby/Cronicle (vérifié le 7 novembre 2026) - Docker Hub,
cronicle/cronicle, 132 076 téléchargements : https://hub.docker.com/r/cronicle/cronicle (vérifié le 7 novembre 2026) - Flux JSON du catalogue des vulnérabilités exploitées de la CISA, vérifié pour CVE-2026-85076 le 7 novembre 2026 (non listée) : https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (vérifié le 7 novembre 2026)