# DF-0510 — Credential bypass via thread0 fallback in socket operations

## Verdict: CODE-PATH CONFIRMED; latent from unprivileged user (root-only netgraph access)

## Mechanism (confirmed at code level)

Three call sites in **sys/netgraph7/ksocket/ng_ksocket.c** use the
identical broken expression (developer's own comment "XXX broken"):

- **line 546** (`ng_ksocket_newhook`): `td` passed to `socreate()`
- **line 661** (`ng_ksocket_rcvmsg`): `td` passed to `sobind()`,
  `solisten()`, `soconnect()`, `sosend()`, `soreceive()`
- **line 886** (`ng_ksocket_rcvdata`): `td` passed to `sosend()`

```c
struct thread *td = curthread->td_proc ? curthread : &thread0;  /* XXX broken */
```

When netgraph message processing is deferred to a kernel thread /
softirq / netisr (which is normal for netgraph async dispatch),
`curthread->td_proc` is `NULL`.  The fallback `&thread0` is the
kernel's boot thread, whose `td_ucred` is the **root credential**
(cr0: uid=0, all groups, all privileges).

All downstream privilege checks then evaluate against root:

- `socreate(AF_INET, ..., SOCK_RAW, ...)` → `PRIV_NET_RAW` /
  `PRIV_NET_PRIV_SOCKET` → **granted** (root has all caps)
- `sobind()` to a privileged port (<1024) → `PRIV_NET_PRIV_PORT` →
  **granted**
- `sobind()` to a jail-restricted address → jail check **bypassed**
  (root cred has no jail)
- `sosend()` / `soreceive()` → firewall / packet-filter checks
  evaluated as root

The **v1 twin** in `sys/netgraph/ksocket/ng_ksocket.c` (the actually
loadable `ng_ksocket.ko` module) has the same defect at lines 559,
640, 880.

## Reachability — why this is "latent from unprivileged"

`ng_ksocket` is driven via netgraph messages.  Creating a netgraph
control socket (`socket(AF_NETGRAPH)`) requires **root**:

```
$ ngctl list
ngctl: socket: Operation not permitted
```

So an unprivileged user cannot directly reach this code path.  The
defect matters in two realistic scenarios where the *trigger* runs
from a privileged-but-restricted context:

1. **Jail escape**: a jailed root user creates a `ng_ksocket`.  If
   any message processing is deferred to a kernel thread, the
   subsequent socket ops run with **full host-root creds** instead
   of the jail-restricted cred.  The jail's IP-binding restriction
   is bypassed; the jailed root can `sobind()` to a non-jail address.
2. **Capsicum sandbox escape**: same pattern — a capsicum-restricted
   process using netgraph gets root-cred socket ops when the message
   is processed asynchronously.

Severity Medium is appropriate: real credential-confusion defect
with a concrete sandbox-escape impact, but gated behind privileged
netgraph access (so it's a priv-boundary issue within root contexts,
not unpriv→root).

## Fix

`fix.diff` replaces the `&thread0` fallback with `NULL` and adds an
explicit `EPERM` guard at each of the three sites:

```c
struct thread *td = curthread->td_proc ? curthread : NULL;
...
if (td == NULL)
    return (EPERM);   /* refuse instead of escalating to root creds */
```

This matches the finding's recommended fix ("refuse when td_proc==NULL
or store crhold at node creation").  A more complete fix would capture
`crhold()` at node creation and reuse it; the conservative refuse
fix is minimal and safe.

## Build / run

```
ssh dfbsd-maxx 'mkdir -p poc/DF-0510'
scp findings/poc/DF-0510/{df0510.c,build.sh,run.sh} dfbsd-maxx:poc/DF-0510/
ssh dfbsd-maxx 'cd poc/DF-0510 && sh ./build.sh && sh ./run.sh'
# static code-path analysis; no live kernel trigger (defense-in-depth)
```
