β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2609

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.

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2609 Β· 10 files
FileTypeDescriptionSize
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-1281 bit 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.c NI query sender/receiver + reply parser (raw ICMPv6 socket)
  • build.sh build command
  • run.sh canonical run (queries, negative control, sysctl matrix)
  • run.log FULL untrimmed output of the canonical run
  • env.txt guest environment (uname, sysctls, interfaces)
  • fix.diff default 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.log hexdumps; FQDN = 16B nodeinfo + 4B TTL + name, NODEADDR = 16B + 20B/addr). The disclosure is the content (hostname and address inventory), not memory.
VERDICT.md
↓ download raw

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)

  1. Hostname disclosure β€” ./niq ::1 fqdn (a single 16-byte ICMPv6 type-139 query, no subject): reply type 140 code 0 carrying 05 64 66 62 73 64 = DNS-encoded "dfbsd" = the guest hostname. Path: icmp6.c:632 (!icmp6_nodeinfo passes with 3) -> mode FQDN -> ni6_input -> dst validation ifa_ifwithaddr (icmp6.c:1147, any unicast) -> oldfqdn path icmp6.c:1182-1186 -> bit-0 gate passes (icmp6.c:1275) -> ni6_nametodns(hostname).
  2. 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 matching ifconfig. Bit-1 gate at icmp6.c:1279.
  3. 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).
  4. 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).
  5. 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, matching icmp6.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 sets m_len to exactly 56+copied at icmp6.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

fixed
baseline reproduced→ patch + rebuild →patched clean

Operational 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.
↓ fix.diffper-fix-DF-2609

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).