In-app reader
mediasoup's built-in SCTP stack (introduced in v3.20.0) authenticates SCTP state cookies using only hardcoded magic byte sequences rather than a per-instance HMAC keyed with a secret, violating RFC 9260 Section 5.1.3. An on-path attacker targeting a PlainTransport with SCTP enabled (and no SRTP/DTLS protection) can craft a forged COOKIE-ECHO chunk that passes all validation, establishing an unauthorized SCTP association and gaining the ability to inject DataChannel messages as a trusted peer.
RFC 9260 Section 5.1.3 states: "An endpoint MUST use a one-time-use secret key to protect the State Cookie." The mediasoup implementation ignores this requirement. The state cookie is defined in worker/include/RTC/SCTP/association/StateCookie.hpp with the following structure (44 bytes total):
Offset 0: Magic1 = "msworker" (hardcoded, 8 bytes)
Offset 8: localVerificationTag (4 bytes, attacker-controlled)
Offset 12: remoteVerificationTag (4 bytes, attacker-controlled)
Offset 16-27: TSN and window fields (attacker-controlled)
Offset 28: tieTag (8 bytes, attacker-controlled)
Offset 36: NegotiatedCapabilitiesField containing Magic2 = 0xAD81 (hardcoded)
The validation function StateCookie::IsMediasoupStateCookie() in worker/src/RTC/SCTP/association/StateCookie.cpp only checks:
bufferLength == 44
bytes[0:8] == "msworker" (Magic1, always the same)
ntohs(bytes[38:40]) == 0xAD81 (Magic2, always the same)
No HMAC, no per-session secret, no nonce. All "magic" values are published constants in the public header.
When a COOKIE-ECHO is received in Association::HandleReceivedCookieEchoChunk() (without an existing TCB), the sole security check is:
if (receivedPacket->GetVerificationTag() != cookie->GetLocalVerificationTag())
Discussion
Sign in to join the discussion.
Keep reading
Optional: create a free account to save items, track programs, and sync across web + app. Reading stays free.