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)
PoC verification
Evidence pack
findings/poc/DF-0522 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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_testablecompile 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.
No comments yet.