FreeBSD RPCSEC_GSS実装に存在するスタックバッファオーバーフロー脆弱性(CVE-2026-4747)が公開された。NFS サーバー(ポート2049/TCP)に対してKerberos認証を使用した攻撃により、カーネル実行(RCE)からroot権限を持つリバースシェル奪取が可能となる。
FreeBSD カーネルの RPC サブシステム内 kgssapi.ko モジュールの svc_rpc_gss_validate() 関数に、長年見落とされていた境界チェック欠陥が存在する。RPCSEC_GSS 認証を使用する RPC パケットの処理時に、認証情報本体(credential body)のサイズ検証を省略したまま 128 バイトのスタックバッファへ memcpy() を実行。ファイアウォール背後やKerberos 環境のあるエンタープライズNFS サーバーは継続的な攻撃リスクにさらされている。
脆弱なコードは以下の構造:
static bool_t svc_rpc_gss_validate(struct svc_rpc_gss_client *client,
struct rpc_msg *msg, gss_qop_t *qop,
rpc_gss_proc_t gcproc) {
int32_t rpchdr[128 / sizeof(int32_t)]; // 128バイト
int32_t *buf;
memset(rpchdr, 0, sizeof(rpchdr));
buf = rpchdr;
// 固定RPCヘッダフィールド8個を書き込み (32バイト)
IXDR_PUT_LONG(buf, msg->rm_xid);
IXDR_PUT_ENUM(buf, msg->rm_direction);
IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
oa = &msg->rm_call.cb_cred;
IXDR_PUT_ENUM(buf, oa->oa_flavor);
IXDR_PUT_LONG(buf, oa->oa_length);
if (oa->oa_length) {
// 【BUG】 境界チェックなし
// rpchdr は 128 - 32 = 96 バイトのみ残存
memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); // ← オーバーフロー可能
buf += RNDUP(oa->oa_length) / sizeof(int32_t);
}
}
スタック低位址 ←───────────────────→ スタック高位址
[rbp - 0xe0] ローカル変数
[rbp - 0xc0] rpchdr[0] ← memset対象
[rbp - 0xa0] rpchdr[32] ← memcpy 開始地点 (オーバーフロー開始)
[rbp - 0x40] rpchdr[128] ← バッファ終端 (96バイトから開始)
[rbp - 0x28] 保存RBX ← credential byte 120で上書き
[rbp - 0x20] 保存R12 ← byte 128
[rbp - 0x18] 保存R13 ← byte 136
[rbp - 0x10] 保存R14 ← byte 144
[rbp - 0x08] 保存R15 ← byte 152
[rbp + 0x00] 保存RBP ← byte 160
[rbp + 0x08] リターンアドレス ← byte 168
バッファオーバーフロー時にスタック上の保存レジスタおよびリターンアドレスが上書き可能となる。RPC XDR層では最大認証サイズ MAX_AUTH_BYTES = 400 に制限されるため、溢出可能な範囲は制限される。
RPC XDR層での制限:
攻撃者がKerberos認証を確立し、NFS サーバーに対して複数のRPCSEC_GSS RPC パケットを送信することで段階的にメモリを破壊し、カーネルコード実行に到達する。以下は攻撃の基本フロー:
| バージョン | 脆弱か | 備考 |
|---|---|---|
| FreeBSD 13.5 < p11 | ✅ 脆弱 | セキュリティパッチ未適用 |
| FreeBSD 14.3 < p10 | ✅ 脆弱 | 同上 |
| FreeBSD 14.4 < p1 | ✅ 脆弱 | テスト対象バージョン |
| FreeBSD 15.0 < p5 | ✅ 脆弱 | 開発版も対象 |
| FreeBSD 14.4-RELEASE-p1 以降 | ❌ パッチ済み | 境界チェック追加 |
公式Advisory: FreeBSD-SA-26:08.rpcsec_gss
┌─────────────────────────────────────────────────────┐
│ 攻撃要件チェックリスト │
├─────────────────────────────────────────────────────┤
│ [必須] NFS サーバー (port 2049/TCP) が accessible │
│ [必須] kgssapi.ko がロード状態 │
│ [必須] Kerberos KDC がアクセス可能 (port 88/TCP) │
│ [必須] 攻撃者が有効な Kerberos ticket 保有 │
│ → 任意ユーザーのチケットで十分 │
│ [推奨] 適切なリソース (CPU, メモリ) │
└─────────────────────────────────────────────────────┘
# イメージダウンロード
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
cp FreeBSD-14.4-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2 freebsd-vuln.qcow2
qemu-img resize freebsd-vuln.qcow2 8G
# Cloud-init 設定
cat > user-data << 'EOF'
#cloud-config
chpasswd:
list: |
root:freebsd
expire: False
ssh_pwauth: True
bootcmd:
- rm -f /firstboot # 自動パッチング無効化
- rm -f /var/db/freebsd-update/*
runcmd:
- echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
- service sshd restart
- kldload kgssapi # ← 脆弱モジュール
- sysrc rpcbind_enable=YES nfs_server_enable=YES
- echo '/export -network 0.0.0.0/0' > /etc/exports
- mkdir -p /export
- service rpcbind start && service nfsd start
EOF
cat > meta-data << 'EOF'
instance-id: cve-test
local-hostname: freebsd-vuln
EOF
genisoimage -output seed.iso -volid cidata -joliet -rock user-data meta-data
# QEMU 起動 (ポート転送: SSH/NFS/KDC)
qemu-system-x86_64 -enable-kvm -cpu host -m 2G -smp 2 \
-drive file=freebsd-vuln.qcow2,format=qcow2,if=virtio \
-cdrom seed.iso \
-netdev user,id=net0,\
hostfwd=tcp::2222-:22,\
hostfwd=tcp::2049-:2049,\
hostfwd=tcp::8888-:88 \
-device virtio-net-pci,netdev=net0 -nographic
# 1. Kerberos インストール
pkg install -y krb5
# 2. krb5.conf 作成
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. Kerberos principals 作成 (hostname = "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 "addprinc -randkey host/[email protected]"
/usr/local/sbin/kadmin.local -q "ktadd -k /etc/krb5.keytab nfs/[email protected]"
/usr/local/sbin/kadmin.local -q "ktadd -k /etc/krb5.keytab host/[email protected]"
# 5. KDC 起動
/usr/local/sbin/krb5kdc
# 6. NFS 設定
mkdir -p /export
echo '/export -network 0.0.0.0/0' > /etc/exports
# 7. rc.conf 設定
sysrc rpcbind_enable=YES
sysrc nfs_server_enable=YES
sysrc nfsv4_server_enable=YES
sysrc nfsuserd_enable=YES
sysrc gssd_enable=YES
sysrc mountd_enable=YES
sysrc nfs_server_flags="-u -t"
# 8. サービス起動
service rpcbind start
service nfsuserd start
service gssd start
service mountd start
service nfsd start
# 9. 検証
sysctl vfs.nfsd.threads # スレッド数確認
sockstat -l | grep 2049 # TCP リスン確認 (UDP のみはNG)
kldstat | grep gss # kgssapi.ko ロード確認
# 10. Kerberos ticket 取得テスト
echo 'password' | kinit [email protected]
klist
# システムパッケージインストール
# Ubuntu/Debian
sudo apt install krb5-user libkrb5-dev python3-gssapi
# Fedora/RHEL
sudo dnf install krb5-workstation krb5-devel python3-gssapi
# krb5.conf 設定 (FreeBSD VM IP に向ける)
sudo tee /etc/krb5.conf << EOF
[libdefaults]
default_realm = TEST.LOCAL
rdns = false # ← 重要: DNS逆引き無効化
dns_canonicalize_hostname = false # ← 重要: hostname正規化無効化
[realms]
TEST.LOCAL = {
kdc = 192.168.1.100:88 # FreeBSD VM IP
}
EOF
# /etc/hosts 設定
echo "192.168.1.100 test" | sudo tee -a /etc/hosts
# Kerberos ticket 取得
echo "password" | kinit [email protected]
klist
⚠️ Critical: rdns = false と dns_canonicalize_hostname = false がないと、Kerberos が逆引きで hostname を生成 → サーバー拒否となる可能性がある。
単一の境界チェック追加:
oa = &msg->rm_call.cb_cred;
if (oa->oa_length > sizeof(rpchdr) - 8*BYTES_PER_XDR_UNIT) {
rpc_gss_log_debug("auth length %d exceeds maximum", oa->oa_length);
client->cl_state = CLIENT_STALE;
return (FALSE); // ← オーバーフロー回避
}
if (oa->oa_length) {
memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
buf += RNDUP(oa->oa_length) / sizeof(int32_t);
}
直近の対応:
freebsd-update fetch install で -p1 パッチ適用/etc/rc.conf で nfs_server_enable=NO 確認根本的な防止:
セキュリティ研究: バッファオーバーフロー脆弱性がRCEに発展するメカニズムの理解が、ディフェンス側の防御設計に必要。カーネル memory protection の強化が急務
この記事はAI業界の最新動向を速報でお届けする「AI Heartland ニュース」です。