โฌข DragonFlyBSD Kernel Audit
DF-2894 / verdict.json
โ† back to finding โ†“ download raw
{
  "finding_id": "DF-2894",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "kenv_init() copies bootloader-supplied env strings of unbounded length into the dynamic kenv table (only the entry count is bounded), while kgetenv() strcpy()s the looked-up value into a fixed 258-byte kernel-stack buffer; an unprivileged kenv(2) KENV_GET on a boot-planted >=258-byte value smashed the kernel stack (Fatal trap 9, saved RBP=0x4141414141414141, fault at kgetenv's ret) and took the guest down. Planting requires loader.conf/boot-prompt control, so the demonstrated unpriv impact is panic; the smash itself is byte-controlled with no canary/SMEP/SMAP/KASLR, giving a credible boot-time-plant -> unpriv-detonate code-exec ceiling. Ingest clamp validated by in-guest kernel rebuild: entry dropped at boot, PoC returns ENOENT, guest healthy.",
  "exploit_chain": "1) privileged/console plant: audit.smash=\"<byte-controlled >=258-byte value>\" in /boot/loader.conf (data-only persistence); 2) kenv_init (SI_BOOT1_POST) ingests it unvalidated; 3) any unprivileged user: kenv(2) KENV_GET audit.smash -> kgetenv -> strcpy past buf[258] -> saved frame/return address overwritten with planter-chosen bytes (strcpy payload, NUL-free); 4) demonstrated: GPF panic at kgetenv ret; on this no-SMEP/SMAP/KASLR/no-canary guest, steps 3-4 are RIP control -> ROP -> uid=0 (chain start still requires the privileged plant, hence impact=panic).",
  "evidence": [
    "run.log โ€” control GET returns (exit 0); trigger GET never returns (ssh hung)",
    "panic.txt โ€” 'Fatal trap 9 ... frame pointer = 0x4141414141414141' / 'Stopped at kgetenv.part.4+0xea: ret' / current process = unpriv trigger",
    "env.txt โ€” plant lines + stock-kernel identity (kernel #0, X86_64_GENERIC)",
    "fix_run.log โ€” fixed kernel #1: boot warning 'oversize entry (713 bytes)', table entry absent, trigger -> ENOENT exit 1, guest up",
    "fix_build.log โ€” make nativekernel KBUILD_DONE_OK; kernel CFLAGS show -fno-stack-protector",
    "fix.diff โ€” ingest clamp in kenv_init + strlen guard in kgetenv"
  ],
  "kernel_refs": [
    "sys/kern/kern_environment.c:580-588",
    "sys/kern/kern_environment.c:231",
    "sys/kern/kern_environment.c:239",
    "sys/kern/kern_environment.c:135-136",
    "sys/kern/kern_environment.c:263-266",
    "sys/platform/pc64/x86_64/machdep.c:2669"
  ],
  "poc_changes": "seed sketch was never runnable: rebuilt as a plain kenv(3) userland trigger (libc exports kenv; implicit-decl warning is cosmetic); plant done via /boot/loader.conf + in-guest awk for the 700-byte value; /tmp is tmpfs so the binary was rebuilt after each reboot.",
  "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": 1150,
  "guest_dirty": 1,
  "build_cmd": "cc -O2 -o /tmp/df2894_trigger /tmp/df2894_trigger.c   (in guest, as uid=1001)",
  "run_cmd": "kenv | grep -c '^audit.smash=' ; /tmp/df2894_trigger audit.hello ; /tmp/df2894_trigger audit.smash   (in guest, as uid=1001, after loader.conf plant + reboot)",
  "code_hash": "ae62c631b68d0bb960dfa4257035d5663e6c7ea481ae582c6f54aa6a6599e7e6",
  "notes": "DUMP path clamp, ksetenv exact-fit math, kunsetenv compaction, alloc-before-lock/free-after-unlock, kfreeenv static->dynamic window (SYSINIT boot is single-threaded), and kgetenv_quad suffix parsing were all checked and killed. Guest left mid-validation on patched kernel; reset to with-src snapshot afterwards.",
  "recommended_fix": "kenv_init(): drop boot-env entries longer than KENV_MNAMELEN+KENV_MVALLEN (mirror ksetenv limits) with a boot warning; defense-in-depth strlen(cp)>=sizeof(buf) guard in kgetenv().",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep  2 22:58:18 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Identical loader.conf plant + identical unpriv PoC on baseline kernel #0 -> GPF panic (RBP=0x41..), guest down; on fixed kernel #1 -> boot warning drops the 713-byte entry, KENV_GET returns ENOENT, control var unaffected, guest stays up.",
  "fix_evidence": [
    "fix_run.log",
    "fix_build.log"
  ]
}