DF-5844 / verdict.json
{ "finding_id": "DF-5844", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "sysctl_sysctl_next_ls() recurses to MIB tree depth with no check of level against CTL_MAXNAME (12), writing attacker(KLD)-chosen 32-bit oid numbers one-per-level past int newoid[CTL_MAXNAME] on sysctl_sysctl_next()s stack. Reproduced twice from an unprivileged nobody next-walk after planting a 24-deep dynamic chain: Fatal trap 9, frame pointer = 0x4141410d4141410c (planted oids), crash at sysctl_sysctl_next+0x63 ret. Value-controlled stack smash; uid=0 chain blocked only by the fact that planting a >12-deep tree requires root (kldload / sysctl_add_oid) - no in-tree subsystem nests deeper than 5, so on a stock system no unprivileged user can arm it. Landmine for any future auto-nested oid tree; immediate kernel smash for modules that nest >12.", "exploit_chain": "root(KLD): sysctl_add_oid chain depth 24 with explicit oid numbers -> unprivileged user: sysctl({CTL_SYSCTL,CTL_SYSCTL_NEXT,...}) walk (trigger.c / sysctl -aN) -> next_ls !namelen descent recurses to tree depth -> *next = oid_number writes newoid[12..23] = 0x4141410c.. smashes saved RBP/RIP of sysctl_sysctl_next -> Fatal trap 9 at ret. Escalation to uid=0 stops at tree-planting: requires kldload (root); documented as hard blocker, not pursued further.", "evidence": "panic.txt (nobody-run: frame pointer = 0x10:0x4141410d4141410c, Stopped at sysctl_sysctl_next+0x63: ret); panic.root-run.txt (same smash, root run); run.log; fix_validation.log (patched kernel: walk finished after 1834 oids, TRIG_RC=0, guest up); VERDICT.md full narrative", "kernel_refs": [ "sys/kern/kern_sysctl.c:719", "sys/kern/kern_sysctl.c:727", "sys/kern/kern_sysctl.c:733", "sys/kern/kern_sysctl.c:749", "sys/kern/kern_sysctl.c:786", "sys/kern/kern_sysctl.c:791", "sys/sys/sysctl.h:55" ], "poc_changes": "Original sketch concept only; wrote rig from scratch: deepoid.c KLD plants 24-deep chain of CTLTYPE_NODE oids via SYSCTL_ADD_NODE with explicit recognizable oid numbers (0x41414100+i); trigger.c iterates __sysctl({CTL_SYSCTL,CTL_SYSCTL_NEXT,<prev>}) like sysctl(8) - a single bare {1,3} query only returns the first oid and does not reach the deep subtree, the walk must iterate.", "attempts": 3, "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": 3300, "guest_dirty": 0, "build_cmd": "cd /root/rig && make && cc -O -o trigger trigger.c && cp trigger /tmp/trigger && chmod 755 /tmp/trigger", "run_cmd": "kldload /root/rig/deepoid.ko && su -m nobody -c /tmp/trigger", "code_hash": "64bd2b51c5235dae8ce3b005375604c14a93a5d94cc69b02141135651a678df4 (trigger.c); 1463a6615fc2351d88de0b063bdbd2c5ea20368ced83be50317f1a1bcc8e01ab (deepoid.c)", "notes": "Natural MIB depth measured 5 on guest (precondition evidence). Walk is read-only, no privilege gate on next_ls path. OUT of j>12 ints (kern_sysctl.c:791) would additionally leak stack past newoid if the smash did not fault first. Baseline panic reproduced twice (root + nobody runs).", "recommended_fix": "Bound the walk: reject level > CTL_MAXNAME at sysctl_sysctl_next_ls entry (fix.diff validated on guest)", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff applied to guest /usr/src/sys/kern/kern_sysctl.c, kernel rebuilt (nativekernel, BUILD_RC=0), installed, rebooted; identical rig + identical su -m nobody trigger now completes cleanly (walk finished after 1834 oids: ENOENT, TRIG_RC=0, sysctl -aN succeeds, guest stays up). Bad behavior gone.", "fix_evidence": "fix_validation.log; fix.diff; env.txt (patched kernel #1 uname)" } |