Cronicle 任务调度器权限提升(CVE-2026-85076)
Cronicle 的非管理员用户可以通过事件参数注入环境变量,并在另一个作业的上下文中执行代码。0.9.124 已修复。
TL;DR
- 拥有事件权限的 Cronicle 非管理员用户可以在事件的
params对象中添加未声明的键。作业运行器会将这些键转为大写并写入子进程环境,因此可以覆盖LD_PRELOAD、PATH、NODE_OPTIONS等变量。 - Cronicle 0.9.124 之前受影响。0.9.124 根据插件架构验证参数键。后续版本直到 0.9.144 增加了更多覆盖和作业 API 加固。
- GitHub 的 CVE 记录将其评为 CVSS v4.0 9.0 严重,而仓库公告评为 High。未报告在野利用,截至 2026 年 11 月 7 日该 CVE 不在 CISA KEV 目录中。
- 请升级到 0.9.144。如果无法升级,请将事件创建和执行限制为受信任的管理员,并审计已存储的事件;参见“如何修复?”。
我受影响吗?
只有同时满足以下所有条件时,您才会受影响:
- 服务器运行 Cronicle 0.9.124 之前的版本。
- 攻击者拥有非管理员 Cronicle 账户。
- 该账户可以创建、编辑或运行事件。公告列出了
run_events、create_events和edit_events权限。 - 攻击者可以访问 Cronicle 的 API 或 Web 界面。
Cronicle 是一个带 Web 前端的多服务器任务调度器。它以插件或事件配置的用户身份运行作业。可以创建事件的非管理员已经可以以该用户身份运行命令。此漏洞增加了更改另一个用户或插件拥有的作业环境的能力,包括在作业开始前加载代码的变量。
| 分支 | 受影响版本 | 修复版本 |
|---|---|---|
| Cronicle | 0.9.124 之前的所有版本 | 0.9.124(2026 年 7 月 17 日);0.9.144(2026 年 11 月 7 日)增加进一步加固 |
CVE 记录将 0.9.124 之前的所有版本列为受影响。0.9.124 是该 CVE 的修复版本。0.9.141 到 0.9.144 加固了相关的事件和作业覆盖路径,因此请运行 0.9.144。
使用控制脚本检查运行版本:
/opt/cronicle/bin/control.sh version
在容器中,请在容器内运行同一命令:
docker exec <container> /opt/cronicle/bin/control.sh version
任何低于 0.9.124 的版本都受影响。
如何修复?
- 备份 Cronicle 数据目录和配置。
- 在每台服务器上运行
/opt/cronicle/bin/control.sh upgrade 0.9.144。省略版本参数时,命令会升级到最新稳定版。升级会保留数据、用户和配置。 - 对于容器,请拉取新镜像并重新创建容器。仅拉取镜像不会替换正在运行的容器。
- 使用
/opt/cronicle/bin/control.sh version确认新版本。 - 在集群中,请在每台服务器上重复升级。Cronicle 要求所有服务器使用相同配置。
如果无法升级,请将事件创建和执行限制为受信任的管理员。公告列出了可到达该漏洞的 run_events、create_events 和 edit_events 权限。从非管理员角色移除这些权限会关闭已记录的路径,但也会移除这些用户依赖的功能。
审计已存储的事件,查找所选插件未定义的参数键。ld_preload、node_options 或 bash_env 等键不属于正常插件架构。公告除升级外未发布任何供应商变通方法。
根本原因是什么?
作业运行器将事件 params 对象中的每个键复制到子进程环境,而事件 API 仅验证 params 是对象。没有任何逻辑检查键是否与插件声明的参数匹配。
在 0.9.123 的 lib/job.js 中,作业复制事件参数:
job.params = event.params || {};
随后,当子进程启动时,运行器将每个键转为大写并写入环境:
// 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] : '');
});
}
}
API 仅在类型检查后接受事件对象:
if (("params" in event) && (typeof(event.params) != 'object')) {
this.doError('api', "Malformed event parameter: params (must be object)", callback);
return false;
}
管理员控制的插件参数先应用。事件参数随后应用,因此与管理员设置的变量冲突的键会胜出。运行器不检查该键是否属于插件。
当攻击者创建或更新事件时,params 对象也会存储在事件上。这使得攻击者可编辑的事件中的注入具有持久性。对于 run_event,参数仅适用于该次运行。
如何被利用?
- 攻击者使用可创建、编辑或运行事件的非管理员账户登录。
- 攻击者创建事件或更新现有事件,并向
params添加未声明的键。公告的示例使用ld_preload指向共享库路径。 - 攻击者运行事件或等待其计划。作业运行器将键转为大写并写入子环境。
ld_preload变成LD_PRELOAD,动态加载器在作业自身代码之前加载攻击者的库。 - 攻击者还可以使用
node_options、bash_env或pythonpath。公告的两步示例用一个事件写入 Node.js 载荷,然后以更高权限用户运行第二个事件,并将node_options设为--require=/tmp/evil.js。 - 注入的代码以目标作业的用户和访问权限运行。公告将影响描述为权限提升、任意代码执行、数据访问或服务中断,具体取决于插件和运行时。
公告发布了一个创建带额外参数事件的请求。改编自公告,请求体形式如下:
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"
}
}
ld_preload 键并非插件定义。运行器仍会将其转换为作业的 LD_PRELOAD。
攻击如何展开?
非管理员添加插件从未定义的键,作业运行器将该键转换为环境变量,目标作业在启动前加载攻击者的代码。
攻击者能做什么?
拥有事件权限的非管理员可以在另一个事件作业的上下文中执行代码。影响取决于目标插件的用户以及作业可以访问的范围。
条件是:
- 攻击者拥有非管理员 Cronicle 账户。
- 该账户可以创建、编辑或运行事件。
- 目标事件使用的插件以高于攻击者的访问权限运行,或者攻击者需要更改该作业的环境。
- 攻击者可以访问 API 或 Web 界面。
- 对于持久注入,攻击者可以更新目标事件。对于一次性注入,攻击者可以修改参数后运行。
公告将漏洞评为 High,并将影响描述为权限提升、任意代码执行、数据访问或服务中断。GitHub 的 CVE 记录将其评为 CVSS v4.0 9.0 严重。差异来自评分模型和假设的前提条件;此处同时列出两者。
该漏洞本身不会给出 Cronicle 管理员账户或 root 用户。注入的代码以作业配置的用户身份运行。以 root 运行的作业,或启动 root 进程的插件,会将该访问权限交给攻击者。公告未展示特定的 root 路径。
为什么修复有效?
修复根据插件声明的参数验证每个事件参数键,并且只将已声明的参数复制到作业中。Cronicle 的补丁添加了 requireValidEventParams() 辅助函数,并更改作业运行器以使用插件架构。
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;
},
作业运行器现在根据插件架构构建 job.params:
- 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;
}
} );
修复后,未声明的键会被 API 拒绝,不会复制到环境中。隐藏的插件参数始终采用插件的值,因此事件无法覆盖它。
0.9.141 到 0.9.144 版本增加了相关修复。0.9.144 的发布说明写道“Prevent script parameter overrides in run_event”。0.9.143 将作业更新 API 限制为严格的属性允许列表。这些是针对同一信任边界的纵深防御。
如何检查是否已遭入侵?
首先检查运行版本。低于 0.9.124 的服务器会暴露。
/opt/cronicle/bin/control.sh version
检查已存储的计划中,参数包含插件未定义键的事件。存储将计划保存在 Cronicle 数据目录中。一个小的 Node.js 检查可以列出键:
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(","));}}'
命中是线索,不是证据。插件可以合法定义这些名称之一的参数。请将键与插件架构进行比较。
检查作业日志和 Cronicle 日志中提及意外库或 Node.js require 的子环境或错误。运行器在调试级别 9 记录子进程启动选项。如果启用了调试日志,请搜索 LD_PRELOAD、NODE_OPTIONS 或您不认识的文件的路径。
grep -Ei 'LD_PRELOAD|NODE_OPTIONS|BASH_ENV|PYTHONPATH' /opt/cronicle/logs/*.log 2>/dev/null
供应商未发布入侵指标。失败的尝试可能不留下痕迹,成功的尝试可能只留下作业输出或已更改的事件。
时间线
- 2026 年 4 月 3 日:针对
update_event作业输出注入的先前公告 GHSA-5j3v-cq96-xw6v 发布,并在 0.9.111 中修复。 - 2026 年 7 月 17 日:提交
da0228fd和 0.9.124 版本根据插件架构验证事件参数。 - 2026 年 9 月 2 日:CVE-2026-85076 被预留。
- 2026 年 9 月 4 日:公告 GHSA-9w42-v8cj-68gq 发布。
- 2026 年 11 月 5 日:CVE 记录于 20:28 UTC 发布。
- 2026 年 11 月 7 日:0.9.143 和 0.9.144 版本增加进一步的覆盖和作业 API 加固。
常见问题
谁可以利用此漏洞?
不是管理员但可以创建、编辑或运行事件的 Cronicle 用户。公告列出了 run_events、create_events 和 edit_events 权限。匿名访客无法利用。
会获得 root 访问权限吗?
本身不会。注入的环境应用于作业的子进程,该进程以插件或事件配置的用户身份运行。配置为以 root 运行的作业会给攻击者 root。以非特权用户运行的作业会给出该用户的访问权限。
0.9.124 足够吗?
它修复 CVE-2026-85076。0.9.141 到 0.9.144 版本进一步加固相同的事件和作业覆盖路径,包括 0.9.144 中的“Prevent script parameter overrides in run_event”。请运行 0.9.144。
是否已在野利用?
截至 2026 年 11 月 7 日未报告在野利用,且该 CVE 不在 CISA KEV 目录中。公告发布了请求示例,因此该技术已公开。
如果无法立即升级怎么办?
从非管理员角色移除 run_events、create_events 和 edit_events 权限。审计已存储的事件,查找插件未定义的参数键。该限制会移除这些用户的功能,因此请与他们一起计划更改。
如何知道事件使用哪个插件?
事件记录有一个 plugin 字段,指定插件 ID。插件定义列出其参数。将事件的参数键与该列表进行比较。“如何检查是否已遭入侵?”中的检查命令会输出事件 ID、标题和键。
参考文献
- GitHub 公告 GHSA-9w42-v8cj-68gq,2026 年 9 月 4 日发布,包含易受攻击代码、复现步骤和修复:https://github.com/jhuckaby/Cronicle/security/advisories/GHSA-9w42-v8cj-68gq(2026 年 11 月 7 日检查)
- CVE-2026-85076 记录,2026 年 11 月 5 日发布,CVSS v4.0 9.0 和 CWE-863/CWE-74:https://www.cve.org/CVERecord?id=CVE-2026-85076(2026 年 11 月 7 日检查)
- Cronicle 提交
da0228fd,“Harden event parameter validation and plugin handling”,2026 年 7 月 17 日:https://github.com/jhuckaby/Cronicle/commit/da0228fdea9bc57c09876c00ea0feda70a0ef110(2026 年 11 月 7 日检查) - Cronicle 0.9.124 版本,2026 年 7 月 17 日:https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.124(2026 年 11 月 7 日检查)
- Cronicle 0.9.144 版本,2026 年 11 月 7 日,包含“Prevent script parameter overrides in run_event”说明:https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.144(2026 年 11 月 7 日检查)
- Cronicle 0.9.143 版本,2026 年 11 月 7 日,包含作业更新 API 允许列表:https://github.com/jhuckaby/Cronicle/releases/tag/v0.9.143(2026 年 11 月 7 日检查)
- Cronicle
lib/job.jsv0.9.123,参数到环境的复制:https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/job.js#L537(2026 年 11 月 7 日检查) - Cronicle
lib/api.jsv0.9.123,参数类型检查:https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/api.js#L252(2026 年 11 月 7 日检查) - Cronicle
lib/job.jsv0.9.124,基于架构的参数复制:https://github.com/jhuckaby/Cronicle/blob/v0.9.124/lib/job.js#L227(2026 年 11 月 7 日检查) - Cronicle 命令行文档,
/opt/cronicle/bin/control.sh version和upgrade:https://github.com/jhuckaby/Cronicle/blob/v0.9.123/docs/CommandLine.md(2026 年 11 月 7 日检查) - GitHub,
jhuckaby/Cronicle,5,875 个星标和 509 个复刻:https://github.com/jhuckaby/Cronicle(2026 年 11 月 7 日检查) - Docker Hub,
cronicle/cronicle,132,076 次拉取:https://hub.docker.com/r/cronicle/cronicle(2026 年 11 月 7 日检查) - CISA 已知被利用漏洞目录 JSON 源,2026 年 11 月 7 日检查 CVE-2026-85076(未列出):https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json(2026 年 11 月 7 日检查)