
長さゼロのIPv6 NDオプションが1つ。チェックが1つ欠落。デーモンは後方に進み、ループの中で生き続ける。 OpenBSD に報告され、修正され、CVE が割り当てられた。
ローカルネットワークからの1つの細工されたICMPv6パケットが、slaacd および/または rad を恒久的にハングさせます。誰かが手動でデーモンを再起動するまで、IPv6 SLAACアドレス自動設定は機能を停止します。認証不要、特権不要、ICMPv6ペイロードはわずか18バイトです。
| CVE | CVE-2026-41285 |
| Bug class | 整数アンダーフローによる無限ループ |
| Root cause | NDオプションパーサーが len==0 をチェックせずに nd_opt_len * 8 - 2 を実行 |
| Component | sbin/slaacd/engine.c, usr.sbin/rad/engine.c |
| Impact | IPv6 SLAAC(slaacd)またはRAサービス(rad)の永続的DoS |
| Required | 同じL2ネットワークセグメント上の任意のデバイス |
| Tested | OpenBSD 7.8 GENERIC amd64 |
RFC 4861 §4.6 は、長さがゼロのNDオプションは無効であり、黙って破棄しなければならないと定めています。カーネル自身の sys/netinet6/nd6.c にある nd6_options() はこれを正しく行います。しかし、そのチェックが効く前に、生のICMPv6パケットはユーザーランドのソケットに配信されてしまいます。
slaacd と rad はどちらもNDオプションを自身で解析します。ループは次のようになっています:
while (len > 0) {
// ...
optlen = nd_opt->nd_opt_len * 8 - 2; // nd_opt_len is uint8_t
if (optlen > len)
break;
len += 2;
// advance pointer by optlen... which is (uint32_t)-2 promoted from int
}
nd_opt_len == 0 の場合:式 0 * 8 - 2 は int に昇格し、-2 になります。ガード条件 (-2 > len) は常に偽になります(符号付き比較であり、len は正の値です)。次に len += 2 が実行され、ポインタは2バイト戻ります。ループは進行しません。CPUは100%に張り付きます。永遠に。
カーネルは自身のコピーを検証しました。ユーザーランドのデーモンが受け取ったのは生の原本です。誰も slaacd に伝えませんでした。
rcctl restart slaacd または再起動が必要python3 poc/kill_slaacd.py <interface>
scapy が必要です。nd_opt_len = 0 の単一のNDオプションを含む、1つのルーター広告(Router Advertisement)を送信します。それだけです。オプションタイプは問いません(PoC ではタイプ200/不明を使用)。
完全なエンドツーエンドの実証(SLAAC 動作中 → エクスプロイト → SLAAC 停止)を行いたい場合:
python3 poc/prove_dos.py
── Before exploit ──
SLAAC addresses: 2001:db8:1:0:df6f:edeb:6e3a:2640, ...
Engine CPU: 0.0%
── After one packet ──
Engine CPU: 23.1% → 43.3% (climbing)
New RA with 2001:db8:2::/64 sent → no address configured
slaacd is dead. IPv6 autoconf: DEAD.
CVE-2022-27881 および CVE-2022-27882(いずれも engine.c における slaacd の無限ループ、同じくNDオプション解析)と同じ攻撃対象領域です。これは同じクラスのバグの新たな事例であり、以前の修正ではすべての解析ループをカバーしていませんでした。
それに対して算術演算を行う前に、nd_opt_len == 0 をチェックします。ループから抜け出します。これはカーネルが nd6_options() 内で既に行っている処理です:
if (nd_opt->nd_opt_len == 0)
break; // or: goto bad;
slaacd の parse_ra()、debug_log_ra()、および rad のRSパーサーに対する推奨パッチです。3つのループすべてに同じ1行のガードが必要です。
これはローカルネットワークのDoSです。slaacd を実行しているOpenBSDマシンと同じL2セグメントにいる場合、1パケットでそのIPv6が停止します。自分が所有していないネットワークには送信しないでください。OpenBSD を運用している場合は、パッチを確認するか、自分で len==0 ガードを追加してください。
Daniel Wade - GitHub · Twitter/X · Bluesky · nadsec.online