⬢ DragonFlyBSD Kernel Audit
DF-3044 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3044",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Every claim of the finding was demonstrated on the guest through the verbatim production code (sha256-gated #include of bootp_subr.c compiled into the kernel): the reply XID is fully predictable from source constants (client DISCOVER and REQUEST both carried xid bytes 0100ffff = 0xFFFF0001 computed blind by the harness attacker), the acceptance test at bootp_subr.c:838-848 never inspects the reply origin because soreceive is called with a NULL name at :807 (harness counter: 0 origin requests; replies from port 9999 accepted), and a single blind OFFER+ACK exchange from 6.6.6.6:9996 gave the attacker complete control of the netboot client identity: hostname, root NFS server and path, swap server and path, and default route were all adopted via verbatim bootpc_decode_reply and fed to setfs/md_mount/rtrequest_global. A predicted-XID ACK with a 3-byte subnet mask additionally tripped the verbatim panic at bootp_subr.c:1390 (panic.txt), proving the boot-time crash path. uid=0 is not reachable on this guest because the stock kernel omits options BOOTP and boots from disk; on real netboot deployments the demonstrated control implies total (root filesystem) compromise. The validated fix (karc4random xid seed + require reply source port 67) killed the blind prediction, the arbitrary-origin acceptance and the panic in A/B runs; a same-L2 sniffer spoofing port 67 remains, which is the documented unauthenticated-DHCP residual.",
  "exploit_chain": "malicious peer on the boot network computes xid = 0xFFFF0000 + 0x100*ifindex + 1 from source constants (no packet observation), knows the client MAC (broadcast in every DISCOVER), sends BOOTREPLY op=2 with matching xid/hlen/chaddr from any address and any port; verbatim matcher accepts; bootpc_received promotes state; bootpc_decode_reply adopts attacker yiaddr/siaddr/hostname/root path/swap path/router; bootpc_init mounts root and swap from the attacker NFS server via md_mount and installs the attacker default route; variant with malformed TAG_SUBNETMASK length 3 panics the client at boot (DoS)",
  "evidence": "run.log: verbatim console lines Received DHCP Offer packet on vtnet0 from 6.6.6.6 (accepted), Received DHCP Ack ... (got root path), decode line vtnet0 at 66.6.6.6 server 6.6.6.6 ... rootfs 6.6.6.6:/pwn3d ... hostname pwn3d, markers hostname ADOPTED / root NFS mount ADOPTED / default route ADOPTED via attacker / xid MATCH / origin requested 0 times; panic.txt: panic bootpc: subnet mask len is 3 with backtrace bootpc_init <- sysctl_df_test <- sysctl_root <- userland_sysctl; run.fixed.log: xid differ, bootpc: reply not from server port 67 ignored, test 4 without panic, test 22 residual documented",
  "kernel_refs": [
    "sys/vfs/nfs/bootp_subr.c:807-808",
    "sys/vfs/nfs/bootp_subr.c:838-848",
    "sys/vfs/nfs/bootp_subr.c:429-435",
    "sys/vfs/nfs/bootp_subr.c:1194-1196",
    "sys/vfs/nfs/bootp_subr.c:1538",
    "sys/vfs/nfs/bootp_subr.c:1341-1515",
    "sys/vfs/nfs/bootp_subr.c:1644-1666",
    "sys/vfs/nfs/bootp_subr.c:1142-1148",
    "sys/vfs/nfs/bootp_subr.c:1389-1391"
  ],
  "poc_changes": "replaced the assumed-impossible PXE harness with a verbatim-interposition kernel-compiled rig (stock guest has no options BOOTP); corrected the prediction model after guest observation: bootpc_compose_query bumps xid only when the ifctx is unresolved, so the OFFER and ACK share the DISCOVER xid and only a failed exchange advances it; harness stub yields via lwkt_yield on the EWOULDBLOCK path because the production wait loop relies on the blocking soreceive to re-read the non-volatile time_uptime",
  "attempts": 5,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #2 Tue Oct 20 14:47:53 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline decisive boot); fixed decisive boot #2 Tue Oct 20 16:53:09 UTC 2026",
  "runtime_sec": 85,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh  (sha256 gate + make -j6 nativekernel KERNCONF=X86_64_GENERIC + make installkernel)",
  "run_cmd": "sysctl -w kern.dfrig12=2; sysctl -n kern.dfrig12_status  (+ 20 control, 4 panic on dedicated boot; 2/21/22 on fixed kernel)",
  "code_hash": "f017052c0ccd052813c7dabfb13ea4ec0b7e28046308beef1dbef707102b9902",
  "notes": "impact recorded as panic because a kernel panic from the crafted reply was reproduced on the guest; the heavier real-world impact (attacker-chosen root NFS server = root on a diskless client, pre-auth) is demonstrated by the adoption markers but has no uid0 manifestation on a disk-booting guest whose stock kernel lacks options BOOTP. Attacker constants were stored big-endian, appearing on-wire as 66.6.6.6 / 6.6.6.6 / 1.6.6.6. Fixed source sha256 52154f24f8d0f6fd8964d8cab366e81a377c5c10c622a7b3531c293d33be0148.",
  "recommended_fix": "seed gctx->xid with karc4random() and require BOOTP replies to originate from UDP port 67 by passing a name to soreceive and checking sin_port (validated in fix.diff)"
}