当社のAIエンジンNeutronが、UCバークレーのCyberGymベンチマークで97.1%のスコアを記録しました。 詳細を見る

ディープダイブ

ディープダイブ

Cronicle タスクスケジューラの特権昇格(CVE-2026-85076)

Cronicle の非管理者ユーザーがイベントのパラメータ経由で環境変数を注入し、別のジョブのコンテキストでコードを実行できます。0.9.124 で修正されました。

要点

  • イベント権限を持つ 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 の Critical と評価し、リポジトリのアドバイザリは 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 より前のバージョンが影響を受けます。

修正方法

  1. Cronicle のデータディレクトリと設定をバックアップします。
  2. 各サーバーで /opt/cronicle/bin/control.sh upgrade 0.9.144 を実行します。バージョン引数を省略すると、最新の安定版に更新されます。更新はデータ、ユーザー、設定を保持します。
  3. コンテナの場合は、新しいイメージを取得してコンテナを再作成します。イメージを取得するだけでは実行中のコンテナは置き換わりません。
  4. /opt/cronicle/bin/control.sh version で新しいバージョンを確認します。
  5. クラスタでは、すべてのサーバーで更新を繰り返します。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 の場合、パラメータはその実行にのみ適用されます。

どのように悪用されますか?

  1. 攻撃者は、イベントを作成、編集、または実行できる非管理者アカウントでサインインします。
  2. 攻撃者はイベントを作成するか既存のイベントを更新し、params に未宣言のキーを追加します。アドバイザリの例では、共有ライブラリへのパスを指定した ld_preload を使用します。
  3. 攻撃者はイベントを実行するか、スケジュールを待ちます。ジョブランナーはキーを大文字にして子環境に書き込みます。ld_preload は LD_PRELOAD になり、動的ローダーはジョブ自身のコードより先に攻撃者のライブラリを読み込みます。
  4. 攻撃者は node_options、bash_env、pythonpath も使用できます。アドバイザリの 2 段階の例では、1 つのイベントで Node.js のペイロードを書き込み、次により強い権限のユーザーとして実行される 2 つ目のイベントで node_options を --require=/tmp/evil.js に設定します。
  5. 注入されたコードは対象ジョブのユーザーとアクセス権で実行されます。アドバイザリは影響を特権昇格、任意コード実行、データアクセス、サービス妨害と説明し、プラグインと実行環境に依存するとしています。

アドバイザリは追加パラメータ付きでイベントを作成するリクエストを公開しています。アドバイザリから適応したリクエスト本文の形式は次のとおりです。

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 に変換します。

攻撃の流れは?

非管理者がプラグインで定義されていないキーを追加し、ジョブランナーがそのキーを環境変数に変換し、対象ジョブが開始前に攻撃者のコードを読み込みます。

flowchart LR A["非管理者がイベントを作成または実行"] --> B["params に ld_preload または node_options を含める"] B --> C["API は params がオブジェクトであることだけを確認"] C --> D["job.params がすべてのキーをコピー"] D --> E["job.js が各キーを大文字にして child_opts.env に書き込む"] E --> F["対象ジョブが LD_PRELOAD または NODE_OPTIONS 付きで起動"] F --> G["攻撃者のコードがジョブのユーザーとして実行"]

攻撃者に何ができますか?

イベント権限を持つ非管理者は、別のイベントのジョブのコンテキストでコードを実行できます。影響は対象プラグインのユーザーと、ジョブが到達できる範囲に依存します。

条件は次のとおりです。

  • 攻撃者が管理者ではない Cronicle アカウントを持っている。
  • そのアカウントがイベントを作成、編集、または実行できる。
  • 対象イベントが、攻撃者より強いアクセス権で実行されるプラグインを使用している。または、攻撃者がそのジョブの環境を変更する必要がある。
  • 攻撃者が API または Web インターフェースに到達できる。
  • 永続的な注入では、攻撃者が対象イベントを更新できる。単発の注入では、変更したパラメータで実行できる。

アドバイザリは脆弱性を High と評価し、影響を特権昇格、任意コード実行、データアクセス、サービス妨害と説明しています。GitHub の CVE レコードは CVSS v4.0 9.0 の Critical と評価しています。差はスコアリングモデルと想定される前提条件によるもので、両方をここに示します。

この脆弱性だけでは 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 のさらなる強化を追加します。

修正はアドバイザリの 7 週間前、CVE レコードの 4 か月前に出荷されました。
図 1:0.9.124 の修正からアドバイザリ、CVE レコード、11 月の強化リリースまで。出典:Cronicle のリリース、アドバイザリ、CVE レコード、2026年11月7日確認

よくある質問

誰がこの脆弱性を悪用できますか?

管理者ではないが、イベントを作成、編集、または実行できる 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 の権限を外します。保存されたイベントを、プラグインが定義していないパラメータキーについて監査します。制限はそれらのユーザーから機能を奪うため、変更を彼らと計画してください。

イベントがどのプラグインを使用しているかはどう分かりますか?

イベントレコードにはプラグイン ID を名指す plugin フィールドがあります。プラグイン定義はそのパラメータを一覧します。イベントのパラメータキーをその一覧と比較してください。「侵害されたかどうかを確認するには?」のチェックコマンドはイベント 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.js v0.9.123、パラメータから環境へのコピー:https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/job.js#L537(2026年11月7日確認)
  • Cronicle lib/api.js v0.9.123、パラメータの型チェック:https://github.com/jhuckaby/Cronicle/blob/v0.9.123/lib/api.js#L252(2026年11月7日確認)
  • Cronicle lib/job.js v0.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日確認)