TP-08_stride_threat_emulation
TP-08 — STRIDE Threat Emulation Tests
Domain: Active Threat Emulation (STRIDE Framework)
Standards: GSMA FS.40 v3.0 · 3GPP TS 33.501 · NIST SP 800-187
Prerequisites: TP-01 baseline established; both 4G and 5G labs running; Scapy/pycrate installed
TC-STRIDE-S01: Spoofing — Rogue gNB / IMSI Catcher Simulation
Threat Model — Spoofing
graph TD
subgraph SCENARIO_4G["4G — IMSI Exposure (Stingray)"]
UE4G["srsUE (4G UE)\n(real device equivalent)"]
REAL_ENB["Legitimate eNB\n(srsENB)"]
ROGUE_ENB["Rogue eNB\n(Stingray simulator)\nStronger signal"]
UE4G -->|Selects strongest signal| ROGUE_ENB
ROGUE_ENB -->|Identity Request\n(before security)| UE4G
UE4G -->|Identity Response\nIMSI in CLEARTEXT| ROGUE_ENB
ROGUE_ENB -.->|Relay or drop| REAL_ENB
end
subgraph SCENARIO_5G["5G — SUCI Protection (Mitigated)"]
UE5G["UERANSIM UE (5G UE)"]
ROGUE_GNB["Rogue gNB\n(fake UERANSIM gNB)"]
UE5G -->|Registration Request| ROGUE_GNB
Note1["SUCI encrypted\nNo IMSI in request"]
ROGUE_GNB -.->|Cannot decode SUCI\nNo Home Network Private Key| BLOCKED["❌ SUPI not revealed\nIMSI Catcher FAILS in 5G"]
end
subgraph COMPARE["4G vs 5G Comparison"]
C1["4G: IMSI in cleartext\nbefore AKA setup\n☠️ Stingray-exploitable"]
C2["5G: SUCI encryption\nECIES scheme\n✅ IMSI protected"]
end
style ROGUE_ENB fill:#7b2d00,color:#fff
style ROGUE_GNB fill:#7b2d00,color:#fff
style BLOCKED fill:#27ae60,color:#fff
style C1 fill:#c0392b,color:#fff
style C2 fill:#27ae60,color:#fffObjective
Simulate rogue gNB/eNB; demonstrate 4G IMSI exposure vs 5G SUCI protection.
Steps
# === 4G: Demonstrate IMSI Exposure ===
# 1. Start Open5GS 4G EPC and srsENB (TAC=1, PLMN=00101)
# 2. Start a "rogue" srsENB with higher signal — same PLMN, same TAC
# (In ZMQ/software radio, both are equally strong — note the concept)
# Monitor S1AP traffic for Identity Response (IMSI in cleartext)
sudo tshark -i lo \
-Y "s1ap && s1ap.IdentityResponse" \
-T fields -e s1ap.id_NAS_PDU 2>/dev/null &
# Start srsUE — note: 4G will send IMSI in Identity Response if no GUTI cached
sudo srsue \
--usim.imsi=001010000000001 \
--usim.k=465B5CE8B199B49FAA5F0A2EE238A6BC \
--usim.opc=E8ED289DEBA952E4283B54E88E6183CA \
--usim.imei=356938035643809 \
--log.all_level=info 2>&1 | grep -E "IMSI|Identity|attach"
# === 5G: Verify SUCI Protection ===
# 3. Capture Registration Request on N2
sudo tshark -i br-$(docker network ls --filter name=sbi -q) \
-Y "ngap && nas-5gs.mm.message_type == 0x41" \
-T fields \
-e nas-5gs.mm.mobile_identity_type \
-e nas-5gs.mm.suci.protection_scheme_id \
2>/dev/null &
# Restart UE to trigger fresh registration
docker compose -f ~/open5gs-lab/5g/docker-compose.yml restart ue
sleep 15
# Check: Mobile Identity Type should be SUCI (1), not IMSI (2)
echo "=== 5G NAS Registration Request Mobile Identity ==="
tshark -r ~/open5gs-lab/captures/tc-reg-03.pcap 2>/dev/null \
-Y "nas-5gs.mm.message_type == 0x41" \
-T fields -e nas-5gs.mm.mobile_identity_type | head -5
Expected Results
- 4G: IMSI appears in plaintext in Identity Response (expected — documents known gap)
- 5G: Mobile Identity Type = 1 (SUCI); no IMSI in any NAS message to gNB
- Rogue gNB cannot extract SUPI from 5G registration
Pass Criteria (5G)
Zero IMSI disclosures to gNB. SUCI type confirmed. 4G IMSI exposure documented as architectural limitation.
TC-STRIDE-T01: Tampering — User Plane MitM (GTP-U Content Modification)
Threat Model — Tampering
sequenceDiagram
participant UE as UE
participant GNB as gNB
participant MITM as MitM Node\n(lab VM / iptables)
participant UPF as UPF
participant DN as Internet
Note over UE,DN: THREAT VECTOR: S1-U/N3 GTP-U carries unencrypted\nuser traffic in 4G (and 5G without UP integrity)
UE->>GNB: User data (HTTP GET request)
GNB->>MITM: GTP-U encapsulated\n(TEID=0x1234, inner IP: HTTP GET)
Note over MITM: ⚠️ MitM modifies inner IP payload\nChanges HTTP response content
MITM->>UPF: GTP-U (same TEID, modified inner payload)
UPF->>DN: Decapsulated modified packet
Note over UE,DN: 4G RESULT: Modification transparent\nUser receives modified content\n(DNS poisoning, HTTP injection possible)
rect rgb(26, 60, 100)
Note over UE,DN: 5G WITH UP INTEGRITY (NIA2)
UE->>GNB: PDCP PDU with integrity MAC
GNB->>MITM: GTP-U with PDCP integrity protection
MITM->>MITM: Modify inner payload\nbut cannot forge PDCP MAC
MITM->>UPF: Modified GTP-U
UPF->>UPF: PDCP integrity check FAILS
UPF-->>GNB: PDCP integrity failure
Note over UE,DN: ✅ 5G: Modification DETECTED
endObjective
Demonstrate user-plane tampering in 4G (expected gap) and verify 5G UP integrity detection.
Steps
# === 4G: Demonstrate GTP-U MitM ===
# 1. Enable IP forwarding on host (acts as transparent bridge)
sudo sysctl -w net.ipv4.ip_forward=1
# 2. Use iptables to intercept and modify GTP-U traffic on S1-U path
# (Lab: modify DNS response inside GTP-U tunnel)
# This uses Scapy for transparent MitM
python3 << 'PYEOF'
from scapy.all import *
from scapy.contrib.gtp import GTP_U_Header
# Conceptual MitM: intercept GTP-U, modify inner DNS response
# In a real lab, you'd run this on a VM bridging eNB to SGW-U
def gtp_mitm(pkt):
"""Intercept GTP-U and modify inner IP payload"""
if GTP_U_Header in pkt and DNS in pkt:
print(f"[INTERCEPT] GTP-U TEID={hex(pkt[GTP_U_Header].teid)}")
print(f"[INTERCEPT] Inner DNS: {pkt[DNS].summary()}")
# In real attack: modify DNS response to point to attacker's server
# This demonstrates the lack of integrity protection on S1-U
print("[WARNING] 4G S1-U has NO integrity protection!")
print("[WARNING] Inner payload can be modified transparently")
return pkt # pass through unmodified (demonstration only)
# Listen for GTP-U traffic (demonstration without actual modification)
print("Starting GTP-U monitor (demonstration mode)...")
print("4G S1-U interface has no encryption or integrity protection.")
print("Any host on the S1-U segment can transparently modify user traffic.")
print("")
print("Mitigation: 5G UP Integrity (NIA2) + GTP-U IPsec tunnel")
PYEOF
# === 5G: Check UP Integrity Configuration ===
# 3. Check if SMF has UP integrity configured
docker exec open5gs-smf cat /etc/open5gs/smf.yaml 2>/dev/null | \
grep -A5 "integrity\|security"
# 4. Check pcap for PDCP protection indicator
sudo tshark -i br-$(docker network ls --filter name=ran -q) \
-Y "pdcp-lte || pdcp-nr" \
-c 20 2>/dev/null
Expected Results
- 4G S1-U: GTP-U inner payload is not integrity-protected (expected architectural gap)
- 5G: When UP integrity configured in
smf.yaml, PDCP MAC present in NR PDCP PDUs - MitM modification of GTP-U inner payload: detectable in 5G, transparent in 4G
Pass Criteria
4G gap documented. 5G UP integrity config verified in smf.yaml. Document as architectural comparison.
TC-STRIDE-I01: Information Disclosure — MongoDB Direct Access
Threat Model — Information Disclosure
graph TD
subgraph ATTACK_PATH["Attack Path: Lateral Movement to MongoDB"]
ATK["Attacker gains foothold\n(e.g., via compromised NF container\nor rogue container on Docker network)"]
ATK -->|1. Network scan| DISCOVERY["Discovers MongoDB\n172.22.0.2:27017"]
DISCOVERY -->|2. Connect| AUTH_CHECK{"MongoDB\nauthentication?"}
AUTH_CHECK -->|No auth (default)| MONGO_OPEN["mongosh mongodb://172.22.0.2/open5gs"]
AUTH_CHECK -->|Auth required| BLOCKED_AUTH["✅ Access denied\nCredentials required"]
MONGO_OPEN -->|3. Query subscribers| DATA["db.subscribers.find()\n→ ALL subscriber records:\n- IMSI\n- Authentication Key K\n- OPc\n- SQN\n- Subscriber profile"]
DATA -->|Impact| I1["❌ K extracted\n→ Full AKA compromise\nClone any SIM"]
DATA -->|Impact| I2["❌ OPc extracted\n→ Operator secret exposed"]
DATA -->|Impact| I3["❌ SQN extracted\n→ Auth replay possible"]
DATA -->|Impact| I4["❌ Mass subscriber PII\n→ GDPR violation\n→ SIM swap enablement"]
end
subgraph ISOLATION["Expected Controls"]
CTL1["MongoDB only on SBI/control plane network\n(not accessible from RAN network)"]
CTL2["SCRAM-SHA-256 authentication required"]
CTL3["Bind to 127.0.0.1 only (production)"]
end
style MONGO_OPEN fill:#c0392b,color:#fff
style I1 fill:#c0392b,color:#fff
style I2 fill:#c0392b,color:#fff
style I3 fill:#c0392b,color:#fff
style I4 fill:#c0392b,color:#fff
style BLOCKED_AUTH fill:#27ae60,color:#fffObjective
Verify MongoDB subscriber database is isolated and requires authentication; lateral movement from RAN-side containers is blocked.
Steps
# 1. Attempt MongoDB access from UERANSIM (RAN-side) container
echo "=== Test 1: RAN container cannot reach MongoDB ==="
docker exec ueransim-ue bash -c \
"curl -s --connect-timeout 3 http://172.22.0.2:27017 && echo 'REACHABLE' || echo 'BLOCKED'"
# Expected: BLOCKED (different network)
# 2. Check if MongoDB is on RAN network
echo "=== Test 2: MongoDB network attachment ==="
docker inspect open5gs-mongodb 2>/dev/null | jq '.[].NetworkSettings.Networks | keys'
# Should show ONLY SBI/control plane network, NOT RAN network
# 3. Attempt connection without authentication
echo "=== Test 3: MongoDB auth check ==="
docker exec open5gs-mongodb mongosh --eval \
'db.runCommand({connectionStatus:1})' --quiet 2>&1 | head -5
# Try unauthenticated query
docker exec open5gs-mongodb mongosh open5gs --eval \
'db.subscribers.countDocuments()' --quiet 2>&1
# 4. Check from a freshly launched container (simulating attacker foothold)
echo "=== Test 4: Attacker container on SBI network ==="
docker run --rm \
--network open5gs-lab_sbi \
curlimages/curl:latest \
curl -s --connect-timeout 3 http://172.22.0.2:27017
# Should be accessible from SBI network — check if auth is needed
# 5. Verify MongoDB bind address
docker exec open5gs-mongodb cat /etc/mongod.conf 2>/dev/null | grep "bindIp"
# 6. Check what data is exposed (run as authorized user only)
docker exec open5gs-mongodb mongosh open5gs --eval \
'db.subscribers.findOne({},{imsi:1, "security.k":1})' --quiet
echo "WARNING: Auth key K exposed above — must be encrypted at rest in production"
Expected Results
- UERANSIM container CANNOT reach MongoDB (different Docker network)
- MongoDB on SBI network only (not RAN)
- MongoDB requires authentication (SCRAM-SHA-256)
- Subscriber K/OPc values stored in plaintext in lab (expected finding — document for production)
Finding
MongoDB accessible without auth: HIGH FINDING — all subscriber authentication keys exposed.
MongoDB accessible from RAN network: CRITICAL FINDING — RAN compromise leads to full subscriber database access.
Pass Criteria
MongoDB not reachable from RAN containers. Auth required for any query.
TC-STRIDE-E01: Elevation of Privilege — Cross-UE Session Modification
Threat Model — Elevation of Privilege
sequenceDiagram
participant UE_A as UE-A (IMSI ...001)
participant UE_B as UE-B (IMSI ...002)
participant GNB as gNB
participant AMF as AMF
participant SMF as SMF
Note over UE_A,SMF: THREAT: UE-A attempts to modify UE-B's PDU session\nto gain elevated QoS or disrupt service
Note over UE_A,SMF: Legitimate state
UE_A->>AMF: Registered (AMF-UE-NGAP-ID=100, SUPI=...001)
UE_B->>AMF: Registered (AMF-UE-NGAP-ID=101, SUPI=...002)
UE_A->>SMF: PDU Session 5 (DNN=internet)
UE_B->>SMF: PDU Session 5 (DNN=iot)
Note over UE_A: Attacker constructs NAS PDU Session Modification\nusing UE-B's PDU Session ID (5) but in UE-A's NAS context
UE_A->>GNB: NAS: PDU Session Modification Request\n{PDU Session ID: 5, RequestedQoS: GBR 1Gbps}
GNB->>AMF: NGAP UplinkNASTransport\n{AMF-UE-NGAP-ID: 100 — UE-A's ID!\n NAS: PDU Session Mod for session 5}
AMF->>AMF: Bind PDU Session ID 5 to UE-A's NAS context\n(SUPI ...001, PDU session ID 5)
AMF->>SMF: POST /nsmf-pdusession/v1/sm-contexts/ue-a-session5/modify
Note over SMF: SMF looks up session by SUPI+PDU-Session-ID\nSUPI=...001, PDU-Session-ID=5 = UE-A's session
SMF->>SMF: Apply modification to UE-A's session\n(not UE-B's!)
Note over UE_B: UE-B's session unchanged\n✅ Context binding prevents cross-UE elevation
Note over UE_A: If SMF used ONLY PDU-Session-ID without SUPI binding:
Note over SMF: ❌ Could modify UE-B's session\n(privilege escalation)Objective
Verify AMF and SMF bind PDU Session IDs to subscriber context (SUPI); cross-UE session modification is impossible.
Steps
# 1. Register two UEs
docker compose -f ~/open5gs-lab/5g/docker-compose.yml up -d ue
sleep 10
UE_A_IP=$(docker exec ueransim-ue ip addr show uesimtun0 | grep "inet " | awk '{print $2}' | cut -d/ -f1)
# Register UE-B
docker exec open5gs-mongodb mongosh open5gs --eval '
db.subscribers.insertOne({
imsi: "001010000000002",
security: {k: "465B5CE8B199B49FAA5F0A2EE238A6BC",
opc: "E8ED289DEBA952E4283B54E88E6183CA", amf: "8000", sqn: NumberLong(0)},
slice: [{sst: 1, default_indicator: true,
session: [{name: "internet", type: 3,
qos: {index: 9, arp: {priority_level: 8,
pre_emption_capability: 1, pre_emption_vulnerability: 1}}}]}]
})'
docker run -d --name ueransim-ue-b \
--cap-add=NET_ADMIN --network open5gs-lab_ran \
-v ~/open5gs-lab/5g/config/ue-002.yaml:/etc/ueransim/ue.yaml \
louisroyer/ueransim-ue:latest
sleep 15
# 2. Check SMF session contexts
docker logs open5gs-smf 2>&1 | grep "SUPI\|PDU Session" | tail -20
# 3. Attempt to modify UE-B's session from UE-A via UERANSIM CLI
# Use nr-cli to send PDU session modification request
docker exec ueransim-ue nr-cli imsi-001010000000001 \
-e "ps-modify 5 --ambr-downlink 1000000000" 2>&1
# This requests QoS modification on PDU Session 5
# 4. Verify modification affected only UE-A's session
docker logs open5gs-smf 2>&1 | grep -E "modify|001010000000001|001010000000002" | tail -20
# UE-A session: modified (if SMF applied it)
# UE-B session: UNCHANGED
# 5. Cross-session via pycrate (conceptual demonstration)
python3 << 'PYEOF'
print("Cross-UE session attack mitigation analysis:")
print("")
print("AMF binds: AMF-UE-NGAP-ID → SUPI → NAS context → PDU Session IDs")
print("Any NAS PDU Session Modification from UE-A is bound to UE-A's SUPI")
print("SMF session lookup: /nsmf-pdusession/v1/sm-contexts/{UE-A-context-ID}")
print("UE-B's context ID is never exposed to UE-A")
print("")
print("Conclusion: Cross-UE session modification requires knowledge of")
print("UE-B's AMF-assigned context ID, which is never shared with other UEs")
PYEOF
# Cleanup
docker rm -f ueransim-ue-b
Expected Results
- PDU Session Modification from UE-A only affects UE-A's session context
- UE-B's PDU session: unchanged (different SUPI → different SMF context)
- AMF log: modification request linked to UE-A's SUPI only
- SMF context IDs not guessable/enumerable from other UEs
Pass Criteria
Cross-UE session modification impossible. Session context binding verified in AMF/SMF logs.