
End-to-end reproduction and cross-layer detection of CVE-2026-53576, the unauthenticated RCE in Kestra - taken past the base PoC to show how a common Docker-socket misconfiguration turns container-root into full host compromise.
Kestraは、オープンソースのワークフローオーケストレーションプラットフォームであり、リクエストに認証情報が必要かどうかを、URLが/configsで終わるかどうかで判断する認証フィルターを出荷していました。これは、1つの無害な公開エンドポイントを公開するためのチェックでした。
このチェックは文字列の末尾のみを確認していたため、URLがたまたまそのように終わるリクエストはすべて認証を完全にスキップしました。これには、任意のコードを作成・実行するエンドポイントも含まれます。 その結果: ネットワーク経由で脆弱なKestraインスタンスに到達できる者は誰でも、認証情報ゼロ、フィッシングなし、パスワード推測なし、権限昇格ステップ不要で、rootとしてコマンドを実行できます。
この演習では、その単一のギャップをセルフホストのラボインスタンスに対してエンドツーエンドで実行しました:
すべての段階は、防御側のテレメトリ (ネットワークIDS、ホストランタイムセキュリティ、Linux audit、およびシステムログ) に対してキャプチャされました。
未パッチの場合のビジネスへの影響: Kestraを実行しているホストの完全な侵害であり、アプリケーションだけでなく、そのホストから到達可能な他のすべてのものにも及びます。
修正: Kestra 1.0.45 / 1.3.21以降にアップグレードしてください (パッチ単体で主要な脆弱性は塞がれますが、昇格と永続化の段階には別個の独立した修正が必要です - アプリケーションコンテナにDockerソケットをマウントしないでください。セクション8を参照)。
本レポートは、Kestra ≤1.3.20におけるCVSS 10.0の未認証リモートコード実行脆弱性であるCVE-2026-53576を文書化したものです。これは、パスサフィックス認証バイパス (AuthenticationFilter.java、endsWith("/configs")) に起因します。
ワークフローの名前空間とIDをconfigsと命名した攻撃者は、認証情報なしで、Kestraコンテナ内でrootとして、任意のシェルコマンドを作成・実行できます。1.0.45および1.3.21で修正されました。同一のバグはCVE-2026-49869としても独立に報告されており、これが実際の野放しの悪用に関連するCISA KEVに掲載された番号です。公開情報源やスキャナーはいずれかを参照する可能性があるため、両方を併記すべきです。
| 脆弱 | Kestra ≤ 1.3.20 (および1.0.x系では1.0.45より前) |
| 修正済み | 1.0.45 / 1.3.21+ |
| CVE | CVE-2026-53576 |
| 双子のアドバイザリ | CVE-2026-49869 - 同一のバグ、CISA KEV掲載 (野放し) |
| 日付 | イベント |
|---|---|
| 2026-06-02 | Kestra 1.3.21リリース。チェンジログが「認証フィルターにおける潜在的な認証バイパス」に言及 |
| 2026-06-03 | Kestra 1.0.45が1.0.x系でリリース |
| 2026-09-02 | CVE-2026-49869 (双子のアドバイザリ) がCISA KEVカタログに追加 |
| 2026-09-22 to 09-24 | 本ラボでの再現、検知構築、およびクロスレイヤー検証 |
セルフホストのホームラボ:
- Debianホスト (ホスト名docker、カーネル6.12.107+deb13-amd64)、
- Docker Engine 29.8.1。
- Kestraは公式のkestra/kestra:v1.3.20イメージとしてデプロイされ、kestra.int.atlasvec.comのCaddyリバースプロキシ経由で到達可能。
- テスト対象のテレメトリスタック: Falco (ランタイム/eBPF、ホストレベル)、
- Suricata 8.0.3 IDS (パッシブSPANミラー)、Splunkへ転送。
何かに触れる前に、ターゲットが実際に脆弱であることと、テレメトリスタックが稼働していることを確認しました。また、攻撃前の状態のスナップショットを取得し、完了後に元に戻せるようにしました。
脆弱なバージョンを実行しているKestra:

コンテナ自体が起動し、到達可能:

ホストにロードされたFalcoのユニット:

Falcoがイベントをアクティブに発行中 (モダンeBPFモード):

Suricataのサービスがアクティブ:

そしてそのeve.jsonがライブイベントを書き込み中:

これは1つではなく、2つの誤りが重なったものです。
第一に、認証フィルターは、リクエストパスの末尾を照合することで、リクエストが認証をスキップできるかどうかを判断します:```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 }
そのチェックは、単一の無害なエンドポイントである公開インスタンス設定を露出させるために存在する。`endsWith` は文字列の末尾だけを読むため、1つの安全なパスと、たまたま同じ末尾を持つ他のあらゆるパスとを区別できない。
第二に、Kestra のルーターはそれらの類似パスを、実際の機密性の高いハンドラーへ依然としてディスパッチする。`/api/v1/main/flows/configs` は flow-create ハンドラーに到達する。`/api/v1/main/executions/configs/configs` は execution ハンドラーに到達する。フィルターは両方を公開として通し、ルーターはそのまま実行する。フローの namespace と ID を `configs` と名付ければ、コード実行エンドポイントは `/configs` で終わるようになり、公開パスのフリーパスを継承する。
キャプチャしたリクエストのペアがそれを明示している。認証情報なしの `GET /api/v1/main/flows/search` は `401` を返す。同じ空の認証ヘッダーを付けた `POST /api/v1/main/flows/configs` は `200` を返し、フローを保存する(§5.1 参照)。
## 5. 攻撃シミュレーション
> 以下の各ステップは、それぞれのスクリーンショットとともにキャプチャされている。すべては、リバースプロキシの背後にある自己ホスト型 Kestra インスタンスに対して、私自身の隔離されたホームラボ内で実行されている。
### 5.1 ステージ 1 - 認証バイパス -> Kestra コンテナ内での root
**コントロール :** 認証情報なしの `GET /api/v1/main/flows/search` → `401 Unauthorized`。
通常のルートで認証が強制されていることを確認する。

_____
**植え付け :** 認証ヘッダーなしで `POST /api/v1/main/flows/configs` を送信する。ボディはフロー YAML で、その `namespace` と `flow id` はどちらも `configs` であり、Process タスクランナーを使用する Commands タスクを含む。サーバーは `200 OK` を返し、フローを保存する。それ以外は保護されたルート上の `/configs` サフィックスが、バイパスを引き起こすものである **(セクション 4 参照)。**

**リッスン & 待機** : テスト目的で nc を使った簡単なリスナーを起動し、リバースシェルを捕まえる。

**トリガー / 実行** `POST /api/v1/main/executions/configs/configs`、認証ヘッダーなし → `200 OK`、新しい実行が作成される。

**BaaaaM :** シェルを奪取したが、忘れないでほしい - 我々は「隔離」されており、`root` ではある **が** Kestra docker コンテナの内部であるため、ここから「脱出できないはず」である。「**そう、それが私の考えだった**」

タスクのコマンドは Kestra JVM プロセスの直接の子として実行され、ホスト側のプロセスツリーで確認され、Kestra 自身の実行ログにより正常に完了している。