{"id":"GHSA-pfvm-w89x-94jw","title":"SIPSorcery: Malformed UDP datagram crashes TurnServer receive loop with no restart, disabling TURN UDP relay for all clients (DoS)","summary":"SIPSorcery: Malformed UDP datagram crashes TurnServer receive loop with no restart, disabling TURN UDP relay for all clients (DoS)","severity":"high","cvss":7.5,"cwe":["CWE-248","CWE-755"],"vendor":"SIPSorcery","product":"SIPSorcery","ecosystem":"nuget","affected":["SIPSorcery >= 10.0.5, <= 10.0.13"],"patched":["SIPSorcery 10.0.14"],"published":"2026-08-12","updated":"2026-08-12","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-pfvm-w89x-94jw","references":[{"url":"https://github.com/sipsorcery-org/sipsorcery/security/advisories/GHSA-pfvm-w89x-94jw"},{"url":"https://github.com/sipsorcery-org/sipsorcery/commit/ccb0b5a845efa2fb131fd00de4f5321bae627f29"},{"url":"https://github.com/advisories/GHSA-pfvm-w89x-94jw"}],"tags":["ghsa","nuget"],"ingestedAt":"2026-08-12T19:54:27.482Z","slug":"GHSA-pfvm-w89x-94jw","body":"## Overview\n\n## Summary\n`TurnServer.ReceiveUdpAsync` places its generic `catch (Exception)` OUTSIDE the `while` receive loop, and `Start()` launches the loop fire-and-forget with no supervision or restart. A single pre-authentication UDP datagram whose STUN header first byte is in `0x80–0xFF` causes `STUNHeader.ParseSTUNHeader` to throw `ApplicationException`, which unwinds past the loop and terminates it. The TURN UDP relay is then dead for ALL clients until the process is restarted.\n\n## Root Cause\n`src/SIPSorcery/net/TURN/TurnServer.cs`:\n- `ReceiveUdpAsync` (:555-577): the inner `try` (:562-567) wraps only `_udpSocket.ReceiveAsync()`; `HandleUdpDatagram(result.Buffer, result.RemoteEndPoint)` (:569) is inside the `while` body but OUTSIDE that inner try. The generic `catch (Exception ex)` (:573) is lexically OUTSIDE the `while`.\n- `Start()` does `_ = ReceiveUdpAsync();` (:381) — fire-and-forget, no restart.\n- `HandleUdpDatagram` (:579) calls `STUNMessage.ParseSTUNMessage(data, data.Length)` (:600) for any non-ChannelData datagram; `ParseSTUNMessage` (STUNMessage.cs:94) has no try/catch.\n\n## Impact\n`ApplicationException` propagates out of the `while`, is caught at :573, logged, and the method returns. `_running` remains true but nothing re-invokes `ReceiveUdpAsync` → TURN UDP relay permanently unavailable for all clients (whole-server DoS). Pre-authentication: STUN parsing precedes any TURN allocation/credential check.\n\n## Proof of Concept\nSend one UDP datagram to the TURN port (default 3478) with first byte `0x80` (e.g. `80 00 00 00`). `0x80 & 0xC0 = 0x80 ≠ 0x40` → not ChannelData → `ParseSTUNMessage` → `ParseSTUNHeader` executes `if ((Array[startIndex] & 0xC0) != 0) throw new ApplicationException(...)` (STUNHeader.cs:169-172); `0x80 & 0xC0 = 0x80 ≠ 0` → throws.\n\n## Attack Chain\n1. Entry: one UDP datagram to the TURN port, first byte `0x80–0xFF`. Guard: ChannelData branch requires `(data[0] & 0xC0) == 0x40` (:583). Bypass: `0x80 & 0xC0 = 0x80 ≠ 0x40` → falls through to `ParseSTUNMessage` (:600).\n2. Sink: `STUNMessage.ParseSTUNMessage` → `STUNHeader.ParseSTUNHeader` (STUNHeader.cs:169-172) throws `ApplicationException`. Guard: none before the throw; `ParseSTUNMessage` has no try/catch. Bypass: `0x80 & 0xC0 = 0x80 ≠ 0` → throws.\n3. Impact: exception unwinds past the `while` into `catch(Exception)` at :573 → logged → method returns → loop exits. Guard: none — no restart (`Start()` :381 fire-and-forget). Bypass: N/A. TURN UDP relay dead for all clients until process restart.\n\n## Bypass Evidence\n- Loop/catch structure: catch at TurnServer.cs:573 is outside the `while` at :559; `HandleUdpDatagram` at :569 is outside the inner try (:562-567).\n- Unguarded `ParseSTUNMessage` at :600; throw at STUNHeader.cs:169-172.\n- Fire-and-forget start at :381 with no restart in `Start()`.\n- `TurnServerConfig.ListenAddress` defaults to `IPAddress.Loopback` (:42), but a functioning TURN server must bind a routable address to serve clients, so real deployments are exposed. Non-default config narrows the vulnerable population, not the attack difficulty → AC:L.\n\n## Affected Versions\n`nuget:SIPSorcery <= 10.0.13` (TurnServer component present since 10.0.5; verified on release tag v10.0.13 and HEAD).\n\n## Dedup\nNOT a duplicate of GHSA-28gm-jrmw-xx93 (CVE-2026-54632), which covers the client RTP/ICE socket (`UdpReceiver`/`RTPChannel`). `TurnServer` is a distinct shipped RFC 5766 server component with its own loop and fix location.\n\n## Suggested Fix\nWrap `HandleUdpDatagram` in a per-datagram try/log-and-continue INSIDE the `while` (matching the drop-and-continue intent of fix bdb76cb), and/or add loop supervision/restart.\n\n---\nReported by **zx (Jace)** — GitHub: @manus-use\n\n## Affected packages\n\n- `SIPSorcery >= 10.0.5, <= 10.0.13`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `SIPSorcery 10.0.14`","depth":"twilight","depthScore":41,"depthScoreParts":{"impact":41.3,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}