DF-3286 / verdict.json
{ "finding_id": "DF-3286", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "mesh_decap memcpy(b, m_data, hdrlen) copies 56 bytes (DSTODS hdrspace 32 + AE=11 mesh control 24) into the 50-byte stack buffer b[sizeof(qosframe_addr4)+sizeof(meshcntl_ae10)] because mesh_input never rejects the reserved AE=11 bits. The 6-byte OOB write of attacker-controlled bytes (would-be mc_addr6) is proven to execute on the stock INVARIANTS kernel: shipped-binary disassembly shows the 50-byte object at rbp-0x62 and a rep movsb of 56, and the is_mesh_badae counter (bumped after the memcpy) increments on each injected frame. On this exact gcc-8.3 build the 6 bytes land in stack padding (saved registers begin 2 bytes beyond), so no crash is observed - the primitive is a layout-latent remote stack overwrite; 8 more bytes would reach saved rbx.", "exploit_chain": "attacker mesh STA -> auto-peers with victim (default ms_flags AP|FWD) -> single 68-byte multicast DSTODS QoS data frame with mc_flags AE=11 -> mesh_input computes meshdrlen=24, pulls 56-byte header -> group path reaches mesh_decap -> memcpy overflows b[] by 6 attacker-chosen bytes -> (this build: padding; relayout: stack corruption)", "evidence": "run.log: is_mesh_badae=1 -> =21 after repeated step 10, GUEST_STILL_UP\nrun.log: objdump excerpt - lea -0x62(%rbp),%r14 / rep movsb with hdrlen=56\nVERDICT.md: full disasm + layout analysis", "kernel_refs": [ "sys/netproto/802_11/wlan/ieee80211_mesh.c:1281", "sys/netproto/802_11/wlan/ieee80211_mesh.c:1296", "sys/netproto/802_11/wlan/ieee80211_mesh.c:1738", "sys/netproto/802_11/wlan/ieee80211_mesh.c:1741" ], "poc_changes": "wrote mesh-capable harness from scratch (fwm.c, extends DF-3282 fw.c pattern): MBSS vap, synchronous beacon+OPEN+CONFIRM peering dance, RX injection via ieee80211_input_mimo, TX capture in ic_raw_xmit; learned victim mlpid from captured OPEN frames", "attempts": 4, "guest_uname": "DragonFly dfbsd 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": 1500, "guest_dirty": 1, "build_cmd": "sh findings/poc/DF-3286/build.sh (make SYSDIR=/usr/src/sys in /root/fwm)", "run_cmd": "sh findings/poc/DF-3286/run.sh (sysctl kern.fwm_step=4 then =10)", "code_hash": "1b0e0b1a43331b1bc92e2614476535a88b88d3863db6ef91a36eead5c8ad7503", "notes": "impact=none because the write is absorbed by padding on this build; the memory-corruption primitive itself is certain. Fix kernel validation: see fix_* fields.", "recommended_fix": "reject ae > IEEE80211_MESH_AE_10 in mesh_input before computing meshdrlen (see fix.diff hunk 4)", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Thu Sep 10 05:34:08 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (fix kernel)", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "AE=11 frames rejected at the new mesh_input gate (line-annotated disasm of fixed kernel mesh.c:1766-1770); the 56-byte decap memcpy is unreachable for AE=11; legal AE=0 data still decaps+delivers (no regression); no panic", "fix_evidence": [ "fix.log: binary proof + AE3_REJECTED_EARLY / AE0_DELIVERED_NO_CRASH" ] } |