DF-3320 / verdict.json
{ "finding_id": "DF-3320", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED. The IPv4 address-set ioctls persist user-supplied sin_len verbatim into the live struct in_ifaddr (in.c:998 ia_addr = *sin via SIOCSIFADDR/SIOCAIFADDR->in_ifinit; :602 ia_dstaddr via SIOCSIFDSTADDR; :625 ia_broadaddr via SIOCSIFBRDADDR; :668 ia_sockmask via SIOCAIFADDR) and export them as ifa->ifa_addr/ifa_dstaddr/ifa_netmask with no validation -- the IPv4 twin of DF-3318. Unprivileged consumers then trust sa_len as a copy length: ifconf() (SIOCGIFCONF, no privilege check, sys/net/if.c:1996-1997) does copyout(sa, &ifrp->ifr_addr, sa->sa_len) (sys/net/if.c:2590-2592), and rtsock copies ifa sockaddrs with RT_ROUNDUP(sa->sa_len) into route messages (sys/net/rtsock.c:1140,1175,1202). After a one-time privileged SIOCAIFADDR with sin_len=200 on lo0 (152 bytes of the copy read past the 360-byte in_ifaddr allocation), an unprivileged user reading SIOCGIFCONF obtained three adjacent-slab kernel heap pointers -- 0xfffff8004f2e79d8 / ...e8 / ...f8, the neighbouring in_ifaddrs ifa_addr/ifa_dstaddr/ifa_netmask -- identical across 3/3 runs as nobody. Fix validated on rebuilt kernel #3: setup rejected with EINVAL, leak finds no entry, normal alias add/remove unaffected.", "exploit_chain": "root (one-time misconfiguration, e.g. buggy config tool): SIOCAIFADDR(lo0, addr=127.200.0.200 sin_len=200, mask /8) -> in_control_internal -> in_ifinit (in.c:998) persists sin_len=200 into ia_addr (and ia_dstaddr via the loopback copy at :1062-1063) -> any local user: socket+ioctl(SIOCGIFCONF) -> ifconf() copyout(sa, &ifrp->ifr_addr, 200) reads 152 bytes past the in_ifaddr heap object -> userspace receives kernel heap pointers of the adjacent in_ifaddr (KASLR defeat + slab layout disclosure). Route sockets (rtsock.c RTM_NEWADDR broadcast on the same setup ioctl) leak the same via ifa_dstaddr/ifa_netmask lengths.", "evidence": [ "run.log: baseline no entry before setup; after setup, 3/3 identical runs as nobody showing entry: lo0 sa_len=200 family=2 addr=127.200.0.200 with kernel pointers at leaked offsets 72/80/88", "leak_sample.txt: hexdump of the 200-byte leaked sockaddr -- [0..15] ia_addr (c8 02 = len 200, family 2), [16..31] ia_dstaddr, [32..47] ia_sockmask (05 02 ... ff = /8 trimmed), [48..] out-of-bounds region containing 0xfffff8004f2e79d8/e8/f8", "env.txt: guest uname, cc 8.3, lo0 state after setup", "fix_run.log: patched kernel #3 -- setup => SIOCAIFADDR(lo0, len=200): Invalid argument, leak => no entry, normal ifconfig alias add/remove works", "fix.diff: set-side sin_len>sizeof(struct sockaddr_in) rejection (SIOCAIFADDR/SIOCSIFDSTADDR/SIOCSIFBRDADDR/in_ifinit) + DF-3319 hunks" ], "kernel_refs": [ "sys/netinet/in.c:998", "sys/netinet/in.c:543-545", "sys/netinet/in.c:602", "sys/netinet/in.c:625", "sys/netinet/in.c:668", "sys/netinet/in.c:1062-1063", "sys/net/if.c:1996-1997", "sys/net/if.c:2578-2592", "sys/net/rtsock.c:1140", "sys/net/rtsock.c:1175", "sys/net/rtsock.c:1202" ], "poc_changes": "Fixed the pointer-scan filter in leak.c (v & 0xf == 0 rejected the 0x...8-ending kernel pointers; changed to (v & 0x7) == 0); added five filler aliases in setup.c for deterministic adjacent-slab content.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 300, "guest_dirty": 0, "build_cmd": "cc -O -o setup setup.c && cc -O -o leak leak.c", "run_cmd": "./setup lo0 (root); su -m nobody -c ./leak 127.200.0.200 x3", "code_hash": "9005d5a6b33e2ffae9f62e5e29753ac2a295bb8a36d759d3a02b39afe3fc6dd0", "notes": "The in-file consumers in in_lifaddr_ioctl (in.c:927/:930) are gated behind the DF-0490 AF_INET6 filter and only see the confused in6_ifaddr (that manifestation is DF-3319); SIOCGIFCONF and rtsock make the poisoned v4 sin_len exploitable unprivileged today. Threat model identical to DF-3318: one privileged misconfiguration required; no unprivileged-only path to a write primitive exists.", "recommended_fix": "Reject sin_len > sizeof(struct sockaddr_in) on all IPv4 address-set ioctls (SIOCAIFADDR/SIOCSIFADDR/SIOCSIFDSTADDR/SIOCSIFBRDADDR, and in in_ifinit before :998) so unvalidated lengths are never persisted into live ifaddrs consumed by ifconf()/rtsock.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #3: Thu Sep 10 19:51:37 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied in-guest, kernel rebuilt (0 warnings) and installed as #3. Baseline stock: setup OK, unpriv leak shows 200-byte entry with 3 kernel heap pointers (3/3 runs). Patched: setup => EINVAL at the SIOCAIFADDR door; leak => no sa_len>16 entry; normal ifconfig lo0 alias 127.200.0.200 netmask 255.0.0.0 succeeds and removes cleanly (no regression).", "fix_evidence": "fix_run.log (validation matrix incl. DF-3320 steps 4-6), fix_build.log (kernel #3 build), fix.diff" } |