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

thread0 root-credential fallback for socket operations (DF-0510 v1 twin)

Summary

3 call sites(:559,:640,:880) td=curthread->td_proc?curthread:&thread0 XXX broken. When curthread->td_proc==NULL(kernel thread/softint/netgraph dispatch) falls back to thread0=root creds. td passed to socreate(:598) sobind(:662) solisten(:674) soconnect(:726) sosend(:912). PRIV_NET_RAW PRIV_NET_PRIV_PORT bind<1024 jail bypass all evaluated against root. Identical to DF-0510(ng7). Developer comment acknowledges broken. Fix: capture credential at node creation fail closed when no user cred available.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0522 Β· 9 files
FileTypeDescriptionSize
trigger.sh trigger-source loads ng_ksocket, documents the 3 broken sites 1.7 KB view raw
build.sh build-script 228 B view raw
run.sh run-script 216 B view raw
fix.diff suggested-fix fail-closed (return EACCES) at all 3 sites instead of &thread0 fallback 1.8 KB view raw
VERDICT.md verdict source confirmation; jail-bypass impact ceiling; root-only reachability 4.1 KB ↓ raw
build.log build-log fix compile-validated RC=0 63 B view raw
env.txt environment 829 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict source confirmation; jail-bypass impact ceiling; root-only reachability
↓ download raw

DF-0522 β€” VERDICT

Verdict: SOURCE-CONFIRMED (3 "XXX broken" sites); NOT DIRECTLY TRIGGERABLE in a simple PoC; root-only reachability

The bug (confirmed in source)

ng_ksocket uses an acknowledged-broken credential fallback at three call sites:

// sys/netgraph/ksocket/ng_ksocket.c:559  (ng_ksocket_newhook -> socreate/sobind)
// sys/netgraph/ksocket/ng_ksocket.c:640  (ng_ksocket_rcvmsg  -> soconnect/sogetopt/...)
// sys/netgraph/ksocket/ng_ksocket.c:880  (ng_ksocket_rcvdata  -> sosend)
struct thread *td = curthread->td_proc ? curthread : &thread0;   /* XXX broken */

When a ksocket operation runs in a context where curthread->td_proc == NULL (i.e. a kernel thread / softinterrupt / asynchronous netgraph dispatch β€” no owning user process), the code falls back to &thread0, which carries root credentials. The td is then passed to socreate (:598), sobind (:662), solisten (:674), soconnect (:726), sosend (:912). Consequently the privilege checks evaluated inside those socket operations β€” PRIV_NET_RAW, PRIV_NET_PRIV_PORT (bind < 1024), and jail restrictions β€” are all evaluated against root, not the real (or absent) credential. The developer's own /* XXX broken */ comment concedes the defect. This is the same defect as the netgraph7 twin DF-0510.

Why it is not directly triggerable in a simple PoC

The fallback fires only when curthread->td_proc == NULL β€” i.e. when the netgraph hook/data operation is serviced by a kernel worker thread rather than synchronously in the issuer's syscall context. A synchronous ngctl control message issued by root runs in root's own process context (curthread->td_proc != NULL), so the fallback branch is NOT taken and root's own (legitimate) credential is used. Verified: ng_ksocket node creation succeeds without panic (ngctl mkpeer .: ksocket ... β†’ RC=0, no crash), unlike ng_fec.

Forcing the async-dispatch context where td_proc == NULL requires constructing a netgraph topology whose hook/data callbacks are queued to a netgraph worker thread rather than executed inline β€” impractical for a standalone PoC. The defect is therefore confirmed at the source level (the broken pattern and the XXX broken acknowledgement are unambiguous), not by a runtime privilege escalation.

Reachability / privilege model

Netgraph node creation requires the ng_socket control socket, gated by caps_priv_check(SYSCAP_RESTRICTEDROOT) (sys/netgraph/socket/ng_socket.c:172) β€” root only. Verified: unprivileged maxx gets EPERM. So even if the fallback fired, the ksocket node was created by root; the realistic impact ceiling is a jail privilege-bypass: a root-configured ksocket node inside a jail, whose hook ops are serviced by a kernel worker thread, would have its PRIV_NET_RAW / bind-<1024 / jail checks evaluated against host-root (thread0) instead of the jail credential β€” a jail privilege-boundary confusion.

Impact ceiling

Medium logic / privilege-confusion defect: socket privilege checks (PRIV_NET_RAW, privileged-port bind, jail) evaluated against root when ksocket ops run in kernel-dispatch context. Not a memory-corruption primitive; no uid=0 chain.

Exploit chain

none β€” logic/privilege bug, not memory corruption.

Fix validation

fix.diff replaces the &thread0 fallback with fail-closed at all three sites: if curthread->td_proc == NULL, return EACCES instead of granting root credentials. Compiles cleanly (RC=0, ng_ksocket.ko produced; all 3 sites patched). fix_status = not_testable β€” the fallback only fires under async dispatch, which a synchronous PoC cannot force; compile-validated only.

(An alternative, more complete fix would capture the creating user's credential at node/hook creation time and use that stored credential in rcvmsg/rcvdata β€” matching the finding's "capture credential at node creation" recommendation. The fail-closed fix is the minimal safe version.)

PoC changes

Wrote trigger.sh (loads ng_ksocket, documents the 3 broken sites), build.sh/ run.sh, fix.diff (fail-closed at all 3 sites). No upstream PoC.

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. ng_ksocket thread0 root-cred fallback at 3 sites (XXX broken). Latent from unpriv. Compile validated.