KVM-XJ ホットパッチ
dump_tool.py はオフセットを自動検索するためのものです。使い方はコードを見てください。
脆弱性概要
CVE-2026-53359(Januscape)は、Linux カーネル KVM シャドウ MMU における Use-After-Free(UAF)脆弱性です。
- 潜伏期間:16 年(2010年8月~2026年7月)
- 影響範囲:ネステッド仮想化を有効にしたすべての Intel/AMD x86 システム
- 危険度:高、VM 内の root ユーザーがホストに脱出し、ホストの root 権限を取得可能
- 攻撃方法:ホストのカーネルパニック(DoS)または完全な仮想マシン脱出(公開 PoC なし)
脆弱性の原理
kvm_mmu_get_page() 関数は、ハッシュテーブル内で再利用可能なシャドウページテーブルを検索する際、gfn(ゲスト物理ページフレーム番号)のみを比較し、role(ページテーブルロール / MMU ロール)を比較しませんでした。そのため、ロールが一致しないページテーブルが誤って再利用され、UAF が発生します。
修正前(脆弱):
┌──────────────────────────────────────┐
│ kvm_mmu_get_page() │
│ ハッシュテーブルを走査 │
│ if (child->gfn == gfn) │
│ return child ← gfnのみ比較! │
│ ロール不一致でも再利用 → UAF! │
│ 新しいページを割り当て(安全) │
└──────────────────────────────────────┘
上流カーネルのコミット 81ccda30b4e8 でロール比較が1行追加されました:
// 修正前
if (... && spte_to_child_sp(*sptep)->gfn == gfn)
// 修正後
if (... && spte_to_child_sp(*sptep)->gfn == gfn
&& spte_to_child_sp(*sptep)->role.word == role.word)
私の修正方法
方法:text_poke バイナリホットパッチ(NOP パッチ)
カーネルソースを変更せず、関数を置き換えず、実行中のカーネルメモリ上で脆弱性の命令を直接修正します。
原理
バイナリレベル:
オフセット 0x104: 49 39 47 28 cmp %rax, 0x28(%r15) ← gfn 比較
オフセット 0x108: 0f 84 9f 01 00 00 je +0x19f ← 一致すればジャンプして再利用
パッチ適用後:
オフセット 0x108: 66 0f 1f 44 00 00 NOP × 6 ← 何もしない
修正後:
┌──────────────────────────────────────┐
│ kvm_mmu_get_page() │
│ ハッシュテーブルを走査 │
│ if (child->gfn == gfn) │
│ NOP(ジャンプが消去され、スルー)│
│ 新しいページを割り当て(強制的に安全経路)│
│ → 再利用しない = UAF を誘発しない = 脆弱性修正 │
└──────────────────────────────────────┘
技術的実装
kallsyms_lookup_name で kvm_mmu_get_page 関数のアドレスを取得
kallsyms_lookup_name で text_poke 関数(カーネルコードのホット修正 API)のアドレスを取得
stop_machine ですべての CPU を停止し、修正の安全性を確保
text_poke で 6 バイトの je 命令を 6 バイトの NOP に置き換え
- アンロード時に
text_poke で元の命令を復元
他の方式を使わない理由
影響評価
導入要件
| 条件 | 説明 |
|---|
| カーネルバージョン | Linux 4.18+(CentOS 8 / RHEL 8 / Rocky 8 など) |
| コンパイル環境 | kernel-devel + gcc + make |
| ホストの再起動 | 不要 |
| VM の停止 |
コンパイルとロード
# コンパイル
make
# ホットパッチのロード
insmod KVM-XJ.ko
# 状態の確認
dmesg | grep KVM-XJ
cat /sys/module/kvm_intel/parameters/nested # 1 のはず
lsmod | grep KVM_XJ
# アンロード(元のコードを復元)
rmmod KVM_XJ
他のカーネルへの対応
カーネルバージョンによってコンパイル最適化が異なり、je 命令のオフセットも異なります。dump_tool.py で自動解析します:
# 1. ホストから kvm.ko をダウンロード
scp root@ホスト:/lib/modules/.../kvm.ko.xz .
xz -d kvm.ko.xz
# 2. 解析ツールを実行
python dump_tool.py kvm.ko
# 3. ツールがパッチオフセットを出力するので、KVM-XJ.c 内の 0x108 を修正
検証済みカーネル
| カーネルバージョン | je オフセット | 状態 |
|---|
| 4.18.0-496.el8.x86_64 | 0x108 | テスト済み |
| 4.18.0-358.el8.x86_64 | 0xe8 | テスト済み |
注意事項
- 使い方やコードが分からない場合は、使用しないことを推奨します。