β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2950

Jailed uid-0 passes the sysctl write gate and can write every host-global RW MIB defined here β€” kern.securelevel raised irreversibly from inside a jail (host root gets EPERM lowering it), plus kern.hostid/domainname/bootfile/maxprocperuid/maxposixlocksperuid/tls_extra corruption; CTLFLAG_PRISON is dead

Field Value
ID DF-2950
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:H/A:H
CWE CWE-863 Incorrect Authorization
File sys/kern/kern_mib.c
Lines 142-153, 178-181, 248-271 (gate: kern_sysctl.c:1445-1450)
Area kern + jail
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket privesc
Reported pending
Known CVE none
CVE match novel

Summary

sysctl_root's only write-privilege gate is caps_priv_check(td_ucred, SYSCAP_NOSYSCTL_WR). caps_priv_check rewrites group-2 leaf caps to the meta group SYSCAP_SENSITIVEROOT (kern_caps.c:336-348) and prison_priv_check blanket-allows that meta group for jailed creds (kern_jail.c:854-857). Every CTLFLAG_RW oid in kern_mib.c therefore becomes writable by a jailed uid-0: kern.securelevel (no jail check) can be raised one-way β€” the ratchet denies lowering to host root too, permanently locking CTLFLAG_SECURE sysctls and all securelevel-gated kernel paths; kern.hostid, kern.domainname, kern.bootfile/kernelname, kern.maxprocperuid, kern.maxposixlocksperuid, kern.tls_extra are equally host-global and jail-writable. CTLFLAG_PRISON ('Prisoned roots can fiddle') exists for exactly this distinction but is consulted nowhere in the tree except kern.hostname. Root cause shared with DF-2800 (which covered only the time syscalls); this is the sysctl-write sink with a distinct file, impact, and fix locus. VERIFIED on the stock guest: jailed root wrote kern.securelevel βˆ’1β†’1 rc=0; host root then got EPERM lowering it; kern.hostid 0β†’4242, kern.domainnameβ†’pwned-by-jail, kern.maxposixlocksperuid 32528β†’1, kern.tls_extraβ†’777. Jailed nobody and host unpriv correctly get EPERM β€” the gate is specifically jail-blind. No uid0-on-host route (authorization flaw) β€” impact ceiling is host-global state manipulation: irreversible host administration DoS. Fix validated in-guest (CTLFLAG_PRISON enforcement at the core write gate): jailed uid-0 β†’ EPERM on all tested oids with values unchanged, jail-scoped hostname read and host root writes unaffected.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of kern_mib.c (GLM 5.3); reproduced from jail + fix validated; DF-0181 re-verified still present (live corroboration captured).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2950 Β· 11 files
FileTypeDescriptionSize
poc_jail_sysctl.sh β€” 1.4 KB view raw
poc_jail_sysctl2.sh β€” 1.2 KB view raw
run.log β€” 2.3 KB view raw
run.2.log β€” 2.2 KB view raw
df0181_live_corroboration.txt β€” 2.2 KB view raw
env.txt β€” 208 B view raw
fixtest.sh β€” 1004 B view raw
fixtest.log β€” 1.9 KB view raw
fix.diff β€” 783 B view raw
VERDICT.md β€” 4.1 KB ↓ raw
verdict.json β€” 4.8 KB view raw
VERDICT.md
↓ download raw

DF-2950 VERDICT

Status: reproduced (impact=dos, S:C). Confidence: certain.

How it was reproduced

Stock guest, kernel DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (env.txt). jail(8) created plain jails (no special caps) on the host; commands inside run with uid=0 and the jail's prison.

  1. jail / df2950ja 127.0.0.4 /bin/sh -c 'id; sysctl -w kern.securelevel=1' β†’ uid=0(root)... and kern.securelevel: -1 -> 1 rc=0. Path: userland_sysctl (kern_sysctl.c:1569-1576) β†’ sysctl_root:1441 (no CTLFLAG_SECURE on this oid) β†’ :1447 caps_priv_check(td->td_ucred, SYSCAP_NOSYSCTL_WR): cr_uid==0 passes the uid test (kern_caps.c:328), group-2 rewrite (kern_caps.c:337-341) turns the cap into SYSCAP_SENSITIVEROOT, prison_priv_check returns 0 (kern_jail.c:856-857) β†’ handler sysctl_kern_securelvl (kern_mib.c:248-261) has no jail check and level 1 β‰₯ -1 β†’ securelevel = 1.
  2. Host root sysctl -w kern.securelevel=0 β†’ sysctl: kern.securelevel=0: Operation not permitted (sysctl_kern_securelvl's ratchet, kern_mib.c:257-258, applies to host root). The damage is permanent until reboot: CTLFLAG_SECURE sysctls become unwritable (kern_sysctl.c:1441-1444) and every other securelevel-gated kernel path engages. This is a host-wide administration denial-of-service from inside a jail.
  3. Same gate lets jailed uid-0 corrupt host-global identity and resource MIBs (run.2.log [3]/[6]): kern.hostid 0β†’4242, kern.domainname ""β†’pwned-by-jail, kern.maxposixlocksperuid 32528β†’1, kern.tls_extra 6144β†’777 (also kern.bootfile/kernelname and kern.maxprocperuid are in the same class).
  4. Control cases behave exactly as the code predicts: jailed non-root β†’ EPERM (caps_priv_check uid test, kern_caps.c:328-331); host unprivileged user β†’ EPERM. The hole is specifically that the gate is jail-blind for uid-0-in-jail.

Relationship to DF-2800

DF-2800 (kern_jail.c) found the same root cause (group-2 blanket allow, meta-group rewrite) for the time syscalls. This finding is the sysctl-write sink: different file (sys/kern/kern_mib.c oids are the demonstrated attack surface), different impact (irreversible securelevel ratchet + host/cross-jail global-state corruption), fixed most naturally at the sysctl core by making CTLFLAG_PRISON mean something (see fix.diff). Not a re-report: DF-2800's scope ends at settimeofday/clock_settime/adjtime/ntp_adjtime.

Side observation

The first run (run.log) wedged the guest after its final step: a jail hostname write without PRISON_CAP_SYS_SET_HOSTNAME returns EPERM from sysctl_hostname while holding the all-CPU sysctl XLOCK (kern_mib.c:217-226) β€” known DF-0181, still unfixed on this kernel. Captured in df0181_live_corroboration.txt. My finding's evidence (steps [1]-[5]) completed before that wedge.

Fix validation

fix.diff = gate jailed writes on CTLFLAG_PRISON in sysctl_root (kern_sysctl.c, +#include <sys/jail.h> for the jailed() inline, sys/jail.h:158). Applied on a vm.sh reset with-src guest, built with make -j4 nativekernel KERNCONF=X86_64_GENERIC (rc=0), installed, rebooted into #1: Fri Sep 4 00:17:28 UTC 2026. fixtest.log: jailed uid-0 β†’ EPERM for kern.securelevel / kern.hostid / kern.domainname / kern.maxposixlocksperuid, all values unchanged; jail-scoped kern.hostname READ still works; host root writes still work. Baseline reproduced=1, patched reproduced=0 β†’ fix_status=fixed. Guest reset to stock afterwards.

Exploit chain

host runs a jail for an untrusted tenant/service (uid 0 inside the jail = attacker) β†’ sysctl -w kern.securelevel=1 (or higher) β†’ host-global irreversible ratchet: host root cannot lower it; at level β‰₯1 further CTLFLAG_SECURE knobs lock, /dev/mem closes, immutable flags engage β†’ permanent host administration DoS; additionally hostid/domainname/ bootfile/maxprocperuid corruption leaks across the jail boundary into the host and every other jail. Ceiling for this bug class is host-global state manipulation, not uid=0-on-host: memory-safety is not involved, so no escalation chain beyond the integrity/availability impact exists.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched kernel (#1, nativekernel build with fix.diff): jailed uid-0 gets EPERM on kern.securelevel/kern.hostid/kern.domainname/kern.maxposixlocksperuid and values stay unchanged; jail-scoped kern.hostname read still works; host root write (kern.hostid=99) still works. No over-restriction observed. Baseline had reproduced=1 on stock #0.

['fixtest.log:F1 jailed uid-0 EPERM, kern.securelevel stays -1', 'fixtest.log:F2 hostid/domainname/maxposixlocks EPERM, values unchanged', 'fixtest.log:F3 jail hostname read OK (df2950fc)', 'fixtest.log:F4 host root kern.hostid 0->99 OK']
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Fri Sep 4 00:17:28 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Exploit chain

host-root creates jail for untrusted tenant -> attacker holds uid 0 inside jail -> sysctl -w kern.securelevel=1 -> host-global irreversible ratchet (host root cannot lower; CTLFLAG_SECURE sysctls permanently locked at level>=1) -> permanent host administration denial-of-service; same gate permits host/cross-jail corruption of kern.hostid, kern.domainname, kern.bootfile/kernelname, kern.maxprocperuid, kern.maxposixlocksperuid, kern.tls_extra

Evidence (decisive lines)

['run.2.log:14 kern.securelevel: -1 -> 1 (jailed uid-0, rc=0)', 'run.2.log:19-20 host root: sysctl: kern.securelevel=0: Operation not permitted', 'run.2.log:27-32 kern.hostid 0->4242, kern.domainname ->pwned-by-jail from jail', 'run.2.log:40-47 jailed nobody EPERM + host unpriv (su maxx) EPERM control cases', 'run.2.log:52-55 kern.maxposixlocksperuid 32528->1, kern.tls_extra 6144->777 from jail', 'run.log:53-55 + df0181_live_corroboration.txt: incidental live re-trigger of DF-0181 XLOCK wedge (guest down)', 'fixtest.log: jailed uid-0 EPERM on all oids under patched kernel #1; host root write still OK']

PoC changes

Seed sketch corrected: step [5] of run 1 mislabeled host-root as unprivileged (runs inside the root ssh session); run 2 uses su -m maxx for a genuine unprivileged host-user control. Jail-hostname-write step removed from run 2 because on this guest the jail lacks PRISON_CAP_SYS_SET_HOSTNAME and the write exercises known DF-0181 (XLOCK leak -> guest wedge), truncating run 1; that wedge is preserved as side-evidence.

Verified recommended fix

In sysctl_root (kern_sysctl.c), deny writes from jailed creds unless the oid has CTLFLAG_PRISON; see fix.diff (also makes the documented-but-dead CTLFLAG_PRISON flag meaningful).

Verdict

On the stock INVARIANTS guest, a plain jail(2) tenant with uid 0 inside the jail successfully wrote host-global MIBs: kern.securelevel -1->1 (rc=0), after which HOST root got EPERM lowering it (irreversible host-admin DoS), plus kern.hostid 0->4242, kern.domainname ->pwned-by-jail, kern.maxposixlocksperuid 32528->1, kern.tls_extra->777. Root cause: sysctl_root's only write gate is caps_priv_check(SYSCAP_NOSYSCTL_WR) (kern_sysctl.c:1447), which the group-2 meta rewrite (kern_caps.c:337-341) turns into SYSCAP_SENSITIVEROOT, blanket-allowed in jails (kern_jail.c:856-857); none of the kern_mib.c RW oids have a jail check and CTLFLAG_PRISON (sysctl.h:97) is consulted nowhere. Control cases: jailed nobody EPERM, host unpriv EPERM - the gate is specifically jail-blind for jailed uid-0. Jailed-root hostname write without the SET_HOSTNAME cap additionally re-triggered known DF-0181's XLOCK leak live (guest wedged; corroboration file). fix.diff (kern_sysctl.c: enforce CTLFLAG_PRISON for jailed writes) built via make nativekernel and validated: jailed uid-0 now EPERM on all tested oids, host root unaffected.