medium

CVE

CVE-2026-55663

CWE

CWE-345

Affected Surface

  • npm package `mediasoup` `>=3.20.0,<3.20.6` and Rust crate `mediasoup` `>=0.22.0,<0.22.5`
  • Applications that create `PlainTransport` or `PipeTransport` with `enableSctp: true`, especially where traffic crosses a shared, routed, or otherwise attacker-observable network segment
  • Server-to-server SCTP or DataChannel topologies that use mediasoup's worker SCTP stack outside `WebRtcTransport`
  • Deployments using `comedia: true`, where the remote transport tuple is learned from the first inbound packets rather than being fixed ahead of time

The strongest fresh package-security story in the 23 to 26 August window is CVE-2026-55663, published into NVD on 25 August for the npm package mediasoup and the Rust crate mediasoup. This is not a malicious upload story. It is a protocol-authentication bug in mediasoup’s worker SCTP implementation. The boundary that failed was the State Cookie check used during SCTP association setup on PlainTransport and PipeTransport.

The practical result is narrow but real. If your application uses mediasoup’s non-WebRTC SCTP path and an attacker can see or influence traffic on that network path, they can forge a COOKIE-ECHO, establish an unauthorized SCTP association, and inject DataChannel messages as if they were the expected peer.

This matters because teams often treat mediasoup as “the WebRTC layer” and mentally inherit browser-side protections that are not present here. WebRtcTransport is not the vulnerable surface. The exposed code sits in the worker’s plain and pipe SCTP stack, where the project was validating a cookie with fixed markers instead of a secret keyed MAC.

Affected package and version scope

The affected packages are:

PackageAffected versionsFixed versions
mediasoup (npm)>=3.20.0,<3.20.63.20.6
mediasoup (Rust crate)>=0.22.0,<0.22.50.22.5

The transport boundary matters more than the version table.

The official docs show that mediasoup applications can create SCTP-capable non-WebRTC transports like this:

const transport = await router.createPlainTransport({
  listenInfo: { protocol: "udp", ip: "127.0.0.1" },
  comedia: true,
  enableSctp: true,
  enableSrtp: true
});

and:

const transport = await router.createPipeTransport({
  listenInfo: { protocol: "udp", ip: "192.168.1.33" },
  enableSctp: true
});

Those APIs are why this belongs in package security coverage. The application is one npm install mediasoup or cargo add mediasoup away from exposing a stateful network protocol that the library itself must authenticate correctly.

The highest-risk cases are:

  • applications that use PlainTransport or PipeTransport for SCTP or DataChannel traffic between services
  • deployments that place those transports on a shared LAN, Kubernetes node network, VPC segment, or other path an attacker may observe or reach
  • comedia: true deployments, because the docs say mediasoup can learn the remote tuple from the first packets it receives
  • teams that assumed “we are not using browser WebRTC here” meant they did not need to think about protocol-level association forgery

The bug was not “bad crypto” in the abstract

The advisory is precise about what went wrong. mediasoup generated SCTP State Cookies with fixed markers, including the literal msworker magic and the 0xAD81 capability marker, and then treated those values as the signal that the cookie was one of its own.

In the pre-fix code, StateCookie::IsMediasoupStateCookie() was basically a shape check:

if (bufferLength != StateCookie::StateCookieLength)
{
  return false;
}

if (Utils::Byte::Get8Bytes(buffer, 0) != StateCookie::Magic1)
{
  return false;
}

if (ntohs(negotiatedCapabilitiesField->magic2) != StateCookie::Magic2)
{
  return false;
}

return true;

That is not authentication. It proves only that the buffer looks like a mediasoup cookie. It does not prove that this worker instance created it.

RFC 9260 requires more. The cookie needs a MAC keyed with a secret that never leaves the endpoint. Without that, an attacker who can operate on the path can satisfy the worker’s format checks and drive the association logic with attacker-controlled packet fields.

NVD’s wording is the most useful short summary of the exploit condition: the forged cookie passes StateCookie::IsMediasoupStateCookie() and Association::HandleReceivedCookieEchoChunk(), and the packet verification tag can be aligned with the attacker-controlled localVerificationTag.

Why the transport distinction matters

This is not a generic “all mediasoup DataChannels are broken” story.

Upstream and the docs both make the safe boundary explicit:

  • WebRtcTransport is not affected because its SCTP traffic runs inside DTLS
  • PlainTransport and PipeTransport use the worker’s SCTP path directly
  • PlainTransport and PipeTransport also appear in mediasoup’s API as deliberate integration points for non-browser and server-to-server traffic

That makes CVE-2026-55663 a strong example of a package risk that lives below your application logic:

application enables SCTP on plain or pipe transport
  -> mediasoup worker handles SCTP association setup
  -> worker trusts fixed cookie markers instead of a secret-keyed MAC
  -> on-path attacker forges COOKIE-ECHO
  -> unauthorized SCTP association becomes trusted
  -> attacker can inject DataChannel messages

If your application uses those messages for control traffic, cluster signaling, or server-side automation, this is an integrity boundary failure, not just a noisy protocol bug.

The patch shows exactly what was missing

The fix is clean and worth reading because it maps almost one-to-one to the missing properties in the vulnerable design.

First, mediasoup added a transport-level requirement that only applies where SCTP is not already protected by DTLS:

// PlainTransport.cpp
: RTC::Transport::Transport(
    shared, id, listener, options->base(),
    /*requireSctpStateCookieAuthentication*/ true)

// PipeTransport.cpp
: RTC::Transport::Transport(
    shared, id, listener, options->base(),
    /*requireSctpStateCookieAuthentication*/ true)

The associated internal option is explicit:

bool requireAuthenticatedCookie{ false };

with the comment that it “MUST be enabled in transports whose SCTP traffic is not protected by DTLS.”

Second, each SCTP association now generates a private random secret:

if (this->sctpOptions.requireAuthenticatedCookie)
{
  Utils::Crypto::WriteRandomBytes(
    this->stateCookieSecret,
    Association::StateCookieSecretLength);
}

Third, cookie generation now appends a creation timestamp plus an HMAC-SHA1 over the preceding cookie bytes:

Utils::Byte::Set8Bytes(
  buffer,
  StateCookie::TimestampOffset,
  creationTimestampMs);

const uint8_t* mac = Utils::Crypto::GetHmacSha1(
  reinterpret_cast<const char*>(macKey),
  macKeyLength,
  buffer,
  StateCookie::MacOffset);

std::memcpy(
  buffer + StateCookie::MacOffset,
  mac,
  StateCookie::MacLength);

Fourth, COOKIE-ECHO handling now rejects cookies that fail MAC verification or exceed the RFC cookie lifetime:

if (!StateCookie::VerifyMac(
      cookie->GetBuffer(),
      cookie->GetLength(),
      this->stateCookieSecret,
      Association::StateCookieSecretLength))
{
  return false;
}

if (nowMs > creationMs &&
    nowMs - creationMs > StateCookie::ValidCookieLifeMs)
{
  // send Stale Cookie error
  return false;
}

That is the difference between “buffer carries recognizable mediasoup fields” and “this endpoint actually minted this cookie recently.”

One useful patch detail: mediasoup also started rejecting bad CRC32c packets

The same fix train added explicit CRC32c validation for received SCTP packets unless a zero-checksum alternate error-detection mode had been negotiated:

if (!acceptZeroChecksum || receivedPacket->GetChecksum() != 0)
{
  if (!receivedPacket->ValidateCRC32cChecksum())
  {
    return false;
  }
}

That is not the primary root cause of CVE-2026-55663, but it is a good reminder that the worker was tightening more than one SCTP trust check in the same patch. The state-cookie MAC is the real fix. The checksum validation is supporting hardening around the same handshake path.

What this means for real deployments

Many teams will read “on-path attacker” and downgrade the issue mentally. That is a mistake if you use mediasoup outside browser-only topologies.

The attacker does not need to own your Node.js process. They need a position on the transport path you already opened, or a deployment mode that broadens who can present as the remote peer. That is common in:

  • internal media gateways
  • server-to-server SFU interconnects
  • mixed trusted and semi-trusted workloads on shared cluster networks
  • self-hosted realtime products that exposed plain or pipe transports for integration convenience

The docs make one especially relevant point about comedia: mediasoup can infer the remote IP and port from the packets it receives first. That is operationally useful, but it also means the transport is intentionally learning trust from the network path. If the protocol’s own cookie authentication is weak, that convenience becomes part of the exposure story.

How to find risky usage

Start with package inventory:

npm ls mediasoup
pnpm why mediasoup

cargo tree -i mediasoup

Then search for transport patterns that actually exercise the vulnerable code:

rg -n \
  "createPlainTransport|createPipeTransport|enableSctp|enableSrtp|comedia|srtpCryptoSuite" \
  src .github packages apps services lib

Risky configuration signals include code shaped like:

await router.createPlainTransport({
  comedia: true,
  enableSctp: true
});

or:

await router.createPipeTransport({
  enableSctp: true
});

After you patch, the new warnings are useful detection breadcrumbs. The patched worker now logs conditions such as:

  • invalid CRC32c checksum
  • received COOKIE-ECHO with a State Cookie that failed MAC authentication
  • received COOKIE-ECHO with a stale State Cookie

If those start appearing after rollout, treat them as possible evidence that your deployment was reachable by traffic it should not have trusted.

Remediation

The minimum version fix is straightforward:

npm mediasoup:  upgrade to 3.20.6 or later
crate mediasoup: upgrade to 0.22.5 or later

The operational fix is broader:

  1. Inventory every use of PlainTransport and PipeTransport with SCTP enabled.
  2. Patch the package version first.
  3. Re-check whether those transports really need to be reachable across shared or attacker-observable networks.
  4. Prefer WebRtcTransport where browser-style DTLS protection is the intended model.
  5. If plain or pipe SCTP is required, keep the path tightly segmented and monitor for rejected cookie or checksum events after the upgrade.

The key lesson is simple. mediasoup was not missing a policy flag. It was missing cryptographic proof that a State Cookie came from the local worker. Once that proof is absent, a protocol handshake becomes application risk, even when everything above it looks normal.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References