
fs/fuse/readdir.c:fuse_add_dirent_to_cache()における境界チェックの欠落。FUSEサーバーがnamelen = 4095のディレントを返すと、4120バイトのレコードにシリアライズされる。カーネルはこれを1つの4096バイトのページキャッシュページにコピーし、24バイトのサーバー制御データが次の物理ページにはみ出る(オーバーフローする)。
このリポジトリには、書き込みプリミティブのマーカーPoCと、オーバーフローを/etc/passwdのページキャッシュコピーに整形し、rootをパスワードなしのアカウントにするローカル権限昇格が含まれています。
FUSE_NAME_MAXがPATH_MAX - 1に引き上げられたことでreaddirパスが到達可能になった)51a8de6c50bf9 「fuse: ページキャッシュ内の特大ディレントを拒否」(5つの安定版バックポートのうちの1つ)オーバーフローは、FUSEキャッシュページの直後に位置する物理ページへのカーネル書き込みです。整形(グルーミング)はその書き込みを選択したページに狙わせますが、確率的です。失敗すると、24バイトが無関係なページにヒットする可能性があります:
使い捨てのVM内でのみ実行してください。ここにあるスクリプトは常にQEMU/KVM内でエクスプロイトを実行し、ホスト上では決して実行しません。
exploit.c PoCおよびLPE(単一ファイル、静的ビルド)
Makefile ./exploit(静的)をビルド
vm/config.sh すべてのパスと設定(KERNEL_TREEを編集)
vm/make_rootfs.sh Ubuntu 24.04ゲストイメージをビルド(rootで実行)
vm/build_vuln_kernel.sh カーネルツリーから脆弱なbzImageをビルド
vm/run_vm.sh ゲストをヘッドレスで起動
vm/provision_guest.sh ゲスト内のセットアップ(ssh経由で実行)
vm/run_in_vm.sh 1つのコマンド: 起動、プロビジョニング、実行、検証
ホスト: KVM対応Linux(/dev/kvmが存在し、ユーザーがkvmグループに所属)、さらに:
qemu-system-x86_64, sshpass, gcc, makemake_rootfs.sh用: debootstrap(とroot権限)脆弱なカーネルをビルドするためのLinuxカーネルソースツリーも必要です。
vm/config.shを編集します。設定が必要な値はKERNEL_TREEのみです。これはLinuxソースチェックアウトへのパスです。修正コミット51a8de6c50bf9より前のリビジョンを使用してください(6.15..7.0のチェックアウトにはすべてバグがあります)。ツリーに既に修正が含まれている場合、build_vuln_kernel.shはビルドのためにガードを削除し、後でファイルを復元します。その他の値(メモリ、CPU、SSHポート、イメージパス)はデフォルト値があるのでそのままで問題ありません。任意の値は環境変数からも設定できます。例えばKERNEL_TREE=/path/to/linux vm/run_in_vm.shのように。
ゲストイメージをビルドします(Ubuntu 24.04、debootstrapにはroot権限が必要):
sudo vm/make_rootfs.sh
これにより、test:testのsudoユーザー、シリアルコンソール、sshパスワード認証、fuse3が作成されます。
脆弱なカーネルをビルドします:
vm/build_vuln_kernel.sh
エンドツーエンド(起動、プロビジョニング、マーカーPoC、LPE、uid 0の確認):
vm/run_in_vm.sh
手動:
vm/run_vm.sh & # ヘッドレス起動;コンソールはvm/serial.logに出力
make # 静的エクスプロイトをビルド
# ./exploitをゲストにコピーし、testユーザーとして以下を実行:
./exploit --poc -n 20 # マーカーテスト
./exploit # ウォームアップ、その後LPE
VMの停止:
pkill -f bzImage-fuse-vuln
--poc マーカーモードのみ、/etc/passwdの変更なし
-n N ラウンド数
(引数なし) 5ラウンドのウォームアップ、その後最大200回のLPE試行;成功するとパスワードなしのroot行を/etc/passwdに書き込み、キャッシュをクリアする
マーカーモードはラウンドごとのヒット数と合計を表示します。例:20/20 (100.0%)。
LPEモードはWarmup: 5/5、次にLPE n/200 ... HIT!行を表示し、/etc/passwdの最初の行をroot:x:0:0:root:/root:/bin/bashからroot::0:0:root:/root:/bin/shに変更し、続けて空のパスワードでsu -s /bin/sh rootを実行するとuid 0が返ります。
readdirキャッシュパスは各ディレントをページキャッシュページにコピーします。サイズチェックは、ディレントが現在のページの残りスペースに収まるかどうかのみを確認し、ディレントがそもそも1ページに収まるかどうかは確認しません。namelen = 4095の場合、レコードは4120バイトになるため、コピーは新しいページの終端から24バイトはみ出します。
エクスプロイトはCPUごとのページ(PCP)フリーリストを枯渇させ、アロケーターがバディ分割から物理的に隣接するページを割り当てるようにし、readdirキャッシュページと被害ページが隣接するように配置します。LPEでは、被害ページは/etc/passwdのページキャッシュコピーです。24バイトのオーバーフローによりrootの行が書き換えられ、パスワードフィールドが空になり、PAMのnullok(Ubuntuデフォルト)により、su rootが空のパスワードで成功するようになります。
Full writeup: https://cyberstan.co.uk/fuse-readdir-oob/
Qi Tang氏とZijun Hu氏によりアップストリームに報告されました。修正はSamuel Page氏によって作成されました。私はこれを独自に発見し報告しました。アップストリームのクレジットとCVE-2026-31694は彼らのものです。このバグはメインラインおよび安定版ツリーで修正されています。
MIT、LICENSEを参照。教育および許可されたテストのみを目的としています。