GitHub Security Advisory: GHSA-p9mg-74mg-cwwr
Package: github.com/free5gc/smf (GO)
Severity: HIGH (CVSS 8.2)
CWE: CWE-306: Missing Authentication for Critical Function, CWE-476: NULL Pointer Dereference, CWE-862: Missing Authorization
Vulnerable versions: < 1.4.3
Patched in: 1.4.3

Summary: free5GC's SMF UPI DELETE /upi/v1/upNodesLinks/{ref} panics on AN-node deletion via nil UPF dereference; unauthenticated, state-mutating

Details:
### Summary
free5GC's SMF mounts the `UPI` management route group without inbound OAuth2 middleware (same root cause as the broader UPI auth gap reported in free5gc/free5gc#887). On top of that, the `DELETE /upi/v1/upNodesLinks/{upNodeRef}` handler unconditionally dereferences `upNode.UPF` after the type-guarded async release, even though `AN`-typed nodes are constructed without a `UPF` object. As a result, a single unauthenticated `DELETE /upi/v1/upNodesLinks/gNB1` request crashes the handler with a nil-pointer panic AND mutates the in-memory user-plane topology before panicking (the `UpNodeDelete(upNodeRef)` line runs first). This is an unauthenticated, state-mutating panic-DoS sink that an off-path network attacker can trigger by name against any AN entry.

### Details
Validated against the SMF container in the official Docker compose lab.
- Source repo tag: `v4.2.1`
- Running Docker image: `free5gc/smf:v4.2.1`
- Runtime SMF commit: `8385c00a`
- Docker validation date: 2026-03-22 local (container log timestamp `2026-03-21T23:43:17Z`)
- SMF endpoint: `http://10.100.200.6:8000`

Control comparison on the same SMF instance:
- `GET /nsmf-oam/v1/` (no token) -> `401 Unauthorized`
- `DELETE /upi/v1/upNodesLinks/gNB1` (no token) -> `500 Internal Server Error` (panic)

The sibling `nsmf-oam` returning `401` proves OAuth middleware IS wired in for other SMF route groups; the UPI group specifically is mounted without it.

Vulnerable handler logic (paths in `free5gc/smf`):
```go
// NFs/smf/internal/sbi/api_upi.go:94..99
if upNode.Type == smf_context.UPNODE_UPF {
    go s.Processor().ReleaseAllResourcesOfUPF(upNode.UPF)
}
upi.UpNodeDelete(upNodeRef)
upNode.UPF.CancelAssociation()   // <-- panics for AN-typed nodes; nil UPF
```

The `Type == UPNODE_UPF` guard only protects the asynchronous `ReleaseAllResourcesOfUPF` call. After that, `UpNodeDelete(upNodeRef)` runs unconditionally (so the topology mutation lands first), and then `upNode.UPF.CancelAssociation()` is called unconditionally on a `*UPF` that is `nil` for `AN` nodes by construction.

Code evidence:
- UPI group mounted WITHOUT auth middleware:
  - `NFs/smf/internal/sbi/server.go:76`
  - `NFs/smf/internal/sbi/server.go:78`
- Protected control comparison (other SMF groups DO use auth):
  - `NFs/smf/internal/sbi/server.go:99`
  - `NFs/smf/internal/sbi/server.go:105`
- Delete handler (panic site):
  - `NFs/smf/internal/sbi/api_upi.go:94`
  - `NFs/smf/internal/sbi/api_upi.go:99`
- AN nodes are constructed without a UPF object (root cause of the nil deref):
  - `NFs/smf/internal/context/user_plane_information.go:95`
  - `NFs/smf/internal/context/user_plane_information.go:97`

### PoC
Reproduced end-to-end against the running SMF at `http://10.100.200.6:8000`.

1. Control: protected sibling OAM route returns `401`:
```
curl -i http://10.100.200.6:8000/nsmf-oam/v1/
```
```
HTTP/1.1 401 Unauthorized
```

2. Trigger: unauthenticated DELETE on the default AN node `gNB1`:
```
curl -i -X DELETE http://10.100.2