
再現可能なDockerラボ + CVE-2015-3306(ProFTPD mod_copyの認証前任意ファイルコピー)用のraw-socketエクスプロイト — パッチ差分解析の学習演習
完全に再現可能なラボと、CVE-2015-3306 向けに手書きしたエクスプロイト — 脆弱性研究(パッチ差分解析、手動トリガー、rawソケットによるエクスプロイト開発)の学習演習として構築しました。
ProFTPD の mod_copy モジュールは SITE CPFR / SITE CPTO
コマンドペアを実装しており、データをクライアントに転送せずにサーバー側でファイルをコピーします。
バージョン 1.3.5 では、どちらのハンドラーもセッションが認証済みかどうかをチェックしていませんでした。 このモジュールは単に 「ログイン前に SITE コマンドを送る者はいない」 と想定していただけ — この誤った想定が CVE となりました。 認証されていない任意のクライアントが、サーバープロセスが読み取り可能な任意のファイルを、書き込み可能な任意の場所へコピーできました。
修正(コミット 212d54271f、1.3.5a でリリース)は2つのことを行います:
copy_cpfr と copy_cpto の先頭に認証ゲートを追加:
authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
if (authenticated == NULL || *authenticated == FALSE) {
pr_response_add_err(R_530, _("Please login with USER and PASS"));
errno = EPERM;
return PR_ERROR(cmd);
}
CopyEngine on|off ディレクティブを追加 — 以前は、このモジュールを同梱したビルドでは無効化すらできませんでした。完全な unified diff は patch.diff にあります。パッチを読むことがスキルです: 修正箇所は、どこに傷があったかを教えてくれます。
Dockerfile は、公式ソース tarball から ProFTPD 1.3.5(最後の脆弱なリリース)を mod_copy 有効でコンパイルします:
docker build -t proftpd-135 .
docker run -d --name lab135 -p 127.0.0.1:2121:21 -p 127.0.0.1:30000-30010:30000-30010 proftpd-135
docker exec lab135 chmod 777 /home/ftp
proftpd.conf の PassivePorts はデータチャネルを固定します — FTP の2チャネル構成(制御 + 動的データ)が、素朴なコンテナマッピングが失敗する理由です: 制御ポートは機能するのに、データポートは機能しません。
exploit.py は rawソケットを使用します — ftplib は使いません。ライブラリはプロトコルを隠蔽するからであり、プロトコルの隠蔽こそが我々が戦っている対象だからです:
SITE CPFR /etc/segredo.txt -> 350 (USER/PASS 未送信: これが脆弱性そのもの)
SITE CPTO /home/ftp/... -> 250 (任意コピー実行)
USER ftp / PASS ... -> 230 (ログインは単なる外部持ち出し経路)
PASV / RETR -> 150 -> 226 (フラグ取得)
DELE -> 250 (クリーンアップ: IOC を残さない)
AllowOverwrite、ポートマッピング)が最終的な影響を決定します。time.time() ではなく time.time_ns())、残存状態に依存しません。RETR は2回応答します: 150、その後 226。次のコマンドの前に両方を消費します。-d10)がどのチェックが発動したかを正確に教えてくれます。教育および許可されたラボ利用のみを目的としています。所有していない、または書面によるテスト許可を得ていないシステムに対して実行することは違法です。