Default-enabled ICMPv6 Node Information responder discloses hostname and full IPv6 address inventory to unauthenticated remote peers
| Field | Value |
|---|---|
| ID | DF-2609 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N |
| CWE | CWE-200 Exposure of Sensitive Information to an Unauthorized Actor |
| File | sys/netinet6/icmp6.c |
| Lines | 628-709 |
| Area | net |
| Confidence | certain |
| Discovered | 2026-08-28 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:net |
| Reported | pending |
| Known CVE | none |
| CVE match | dfly_specific |
Summary
DragonFly ships icmp6_nodeinfo = 3 (sys/netinet6/in6_proto.c:398), enabling
the KAME node-information responder implemented in icmp6.c. Any remote,
unauthenticated peer that can route an IPv6 packet to any of the host's unicast
addresses receives the system hostname (NI QTYPE FQDN, including the
no-subject oldfqdn compatibility path) and the node's complete IPv6 address
inventory per requested scope flags (NI QTYPE NODEADDR). FreeBSD defaults this
sysctl to 0.
Root cause
icmp6.c:632 gates the whole branch only on if (!icmp6_nodeinfo) break; β
with the default 3, bit 0 (icmp6.c:1275) admits FQDN replies and bit 1
(icmp6.c:1279) admits NODEADDR replies. Destination validation in
ni6_input (icmp6.c:1142-1159) accepts ANY of the node's unicast/anycast
addresses via ifa_ifwithaddr, i.e. global unicast, not just link-local
scope; subject validation is trivially satisfied (FQDN with ni_code==0 and
subjlen==0 takes the oldfqdn path at icmp6.c:1182-1186; NODEADDR with
subject == destination passes at 1225). ni6_store_addrs
(icmp6.c:1660-1806) then returns every in6_ifaddr matching the
attacker-chosen NI_NODEADDR_FLAG_{LINKLOCAL,SITELOCAL,GLOBAL} flags.
FreeBSD's sys/netinet6/in6_proto.c sets this default to 0; DragonFly
diverges with 3.
Threat model & preconditions
- Attacker position: unauthenticated remote peer on any network that can deliver IPv6 to the host's address (default config, no knobs touched).
- Privileges gained or impact: system hostname/FQDN (useful for Kerberos/AD reconnaissance) and enumeration of all IPv6 addresses across interfaces including link-local β defeats IPv6 privacy/scoping expectations.
- Required config or capabilities: none; default kernel, default sysctl.
- Reachability: send ICMPv6 type 139 (NI query) to any host address.
Proof of concept
Trigger is a single crafted ICMPv6 packet (no source needed in the pack):
Build & run
# from any IPv6-capable peer (scapy): # IPv6(dst=<target global unicast>)/ICMPv6NIQuery* qtype NI_QTYPE_FQDN, no subject # or on a BSD peer: ping6 -N <target>
Expected output
ICMP6_NI_REPLY carrying ni6_nametodns(hostname); repeat with qtype NI_QTYPE_NODEADDR + flags GLOBAL|LINKLOCAL to dump the full address list.
Impact
Reconnaissance-grade information leak on every default DragonFly install with IPv6: hostname disclosure and complete IPv6 address inventory to arbitrary off-link remote parties. No memory disclosure.
Recommended fix
Match FreeBSD's safe default and restrict the responder to link-local scope:
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -395,7 +395,7 @@
-int icmp6_nodeinfo = 3; /* enable/disable NI response */
+int icmp6_nodeinfo = 0; /* enable/disable NI response */
Defense-in-depth in icmp6.c: in ni6_input (after icmp6.c:1159)
additionally require IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
IN6_IS_ADDR_MC_LINKLOCAL(...) for the unicast case unless the operator
explicitly opted in, so the responder cannot answer off-link queries even when
the sysctl is enabled.
References
- RFC 4620 (Node Information Queries), Β§security considerations
- FreeBSD in6_proto.c default
icmp6_nodeinfo = 0
Timeline
- 2026-08-28 Discovered during automated audit (pass 2, GLM 5.3).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2609 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| niq.c | β | 6.9 KB | view raw | |
| build.sh | β | 79 B | view raw | |
| run.sh | β | 656 B | view raw | |
| run.log | β | 4.7 KB | view raw | |
| build.log | β | 15 B | view raw | |
| env.txt | β | 429 B | view raw | |
| fix.diff | β | 404 B | view raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| README.md | β | 2.4 KB | β raw | |
| verdict.json | β | 3.7 KB | view raw |
DF-2609 β ICMPv6 Node Information responder default-enabled disclosure
What this proves
DragonFly ships icmp6_nodeinfo = 3 (sys/netinet6/in6_proto.c:398). With
that default, the KAME node-information responder in icmp6.c answers
unauthenticated ICMPv6 NI queries (RFC 4620) addressed to any unicast
address of the host:
- QTYPE FQDN, no subject (code 0, subjlen 0 β the oldfqdn compat path,
icmp6.c:1182-1186) returns the system hostname. - QTYPE NODEADDR with scope flags (
icmp6.c:1278-1281bit 1, gated only by the same sysctl) returns the host's complete IPv6 address inventory across all interfaces, including link-local scopes (ni6_store_addrs,icmp6.c:1660-1806).
Verified live on the audit guest: sysctl net.inet6.icmp6.nodeinfo = 3,
a single NI query to ::1 (and to a global unicast alias) returned
hostname = "dfbsd" and all 4 configured IPv6 addresses. A query to an
address that is not the host's own got no reply (negative control).
Setting the sysctl to 0 silences the responder completely (live fix check);
1 answers FQDN only; 2 would answer NODEADDR only.
No authentication, capability, or scope check is involved β any peer that can route an IPv6 packet to the host gets this information. FreeBSD's default for the same sysctl is 0.
Files
niq.cNI query sender/receiver + reply parser (raw ICMPv6 socket)build.shbuild commandrun.shcanonical run (queries, negative control, sysctl matrix)run.logFULL untrimmed output of the canonical runenv.txtguest environment (uname, sysctls, interfaces)fix.diffdefault flip 3 -> 0 (in6_proto.c)
Reproduce
scp niq.c root@guest:/root/poc/ guest# cc -O2 -Wall -o niq niq.c && ./niq ::1 fqdn && ./niq ::1 nodeaddr 002e
Expected: FQDN ttl=... name=<hostname> and NODEADDR[n] ... lines listing
every IPv6 address of the host. ./niq <foreign-addr> fqdn -> NO REPLY.
Notes
- The query needs a raw ICMPv6 socket (root) only as an injection convenience; the responder itself answers any remotely-routed packet β the threat is the remote unauthenticated peer, not the local user.
- The NI reply itself carries no uninitialized/padding bytes: reply lengths
are exact (
run.loghexdumps; FQDN = 16B nodeinfo + 4B TTL + name, NODEADDR = 16B + 20B/addr). The disclosure is the content (hostname and address inventory), not memory.
DF-2609 β VERDICT
REPRODUCED (info disclosure; recon-grade, Low severity as filed)
Guest: DragonFly dfbsd 6.5-DEVELOPMENT #0 (X86_64_GENERIC, INVARIANTS),
sysctl net.inet6.icmp6.nodeinfo = 3 (default, live-confirmed).
What was demonstrated (run.log, full untrimmed)
- Hostname disclosure β
./niq ::1 fqdn(a single 16-byte ICMPv6 type-139 query, no subject): reply type 140 code 0 carrying05 64 66 62 73 64= DNS-encoded "dfbsd" = the guest hostname. Path:icmp6.c:632(!icmp6_nodeinfopasses with 3) -> mode FQDN ->ni6_input-> dst validationifa_ifwithaddr(icmp6.c:1147, any unicast) -> oldfqdn pathicmp6.c:1182-1186-> bit-0 gate passes (icmp6.c:1275) ->ni6_nametodns(hostname). - Full address inventory β
./niq ::1 nodeaddr 002e: reply listed fe80::5054:ff:fe12:3456 (vtnet0 link-local), 2001:db8::1 (global alias), ::1, fe80::1 (lo0) β i.e. every IPv6 address on the node, exactly matchingifconfig. Bit-1 gate aticmp6.c:1279. - Answers on global unicast β the same queries sent to 2001:db8::1 (global unicast alias on vtnet0) were answered identically; the responder is not restricted to link-local scope (root cause of the recon exposure).
- Negative control β query to 2001:db8::99 (not the host's address): NO REPLY. The disclosure is limited to the host's own info (as claimed).
- Fix check (live) β
sysctl net.inet6.icmp6.nodeinfo=0-> all queries time out;=1-> FQDN answers only; restored=3-> 5/5 replies again. Bit semantics (bit0=FQDN, bit1=NODEADDR) confirmed empirically, matchingicmp6.c:1275/1279.
Quantification
- Bytes disclosed per FQDN reply: hostname length (5 here) + fixed fields.
- Addresses disclosed per NODEADDR reply: all IPv6 addresses of all interfaces (4 at test time) x 20 bytes.
- Uninitialized/padding bytes in the replies: none (reply mbuf lengths
are exact; FQDN path appends an exact-length DNS-name mbuf at
icmp6.c:1365-1371, NODEADDR path setsm_lento exactly 56+copied aticmp6.c:1378-1384). The DB claim is the hostname/address disclosure, which is fully confirmed.
Why "reproduced" at Low severity
The responder is enabled by default on every DragonFly install with IPv6 and hands hostname + complete IPv6 address inventory (incl. link-local) to any unauthenticated remote peer that can deliver a packet to any of the host's unicast addresses β defeating IPv6 recon controls; FreeBSD defaults this sysctl to 0. No memory safety issue; no auth bypass beyond the information exposure itself.
Fix
fix.diff: default icmp6_nodeinfo 3 -> 0 (in6_proto.c:398), matching
FreeBSD. The operational effect was validated live via the sysctl matrix
above (0 = responder silent). Kernel-rebuild validation of the one-line
default change was not performed (the runtime sysctl test is equivalent).
Fix verification
fixedOperational fix validated live without a kernel rebuild: the fix.diff only changes the compiled-in default of the exact sysctl variable exercised at runtime. Baseline (default 3): hostname + full address inventory disclosed on every query. With net.inet6.icmp6.nodeinfo=0 (the fixed default, set at runtime): 0 replies across all query types (run.log FIX CHECK section). Rebuild of the one-line default flip is trivially equivalent and was not separately booted.
run.log: 'sysctl -w net.inet6.icmp6.nodeinfo=0' followed by './niq ::1 fqdn' -> 'NO REPLY (timeout)' and './niq ::1 nodeaddr 002e' -> 'NO REPLY (timeout)'; restored to 3 -> replies return.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: 'FQDN ttl=0x00000000 name=dfbsd' after a 16-byte query to ::1; 'NODEADDR count=4' listing fe80::5054:ff:fe12:3456/2001:db8::1/::1/fe80::1 exactly matching ifconfig; queries to 2001:db8::1 (global unicast) answered; 2001:db8::99 -> 'NO REPLY (timeout)'; sysctl matrix: nodeinfo=0 -> all queries time out, =1 -> FQDN only, restored 3 -> 5/5 replies. env.txt: 'net.inet6.icmp6.nodeinfo: 3' on uname 'DragonFly dfbsd 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026'.
PoC changes
Standalone raw-socket NI query tool written from scratch (no seed). Two fixes during verification: (1) raw ICMPv6 sockets deliver replies WITHOUT the IPv6 header - parser offset auto-detection added; (2) nodeaddr queries require a 16-byte subject equal to the queried destination because ICMP6_NI_SUBJ_IPV6==0 (icmp6.h:165) - subject added.
Verified recommended fix
Default icmp6_nodeinfo to 0 in in6_proto.c:398 (match FreeBSD); defense-in-depth: restrict responder to link-local destinations in ni6_input.
Verdict
REPRODUCED on the stock INVARIANTS guest: sysctl net.inet6.icmp6.nodeinfo defaults to 3 (in6_proto.c:398, live-confirmed) and a single unauthenticated ICMPv6 NI query (RFC 4620) to any of the host's unicast addresses returns the system hostname (QTYPE FQDN, no-subject oldfqdn path icmp6.c:1182-1186, bit-0 gate icmp6.c:1275) and the complete IPv6 address inventory across all interfaces incl. link-local (QTYPE NODEADDR, ni6_store_addrs icmp6.c:1660-1806). hostname 'dfbsd' and all 4 addresses disclosed; negative control (foreign dst) silent; sysctl=0 silences the responder (live fix matrix 0/1/3). Recon-grade info disclosure, no memory-safety involvement; NI replies carry no uninit/pad bytes (exact lengths).
No comments yet.