GitHub Security Advisory: GHSA-rxrq-fv76-26pr
Package: github.com/free5gc/nef (GO)
Severity: HIGH (CVSS 7.5)
CWE: CWE-20: Improper Input Validation, CWE-617: Reachable Assertion, CWE-755: Improper Handling of Exceptional Conditions
Vulnerable versions: < 1.2.3
Patched in: 1.2.3

Summary: free5GC's NEF crashes via logger.Fatal on PFD notification delivery failure (attacker-controlled notifyUri)

Details:
### Summary
free5GC's NEF terminates the entire process when a stored PFD-subscription `notifyUri` cannot be reached. In `PfdChangeNotifier.FlushNotifications()`, the notifier calls `NnefPFDmanagementNotify(...)` and on any delivery error invokes `logger.PFDManageLog.Fatal(err)`, which is `os.Exit(1)`-equivalent in Go. An attacker who can create a PFD subscription with an attacker-chosen `notifyUri` and then trigger a PFD change can deterministically kill NEF on the asynchronous delivery attempt -- the process exits with status `1`, dropping NEF's entire SBI surface until restart. This is materially worse than a per-request panic-DoS (Gin recovery does not catch `Fatal`).

The trigger uses three POSTs that are reachable without an `Authorization` header in v4.2.1, because the underlying NEF SBI route groups themselves are mounted without inbound auth middleware (see free5gc/free5gc#858, free5gc/free5gc#859, free5gc/free5gc#862). So in the lab the entire chain is unauthenticated end-to-end. This advisory is scoped to the `Fatal`-on-delivery-failure code defect; the auth-bypass primitives are tracked separately in the upstream issues above.

### Details
Validated against the NEF container in the official Docker compose lab.
- Source repo tag: `v4.2.1`
- Running Docker image: `free5gc/nef:v4.2.1`
- Runtime NEF commit: `5ce35eab`
- Docker validation date: 2026-03-20 (container log timestamp `2026-03-20T16:00:03Z`)
- NEF endpoint: `http://10.100.200.19:8000`

Vulnerable notifier path:
```go
_, err := nc.notifier.clientPfdManagement.PFDSubscriptionsApi.NnefPFDmanagementNotify(
    context.TODO(), nc.notifier.getSubURI(id), notifyReq)
if err != nil {
    logger.PFDManageLog.Fatal(err)   // <-- os.Exit(1)-equivalent
}
```

The failing branch is reached whenever NEF's outbound POST to the subscriber's `notifyUri` returns an error (connection refused, DNS failure, TLS error, timeout, etc.). The delivery happens asynchronously after the PFD-management transaction is accepted, so the triggering HTTP request (the PFD change) returns `201 Created` and only then does NEF die.

Code evidence (paths in `free5gc/nef`):
- Notifier dispatch:
  - `NFs/nef/internal/sbi/notifier/pfd_notifier.go:135`
- Fatal call site (process exit):
  - `NFs/nef/internal/sbi/notifier/pfd_notifier.go:142`

### PoC
Reproduced end-to-end against the running NEF at `http://10.100.200.19:8000` -- three unauthenticated POSTs, the third one indirectly triggers async notify -> Fatal -> process exit.

1. Create an AF context (no Authorization header):
```
curl -i -X POST 'http://10.100.200.19:8000/3gpp-traffic-influence/v1/afdos/subscriptions' \
  -H 'Content-Type: application/json' \
  --data '{"afAppId":"app-nef-dos","anyUeInd":true}'
```
```
HTTP/1.1 201 Created
Location: http://nef.free5gc.org:8000/3gpp-traffic-influence/v1/afdos/subscriptions/1
```

2. Create a PFD subscription with an attacker-chosen unreachable callback (port 1 = always refused locally):
```
curl -i -X POST 'http://10.10