OpenBSD slaacd/rad Infinite Loop via Malformed ND Option (CVE-2026-41285)
by Daniel Wade (Rat5ak) · 2026-07-05
- Severity
- High
- CVE
- CVE-2026-41285
- Category
- network
- Affected product
- OpenBSD slaacd (SLAAC autoconfiguration daemon) and rad (Router Advertisement daemon)
- Affected versions
- OpenBSD 7.8 GENERIC amd64 (tested); parsing bug present in sbin/slaacd/engine.c and usr.sbin/rad/engine.c
- Disclosed
- 2026-07-05
- Patch status
- unpatched
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-05 |
| Author / Researcher | Daniel Wade (Rat5ak) |
| CVE / Advisory | CVE-2026-41285 |
| Category | network |
| Severity | High |
| CVSS Score | Not specified in source |
| Status | Weaponized |
| Tags | openbsd, slaacd, rad, icmpv6, ipv6, dos, infinite-loop, scapy, neighbor-discovery |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | OpenBSD slaacd (SLAAC autoconfiguration daemon) and rad (Router Advertisement daemon) |
| Versions Affected | OpenBSD 7.8 GENERIC amd64 (tested); parsing bug present in sbin/slaacd/engine.c and usr.sbin/rad/engine.c |
| Language / Platform | C (OpenBSD userland daemons); PoC in Python/Scapy |
| Authentication Required | No |
| Network Access Required | Yes (same L2 network segment) |
Summary
slaacd and rad each contain their own userland parser for ICMPv6 Neighbor Discovery (ND) options, separate from the kernel’s already-correct nd6_options() validation. Their parsing loop computes optlen = nd_opt_len * 8 - 2, and when an attacker sends an ND option with nd_opt_len == 0, this evaluates to -2, which — due to signed/unsigned promotion — fails to trigger the loop’s bounds-check guard. The loop pointer then effectively regresses instead of advancing, spinning the daemon’s engine process at 100% CPU forever. A single 18-byte crafted ICMPv6 Router Advertisement (or Router Solicitation, for rad) from any host on the local network segment permanently disables IPv6 SLAAC address autoconfiguration until the daemon is manually restarted. The repository includes both a minimal one-packet exploit and a full before/after proof script that verifies SLAAC works, sends the exploit, and confirms subsequent legitimate RAs are ignored.
Vulnerability Details
Root Cause
nd_opt->nd_opt_len * 8 - 2, where nd_opt_len is a uint8_t, promotes to a signed int and evaluates to -2 when nd_opt_len == 0; the subsequent bounds check (optlen > len) is always false against a positive len, so the parser never rejects the zero-length option and the loop fails to advance, spinning forever — a bug class the kernel already guards against but that both userland daemons repeat independently.
Attack Vector
- Attacker on the same L2 segment as the OpenBSD target crafts an ICMPv6 Router Advertisement containing one ND option with
nd_opt_typeset to any value andnd_opt_len = 0. - The packet is sent via multicast (
ff02::1) usingsendp()on the local interface — no authentication or prior state required. slaacd’s (orrad’s) engine process receives the RA/RS and enters its ND-option parsing loop, which never terminates due to the arithmetic underflow.- The engine process pegs at 100% CPU and stops processing all further Router Advertisements, so SLAAC addressing eventually goes stale as existing addresses expire.
Impact
Permanent, remote, unauthenticated denial of service against IPv6 autoconfiguration on any OpenBSD host reachable on the local network segment; recovery requires manual daemon restart or reboot.
Environment / Lab Setup
Target: OpenBSD 7.8 GENERIC amd64 running slaacd (or rad) on an IPv6-enabled interface
Attacker: Python 3 + scapy, raw-socket/root privileges, on the same L2 segment (VM lab tested with VMware Network Adapter VMnet8); prove_dos.py additionally uses paramiko for SSH-based verification
Proof of Concept
PoC Script
See
kill_slaacd.pyandprove_dos.pyin this folder.
| |
kill_slaacd.py sends a single crafted Router Advertisement with one ND option (type=200, len=0) over the given interface, which is enough to hang the target’s slaacd engine indefinitely. prove_dos.py automates the complete demonstration over SSH: it restarts slaacd, sends a legitimate RA to confirm SLAAC is working, fires the exploit packet, then sends a second legitimate RA and shows it is silently ignored — proving the daemon is dead — before restarting the service.
Detection & Indicators of Compromise
Signs of compromise:
slaacd/radengine process CPU usage stuck near 100% with no drop-off- IPv6 SLAAC addresses expiring without renewal on affected interfaces
- Unusual ICMPv6 RA/RS traffic on the local segment with anomalously small ND option lengths
Remediation
| Action | Detail |
|---|---|
| Primary fix | No vendor patch confirmed as of 2026-07-05 — apply the suggested one-line guard (if (nd_opt->nd_opt_len == 0) break;) in slaacd’s parse_ra()/debug_log_ra() and rad’s RS parser, or monitor security@openbsd.org for an official fix |
| Interim mitigation | Restrict L2 network access to trusted hosts, monitor slaacd/rad engine CPU usage for anomalies, and be prepared to rcctl restart slaacd/rad if a hang is detected |
References
Notes
Mirrored from https://github.com/Rat5ak/CVE-2026-41285-OpenBSD-v6daemons-go-brrr on 2026-07-05.
| |