
Proof-of-conceptラボ。GitHub Actionsのworkflow dispatchにおけるコマンドインジェクション(CVE-2026-39866)を実証します。教育目的の分析のため、脆弱なバージョンとパッチ適用済みバージョンを並行して実行します。
GitHub Actionsのworkflow dispatchにおけるコマンドインジェクションの概念実証ラボ
CVE-2026-39866は、LawnchairのGitHub Actionsリリースワークフローにおけるコマンドインジェクションのバグです。release_update.ymlファイルは、ユーザー入力を引用符なしでbashに直接渡すため、攻撃者がコマンドを注入できます。このリポジトリは、脆弱なバージョンと修正済みバージョンの両方を並行して実行します。
| フィールド | 値 |
|---|---|
| CVE ID | CVE-2026-39866 |
| GHSA | GHSA-9prc-pp2c-3427 |
| CWE | CWE-78 (OSコマンドに使用される特殊要素の不適切な無害化) |
| 製品 | LawnchairLauncher/lawnchair |
| 影響を受けるバージョン | fcba413より前のすべてのバージョン |
| 修正バージョン | fcba413 |
| 発見者 | Abhay Kumar (@abhayclasher) |
この脆弱性はrelease_update.ymlワークフローファイルに存在します。このアクションはartifactNameパラメータを受け取り、引用符なしでbashコマンドに直接渡します。
- name: Rename artifact
run: mv ${{ inputs.artifactName }}.zip lawnchair.zip
GitHub Actionsは、シェルが実行する前に変数値を文字列に置換します。${{ inputs.artifactName }}の周囲に引用符がないため、シェルはスペースやセミコロンをコマンド区切り文字として解釈します。
攻撃者がartifactNameとしてfile.zip; touch pwned.txt #を指定した場合:
GitHub Actionsが解析する方法:
${{ inputs.artifactName }} → file.zip; touch pwned.txt #
シェルが実行する方法:
mv file.zip; touch pwned.txt #.zip lawnchair.zip
シェルはmv file.zipを実行し、次にtouch pwned.txtを実行し、残りをコメントとして無視します。
GITHUB_TOKENやリリース署名キーなど)を窃取する。git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866
docker compose up -d
サーバー起動後、以下のURLにアクセスしてください:
| URL | 説明 |
|---|---|
http://localhost:3000/workflow/vulnerable | 脆弱なワークフロー — 送信時にコマンドインジェクションが実行されます |
http://localhost:3001/workflow/patched | fcba413パッチが適用された同じワークフロー |
脆弱なバージョンは、注入したコマンドをランナー上で実行します。修正済みバージョンは、入力をリテラル文字列として扱います。
スタンドアロンのPoCも実行できます:
cd poc
node exploit.js
これにより、ポート3000で最小限のサーバーが起動し、完全なアプリケーションコンテキストなしで同じバグを実証します。
1. 攻撃者がartifactName入力パラメータを制御する
2. GitHub Actionsが${{ inputs.artifactName }}をYAMLに展開する
3. runステップがこれをbashに送る
4. bashが引用符なしの特殊文字を認識し、コマンド構文として扱う
5. コマンドがランナー環境の権限で実行される
┌────────────────────────────────────────────────────┐
│ 1. 悪意のある入力: │
│ │
│ artifactName = "file.zip; id #" │
│ │
│ 2. GitHub ActionsがYAMLに置換: │
│ │
│ run: mv file.zip; id #.zip lawnchair.zip │
│ │
│ 3. bashが認識する内容: │
│ - mv file.zip │
│ - id <-- 任意のコマンド │
│ - #.zip lawnchair.zip (無視される) │
│ │
│ 4. シェルが任意のコマンドを実行 │
└────────────────────────────────────────────────────┘
fcba413バージョンでは、補間された変数の周囲に引用符が導入されています:
- name: Rename artifact
run: mv "${{ inputs.artifactName }}.zip" lawnchair.zip
引用符により、シェルが特殊文字を解析するのを防ぎます。ペイロードはリテラルなファイル名文字列として扱われ続けます。
ベストプラクティスとして、環境変数を使用してユーザー入力を完全に分離する方法があります:
- name: Rename artifact
env:
ARTIFACT_NAME: ${{ inputs.artifactName }}
run: mv "$ARTIFACT_NAME.zip" lawnchair.zip
完全なコミットを表示: fcba413
CVE-2026-39866/
├── README.md # このファイル
├── docker-compose.yml # Dockerラボ設定
├── app/
│ ├── Dockerfile # コンテナビルド設定
│ ├── package.json # Node.js依存関係
│ └── server.js # 脆弱版 + 修正版ワークフローランナーシミュレーション
└── poc/
└── exploit.js # スタンドアロンの最小概念実証
Abhay Kumarによって報告されました。このリポジトリはローカルでの教育目的のみを対象としています。脆弱なサーバーを公開ネットワークにデプロイしないでください。
教育目的のみ — 隔離された環境で使用してください