DF-2738 / verdict.json
{ "finding_id": "DF-2738", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Reproduced twice on the stock INVARIANTS guest (DragonFly 6.5-DEVELOPMENT #0 X86_64_GENERIC): a statically linked writer executed as uid 0 inside a jail(8) successfully wrote four host-global sysctls (kern.sugid_coredump, kern.maxfiles, debug.sysctl, kern.securelevel) and the host observed kern.sugid_coredump flip 0->1. Root cause is purely in the write gate chain: sysctl_root (sys/kern/kern_sysctl.c:1438-1450) never consults CTLFLAG_PRISON, whose header comment (sys/sys/sysctl.h:97 'Prisoned roots can fiddle') documents exactly the missing semantics; caps_priv_check delegates to prison_priv_check which deliberately allows SYSCAP_NOSYSCTL_WR inside jails (sys/kern/kern_jail.c:883-885). Only kern.hostname is safe because its handler implements jail semantics itself (kern_mib.c:214-235). Impact ceiling is arbitrary host-global kernel-state mutation from a confined root: host-wide DoS (kern.maxfiles et al.), shared network-stack reconfiguration, kern.sugid_coredump=1 (host credential-disclosure assist), kern.securelevel raising while host securelevel <= 0. Classed dos (closest enum); it is a jail-confinement integrity/availability break, not a direct host uid=0.", "exploit_chain": "hosting scenario: attacker holds uid 0 inside a jail -> sysctlbyname(\"kern.sugid_coredump\", NULL,NULL,&one,4) (or any plain CTLFLAG_RW variable) passes sysctl_root's write gate because prison_priv_check returns 0 for SYSCAP_NOSYSCTL_WR -> handler writes the host-global variable -> host state mutated across the jail boundary; e.g. kern.maxfiles/* denial-of-service for the whole host and other jails, or kern.sugid_coredump=1 to enable host-wide setuid core dumps.", "evidence": [ "findings/poc/DF-2738/run.log and run.2.log: 'write kern.sugid_coredump=1 -> r=0 WROTE-HOST-GLOBAL' x4 inside jail; '=== HOST AFTER ===' shows kern.sugid_coredump = 1 (was 0)", "sys/kern/kern_jail.c:883-885: prison_priv_check returns 0 (allowed) for SYSCAP_NOSYSCTL_WR in jails ('group 2 allowed')", "sys/sys/sysctl.h:97: CTLFLAG_PRISON defined 'Prisoned roots can fiddle' but tree-wide grep shows zero enforcement (only kern_mib.c:242 and if_fxp.c:507/511 set it)", "sys/kern/kern_mib.c:214-235: kern.hostname handler does its own jailed() redirect to prison->pr_host - the only node with real jail write semantics", "findings/poc/DF-2737/run_user_prober.log control: unprivileged uid!=0 write to debug.sysctl -> EPERM (the caps gate itself works; it is jail policy that is open)" ], "kernel_refs": [ "sys/kern/kern_sysctl.c:1438", "sys/kern/kern_sysctl.c:1446", "sys/sys/sysctl.h:97", "sys/kern/kern_jail.c:883", "sys/kern/kern_caps.c:311", "sys/kern/kern_mib.c:214", "sys/kern/kern_proc.c:2204" ], "poc_changes": "authored fresh (no seed): static cc -static writer so the jail needs no dynamic loader or filesystem beyond the binary itself; initial dynamic-chroot attempt segfaulted (guest ld-elf.so.2 in minimal tree), replaced with -static.", "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": 95, "guest_dirty": 0, "build_cmd": "cc -static -o /tmp/jailwrite jailwrite.c", "run_cmd": "sh run.sh (as real root on the guest; creates jail jtest-DF2738, runs /jailwrite inside)", "code_hash": "f5205e3764e3512000c8f8c6e604f5303483636b20fd9ef0a420ad91edba3706", "notes": "Setup requires real root to create the jail - that is the deployment model under test (root inside a customer jail). Unprivileged (non-root) writes are still EPERM. Host state restored after both runs; guest /usr/src reverted to pristine after the compile-check (md5 match). Severity filed as Medium (local, requires jail deployment), impact enum dos is the closest machine label; integrity ceiling is higher (host kernel-state rewrite).", "recommended_fix": "Deny jailed-credential writes to nodes lacking CTLFLAG_PRISON in sysctl_root (kern_sysctl.c:1446-1450) and flag kern.proc.args CTLFLAG_PRISON (it is the one legitimate jailed write, already curproc-checked) - see fix.diff", "fix_status": "inconclusive", "fix_kernel_uname": "", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff is git-apply-clean and both touched objects (kern_sysctl.o, kern_proc.o) compile with the stock kernel CFLAGS incl. -Werror inside the guest build env (build_fix.log). Full kernel build + boot + in-jail re-test was not run (Medium severity, non-corruption; the gate mirrors the already-runtime-proven caps/ANYBODY gate shape, so behavior post-patch is EPERM for jailed writes to non-PRISON nodes). Compile-verified only => inconclusive rather than fixed.", "fix_evidence": ["findings/poc/DF-2738/build_fix.log (OBJ-BUILD-OK, -Werror, guest source reverted md5-verified)"] } |