Escalada de privilegios en el planificador Cronicle (CVE-2026-85076)
Un usuario no administrador de Cronicle puede inyectar variables de entorno mediante un evento y ejecutar código en otra tarea. Corregido en 0.9.124.
Resumen
- Un usuario no administrador de Cronicle con privilegios sobre eventos puede añadir claves no declaradas a
params. El ejecutor las convierte en variables de entorno, lo que permite sobrescribirLD_PRELOAD,PATHyNODE_OPTIONS. - Cronicle anterior a 0.9.124 está afectado. La versión 0.9.124 valida las claves de los parámetros con el esquema del plugin. Las versiones posteriores, hasta 0.9.144, añaden más endurecimiento de las sobrescrituras y de la API de tareas.
- El registro CVE de GitHub lo califica CVSS v4.0 9.0 Crítico; el aviso del repositorio, High. No hay explotación en entornos reales reportada, y no está en KEV de la CISA al 7 de noviembre de 2026.
- Actualice a 0.9.144. Si no puede, restrinja la creación y ejecución de eventos a administradores de confianza y audite los eventos almacenados; consulte «¿Cómo lo corrijo?».
¿Estoy afectado?
Está expuesto solo cuando se cumplen todas estas condiciones:
- El servidor ejecuta Cronicle anterior a 0.9.124.
- El atacante tiene una cuenta de Cronicle que no es de administrador.
- Esa cuenta puede crear, editar o ejecutar eventos. El aviso nombra los privilegios
run_events,create_eventsyedit_events. - El atacante puede alcanzar la API o la interfaz web de Cronicle.
Cronicle es un planificador de tareas multiservidor con interfaz web. Ejecuta las tareas como el usuario configurado para el plugin o el evento. Un usuario que no es administrador y puede crear eventos ya puede ejecutar comandos como ese usuario.
Este fallo añade la capacidad de cambiar el entorno de una tarea que pertenece a otro usuario o plugin. Eso incluye las variables que cargan código antes de que empiece la tarea.
| Rama | Versiones afectadas | Versión corregida |
|---|---|---|
| Cronicle | todas las versiones anteriores a 0.9.124 | 0.9.124 (17 de julio de 2026); 0.9.144 (7 de noviembre de 2026) añade más endurecimiento |
El registro CVE enumera todas las versiones anteriores a 0.9.124 como afectadas. La versión 0.9.124 es la corrección de este CVE. Las versiones 0.9.141 a 0.9.144 endurecen rutas relacionadas de sobrescritura de eventos y tareas, así que ejecute 0.9.144.
Compruebe la versión en ejecución con el script de control:
/opt/cronicle/bin/control.sh version
En un contenedor, ejecute el mismo comando dentro:
docker exec <container> /opt/cronicle/bin/control.sh version
Cualquier versión anterior a 0.9.124 está afectada.
¿Cómo lo corrijo?
- Haga una copia de seguridad del directorio de datos y de la configuración de Cronicle.
- Ejecute
/opt/cronicle/bin/control.sh upgrade 0.9.144en cada servidor. Sin un argumento de versión, el comando actualiza a la última versión estable. La actualización conserva los datos, los usuarios y la configuración. - Para un contenedor, descargue la nueva imagen y vuelva a crear el contenedor. Descargar la imagen no reemplaza un contenedor en ejecución.
- Confirme la nueva versión con
/opt/cronicle/bin/control.sh version. - En un clúster, repita la actualización en cada servidor. Cronicle exige la misma configuración en todos los servidores.
Si no puede actualizar, restrinja la creación y ejecución de eventos a administradores de confianza. El aviso nombra run_events, create_events y edit_events como los privilegios que alcanzan el fallo. Quitar esos privilegios a los roles que no son administradores cierra la ruta documentada, pero también elimina una función de la que dependen esos usuarios.
Audite los eventos almacenados para las claves de parámetros que el plugin seleccionado no define. Una clave como ld_preload, node_options o bash_env no forma parte de un esquema de plugin normal. El aviso no publica ningún workaround del proveedor más allá de la actualización.
¿Cuál es la causa raíz?
El ejecutor de tareas copia cada clave del objeto params de un evento en el entorno del proceso hijo. La API de eventos solo comprueba que params es un objeto. Nada compara las claves con los parámetros declarados por el plugin.
En lib/job.js en 0.9.123, la tarea copia los parámetros del evento:
job.params = event.params || {};
Más tarde, cuando arranca el proceso hijo, el ejecutor convierte cada clave a mayúsculas y la escribe en el entorno:
// 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] : '');
});
}
}
La API acepta el objeto de evento tras una simple comprobación de tipo:
if (("params" in event) && (typeof(event.params) != 'object')) {
this.doError('api', "Malformed event parameter: params (must be object)", callback);
return false;
}
Los parámetros del plugin controlados por el administrador se aplican primero. Los parámetros del evento se aplican después, así que una clave que colisiona con una variable definida por el administrador gana. El ejecutor no comprueba que la clave pertenezca al plugin.
El objeto params también se almacena en el evento cuando un atacante lo crea o lo modifica. Eso hace que la inyección sea persistente para los eventos que el atacante puede editar. Para run_event, los parámetros solo se aplican a esa ejecución.
¿Cómo se explota?
- El atacante inicia sesión con una cuenta que no es de administrador y puede crear, editar o ejecutar eventos.
- El atacante crea un evento, o modifica uno existente, y añade una clave no declarada a
params. El ejemplo del aviso usald_preloadcon una ruta a una biblioteca compartida. - El atacante ejecuta el evento, o espera a su programación. El ejecutor convierte la clave a mayúsculas y la escribe en el entorno hijo.
ld_preloadse convierte enLD_PRELOAD, y el cargador dinámico carga la biblioteca del atacante antes del código de la tarea. - El atacante también puede usar
node_options,bash_envopythonpath. El ejemplo en dos pasos del aviso escribe una carga útil de Node.js con un evento. Luego ejecuta un segundo evento como un usuario con más privilegios, connode_optionsdefinido como--require=/tmp/evil.js. - El código inyectado se ejecuta con el usuario y los accesos de la tarea objetivo. El aviso describe el impacto como escalada de privilegios, ejecución de código arbitrario, acceso a datos o interrupción del servicio, según el plugin y el entorno de ejecución.
El aviso publica una solicitud que crea un evento con un parámetro adicional. Adaptado del aviso, el cuerpo de la solicitud tiene esta forma:
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 clave ld_preload no está definida por el plugin. El ejecutor la convierte igualmente en LD_PRELOAD para la tarea.
¿Cómo se desarrolla el ataque?
Un usuario que no es administrador añade una clave que el plugin nunca definió. El ejecutor convierte esa clave en una variable de entorno, y la tarea objetivo carga el código del atacante antes de empezar.
¿Qué puede hacer un atacante?
Un usuario que no es administrador y tiene privilegios sobre eventos puede ejecutar código en el contexto de la tarea de otro evento. El impacto depende del usuario del plugin objetivo y de lo que la tarea pueda alcanzar.
Las condiciones son:
- El atacante tiene una cuenta de Cronicle que no es de administrador.
- La cuenta puede crear, editar o ejecutar eventos.
- El evento objetivo usa un plugin que se ejecuta con más acceso que el atacante, o el atacante necesita cambiar el entorno de esa tarea.
- El atacante puede alcanzar la API o la interfaz web.
- Para una inyección persistente, el atacante puede modificar el evento objetivo. Para una inyección puntual, el atacante puede ejecutarlo con parámetros modificados.
El aviso califica el fallo como High y describe el impacto como escalada de privilegios, ejecución de código arbitrario, acceso a datos o interrupción del servicio. El registro CVE de GitHub lo califica CVSS v4.0 9.0 Crítico. La diferencia proviene del modelo de puntuación y de los requisitos supuestos; ambos se indican aquí.
El fallo no entrega por sí solo la cuenta de administrador de Cronicle ni el usuario root. El código inyectado se ejecuta como el usuario configurado para la tarea. Una tarea que se ejecuta como root, o un plugin que inicia un proceso root, da ese acceso al atacante. El aviso no demuestra una ruta root específica.
¿Por qué funciona la corrección?
La corrección valida cada clave de parámetro de evento con los parámetros declarados por el plugin y solo copia los parámetros declarados en la tarea. El parche de Cronicle añade un asistente requireValidEventParams() y cambia el ejecutor de tareas para usar el esquema del plugin.
El nuevo asistente en 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;
},
El ejecutor de tareas ahora construye job.params a partir del esquema del 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;
}
} );
Después de la corrección, la API rechaza una clave no declarada y no se copia en el entorno. Un parámetro oculto del plugin siempre toma el valor del plugin, así que un evento no puede sobrescribirlo.
Las versiones 0.9.141 a 0.9.144 añaden correcciones relacionadas. La nota de la versión 0.9.144 dice «Prevent script parameter overrides in run_event». La versión 0.9.143 limita las API de actualización de tareas a una lista estricta de propiedades. Son defensas en profundidad para la misma frontera de confianza.
¿Cómo compruebo si estuve comprometido?
Empiece por la versión en ejecución. Un servidor anterior a 0.9.124 está expuesto.
/opt/cronicle/bin/control.sh version
Examine la programación almacenada para los eventos cuyos parámetros contienen claves que el plugin no define. El almacenamiento conserva la programación en el directorio de datos de Cronicle. Una pequeña comprobación de Node.js puede listar las claves:
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(","));}}'
Una coincidencia es una pista, no una prueba. Un plugin puede definir legítimamente un parámetro con uno de esos nombres. Compare las claves con el esquema del plugin.
Examine los registros de las tareas y el registro de Cronicle para entornos hijos o errores que nombren una biblioteca o un require de Node.js inesperado. El ejecutor registra las opciones de arranque del hijo en el nivel de depuración 9. Si la depuración está activada, busque LD_PRELOAD, NODE_OPTIONS o la ruta de un archivo que no reconozca.
grep -Ei 'LD_PRELOAD|NODE_OPTIONS|BASH_ENV|PYTHONPATH' /opt/cronicle/logs/*.log 2>/dev/null
El proveedor no publica indicadores de compromiso. Un intento fallido puede no dejar rastro, y un intento exitoso puede dejar solo la salida de la tarea o un evento modificado.
Cronología
- 3 de abril de 2026: se publica el aviso anterior GHSA-5j3v-cq96-xw6v para la inyección mediante la salida de tarea
update_event, corregido en 0.9.111. - 17 de julio de 2026: el commit
da0228fdy la versión 0.9.124 validan los parámetros de evento con el esquema del plugin. - 2 de septiembre de 2026: se reserva CVE-2026-85076.
- 4 de septiembre de 2026: se publica el aviso GHSA-9w42-v8cj-68gq.
- 5 de noviembre de 2026: se publica el registro CVE a las 20:28 UTC.
- 7 de noviembre de 2026: las versiones 0.9.143 y 0.9.144 añaden más endurecimiento de las sobrescrituras y de la API de tareas.
Preguntas frecuentes
¿Quién puede explotar este fallo?
Un usuario de Cronicle que no es administrador pero puede crear, editar o ejecutar eventos. El aviso nombra los privilegios run_events, create_events y edit_events. Un visitante anónimo no puede explotarlo.
¿Da acceso root?
No por sí solo. El entorno inyectado se aplica al proceso hijo de la tarea, que se ejecuta como el usuario configurado para el plugin o el evento. Una tarea configurada para ejecutarse como root da root al atacante. Una tarea que se ejecuta como un usuario sin privilegios da el acceso de ese usuario.
¿Basta con 0.9.124?
Corrige CVE-2026-85076. Las versiones 0.9.141 a 0.9.144 añaden más endurecimiento de las mismas rutas de sobrescritura de eventos y tareas, incluido «Prevent script parameter overrides in run_event» en 0.9.144. Ejecute 0.9.144.
¿Se explotó en entornos reales?
No se informa de explotación en entornos reales al 7 de noviembre de 2026, y el CVE no está en el catálogo KEV de la CISA. El aviso publica ejemplos de solicitudes, así que la técnica es pública.
¿Qué hago si no puedo actualizar de inmediato?
Quite los privilegios run_events, create_events y edit_events a los roles que no son administradores. Audite los eventos almacenados para las claves de parámetros que el plugin no define. La restricción elimina una función para esos usuarios, así que planifique el cambio con ellos.
¿Cómo sé qué plugin usa un evento?
El registro del evento tiene un campo plugin que nombra el identificador del plugin. La definición del plugin lista sus parámetros. Compare las claves de parámetros del evento con esa lista. El comando de comprobación de «¿Cómo compruebo si estuve comprometido?» muestra el identificador, el título y las claves del evento.
Referencias
- Aviso de GitHub GHSA-9w42-v8cj-68gq, publicado el 4 de septiembre de 2026, con el código vulnerable, los pasos de reproducción y la corrección: https://github.com/jhuckaby/Cronicle/security/advisories/GHSA-9w42-v8cj-68gq (comprobado el 7 de noviembre de 2026)
- Registro CVE-2026-85076, publicado el 5 de noviembre de 2026, CVSS v4.0 9.0 y CWE-863/CWE-74: https://www.cve.org/CVERecord?id=CVE-2026-85076 (comprobado el 7 de noviembre de 2026)
- Commit de Cronicle
da0228fd, «Harden event parameter validation and plugin handling», 17 de julio de 2026: https://github.com/jhuckaby/Cronicle/commit/da0228fdea9bc57c09876c00ea0feda70a0ef110 (comprobado el 7 de noviembre de 2026) - Versión de Cronicle 0.9.124, 17 de julio de 2026: https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.124 (comprobado el 7 de noviembre de 2026)
- Versión de Cronicle 0.9.144, 7 de noviembre de 2026, con la nota «Prevent script parameter overrides in run_event»: https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.144 (comprobado el 7 de noviembre de 2026)
- Versión de Cronicle 0.9.143, 7 de noviembre de 2026, con la lista blanca de la API de actualización de tareas: https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.143 (comprobado el 7 de noviembre de 2026)
- Cronicle
lib/job.jsen v0.9.123, la copia de parámetros al entorno: https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/job.js#L537 (comprobado el 7 de noviembre de 2026) - Cronicle
lib/api.jsen v0.9.123, la comprobación de tipo de los parámetros: https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/api.js#L252 (comprobado el 7 de noviembre de 2026) - Cronicle
lib/job.jsen v0.9.124, la copia de parámetros según el esquema: https://github.com/jhuckaby/Cronicle/blob/v0.9.124/lib/job.js#L227 (comprobado el 7 de noviembre de 2026) - Documentación de línea de comandos de Cronicle,
/opt/cronicle/bin/control.sh versionyupgrade: https://github.com/jhuckaby/Cronicle/blob/v0.9.123/docs/CommandLine.md (comprobado el 7 de noviembre de 2026) - GitHub,
jhuckaby/Cronicle, 5,875 estrellas y 509 forks: https://github.com/jhuckaby/Cronicle (comprobado el 7 de noviembre de 2026) - Docker Hub,
cronicle/cronicle, 132,076 descargas: https://hub.docker.com/r/cronicle/cronicle (comprobado el 7 de noviembre de 2026) - Feed JSON del catálogo de vulnerabilidades explotadas de la CISA, comprobado para CVE-2026-85076 el 7 de noviembre de 2026 (no listado): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (comprobado el 7 de noviembre de 2026)