
vsftpd 3.0.5 - 高 - リモート - 権限残留 - ブレース deny_file が .private/secret を回避
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vsftpd-deny-file-brace-child
クラス: 権限の取りこぼし 到達範囲: リモート
vsftpd 3.0.5 - Chris Evans
man ページ(vsftpd.conf.5)の例は deny_file={*.mp3,*.mov,.private} である。一致する名前は「いかなる方法でもアクセスできてはならない」。これには「ダウンロード、ディレクトリへの移動、ディレクトリ内の何かへの影響」が含まれる。vsf_access_check_file(access.c:16-43)は、与えられたクライアントパスに対して vsf_filename_passes_filter を呼び出し、その後、ブレース形式のチューナブル全体に対して str_locate_str を呼び出す。フィルタは最終コンポーネント/プレフィックスにマッチする。残りの /secret は失敗し、{*.mp3,*.mov,.private} の部分文字列も見つからない。
CWD .private は拒否される(ベース名がヒットする)。RETR .private/secret と RETR dir/.private は成功する。この「ディレクトリ内」こそが、この取りこぼしが破るものである。
CVE-2015-1419 はディストリビューションによる公開の realpath/ベース名パッチである。公式 3.0.5 はそれを取り込んでいない。Debian のブレース分岐も依然としてフルパスにマッチするため、この子パス走査はパッチ適用済みのディストリビューションのパッケージにも残っている。2015-1419 を再申請しないこと。
この取りこぼしは、設定で制御される拒否リストの見落としである。認証不要の RCE ではなく、chroot エスケープでもなく、ホストの /etc 読み取りでもない。
| ID | CVE 未割り当て |
| クラス | 権限の取りこぼし |
| 到達範囲 | リモート(認証済み FTP) |
| CWE | CWE-863, CWE-22 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| 製品 | vsftpd (Chris Evans) |
| 影響を受けるバージョン | vsftpd 3.0.5 公式 tarball SHA-256 26b602ae454b0ba6d99ef44a09b6b9e0dfa7f67228106736df1f278c70bc91d3 |
| 認証 | 認証済み FTP USER/PASS(ラボでは anonymous)。deny_file にブレースリストを設定(デフォルトは未設定) |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1 のみ。Anonymous 読み取り専用。Witness ファイル。 |
誰が: 認証済み FTP ユーザー(ラボでは anonymous ftp)。通常の vsftpd の権限降格後のセッション UID。deny_file は、その UID から権限を奪うはずだった追加ポリシーである。man ページが挙げる仮想ユーザーのレシピは guest_username を共有するため、Unix DAC ではそれらを分離できない。tunable_deny_file が NULL の素の 3.0.5 は、この取りこぼしには該当しない。
| メソッド + シンク | 権限 |
|---|---|
文書化されたブレース deny_file={*.mp3,*.mov,.private} を使った RETR .private/secret | セッション uid として、ディレクトリ名が拒否リストにあるファイルを handle_retr が str_open する。ラボ: 150/226、37 バイト、ボディ VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS |
RETR dir/.private | 同じシンク。別のディレクトリ配下にある .private という名前のファイル(must_match_at_current_pos はインデックス > 0 でのマッチを拒否する)。ラボ: 150/226、ボディ DIRDOT-VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS |
vsf_access_check_file ゲートから推測。ラボは読み取り専用だった)| メソッド + シンク | 権限 |
|---|---|
write_enable=YES のときに同じパスへの STOR / APPE | セッション uid として handle_upload_common が作成/上書き |
同じパスへの DELE / RNTO | 拒否リストが止めるはずだった名前の str_unlink / リネーム |
| メソッド + シンク | 権限 |
|---|---|
LIST .private/x(または拒否ツリー内での LIST) | handle_dir_common が引数全体をアクセスチェックし(見落とし)、スラッシュ分割後に .private プレフィックスを str_opendir する。名前のみ。Unix DAC は依然として適用される |
| メソッド + シンク | 結果 |
|---|---|
CWD .private | 550 Permission denied。ベース名 .private がブレースにヒットする |
RETR song.mp3 | 550。*.mp3 は最終コンポーネントのパターンであり、実際にマッチする |
CWD .private 自体(ベース名がヒットする)hide_file LIST 非表示(readdir からのベース名。隠し名の RETR は文書化された意図どおり)/etchttp_enable はデフォルト無効。FTP モードの GET はセッションを切断する)deny_file=.private は部分文字列フォールバック(.private/secret 内の .private に対する str_locate_str)で救われる私は GitHub ミラーや Debian/Fedora ツリーではなく、公式 vsftpd 3.0.5(SHA-256 26b602ae...c91d3)を固定した。19 回のソース探索。デフォルトでコンパイルされたデーモンには、未公開の Critical/High の取りこぼしはない。これには、運用者が man ページのブレース例をコピーする必要がある。
vsf_filename_passes_filter(ls.c)は * / ? / ネストされていない {,} にマッチし、最終コンポーネントのパターンに対してのみ有用な処理を行う。名前 .private/secret を {*.mp3,*.mov,.private} に対して照合する場合:
*.mp3、*.mov、.private は .private/secret の先頭でマッチする。トークンが * でない限り、残りの /secret は失敗する。{*.mp3,*.mov,.private} を検索し、見つからない。すでに公開されている誤った道: CVE-2015-1419 はディストリビューション限定の realpath である。公式 Changelog にそのような修正はない。Debian のブレース分岐でさえ正規化されたフルパスをコピーするため、子は依然として走査される。
cd lab
./run.sh
ループバック制御 127.0.0.1:18710、PASV 18711-18718。Compose プロジェクト vsftpd-deny-file-brace-child。公式 3.0.5 tarball、anonymous 読み取り専用、deny_file={*.mp3,*.mov,.private}。Witness VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS。最終行:
SUCCESS vsftpd-deny-file-brace-child cwd-dotprivate=550 retr-child=yes retr-dir-dotprivate=yes mp3=550 VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS
ブレースの選択肢をすべてのパスコンポーネントに対してマッチさせるか、フィルタの前に realpath / ベース名を適用する(通常のシンボリックリンクの注意点を伴う)。文書化された例は、実際に子を拒否すべきである。
ベンダーへの報告先は [email protected](vsftpd)。vsftpd のいかなるミラーにも公開 GitHub issue を立てないこと。
Abraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - [email protected]