Skip to content
nats-serverGHSA-pq2q-rcw4-3hr6

NATS: Pre-auth remote server crash via WebSocket frame length overflow in wsRead

High7.5CVE-2026-27889 · Published Mar 25, 2026 · updated Sep 10, 2026

GitHub advisory

Affected versions

PackageAffectedFixed in
github.com/nats-io/nats-server
Go
all versionsNo fix yet
Details and references

### Background NATS.io is a high performance open source pub-sub distributed communication technology, built for the cloud, on-premise, IoT, and edge computing. When using WebSockets, a malicious client can trigger a server crash with crafted frames, before authentication. ### Problem Description A missing sanity check on a WebSockets frame could trigger a server panic in the nats-server. This happens before authentication, and so is exposed to anyone who can connect to the websockets port. ### Affected versions Version 2 from v2.2.0 onwards, prior to v2.11.14 or v2.12.5 ### Workarounds This only affects deployments which use WebSockets and which expose the network port to untrusted end-points. If able to do so, a defense in depth of restricting either of these will mitigate the attack. ### Solution Upgrade the NATS server to a fixed version. ### Credits This was reported to the NATS maintainers by GitHub user Mistz1. Also independently reported by GitHub user jiayuqi7813. ----- ## Report by @Mistz1 ### Summary An unauthenticated remote attacker can crash the entire nats-server process by sending a single malicious WebSocket frame (15 bytes after the HTTP upgrade handshake). The server fails to validate the RFC 6455 §5.2 requirement that the most significant bit of a 64-bit extended payload length must be zero. The resulting `uint64` → `int` conversion produces a negative value, which bypasses the bounds clamp and triggers an unrecovered `panic` in the connection's goroutine , killing the entire server process and disconnecting all clients. This affects all platforms (64-bit and 32-bit). ### Details **Vulnerable code:** [`server/websocket.go` line 278](https://github.com/nats-io/nats-server/blob/a69f51f/server/websocket.go#L278) ```go r.rem = int(binary.BigEndian.Uint64(tmpBuf)) ``` When a WebSocket frame uses the 64-bit extended payload length (length code 127), the server reads 8 bytes and casts the raw `uint64` directly to `int` with no validation. RFC 6455 §5.2 states: *"the most significant bit MUST be 0"* , but nats-server never checks this. **Attack chain:** 1. The attacker sends a WebSocket frame with the MSB set in the 64-bit length field (e.g., `0x8000000000000001`). 2. At line 278, `int(0x8000000000000001)` produces `-9223372036854775807` on 64-bit Go (two's complement reinterpretation , Go does not panic on integer conversion overflow). 3. `r.rem` is now negative. At line 307–311, the bounds clamp fails: ```go n = r.rem // n = -9223372036854775807 if pos+n > max { // 14 + (-huge) = negative, NOT > max → FALSE n = max - pos // clamp NEVER fires } b = buf[pos : pos+n] // buf[14 : -9223372036854775793] → PANIC ``` The addition `pos + n` wraps to a negative value (Go signed integer overflow is defined behavior , it wraps silently). Since the negative result is never greater than `max`, the clamp is skipped. The slice expression at line 311 reaches the Go runtime bounds check, which panics. 4. There is **no `defer recover()`** anywhere in the goroutine chain: - [`startGoRoutine`](https://github.com/nats-io/nats-server/blob/a69f51f/server/server.go#L4076-L4079): `go func() { f() }()` , no recovery - [`readLoop`](https://github.com/nats-io/nats-server/blob/a69f51f/server/client.go#L1387-L1394): defer only does cleanup , no recovery The unrecovered panic propagates to Go's runtime, which calls `os.Exit(2)`. The **entire nats-server process terminates**. 5. The WebSocket frame is parsed in `wsRead()` called from `readLoop()`, which starts immediately after the HTTP upgrade , **before any NATS CONNECT authentication**. No credentials are required. **Why 15 bytes, not 14:** The 14-byte frame header (opcode + length + mask key) exactly fills the read buffer on the first call, so `pos == max` and the payload loop at line 303 (`if pos < max`) is skipped. The poisoned `r.rem` persists in the `wsReadInfo`

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Severity from
GitHub (reviewed advisory)
Weakness
CWE-190
Also known as
BIT-nats-2026-27889, CVE-2026-27889, GO-2026-4841

More nats-server advisories

All
DateAdvisory
Mar 24NATS has mTLS verify_and_map authentication bypass via incorrect Subject DN matching
CVE-2026-33248Medium4.2no fix yet
Mar 24NATS: Leafnode connections allow spoofing of Nats-Request-Info identity headers
CVE-2026-33246Medium6.4no fix yet
Mar 24NATS Server: Incomplete Stripping of Nats-Request-Info Header Allows Identity Spoofing
CVE-2026-33223Medium6.4no fix yet
Mar 24NATS JetStream has an authorization bypass through its Management API
CVE-2026-33222Medium4.9no fix yet
Mar 24NATS is vulnerable to pre-auth DoS through WebSockets client service
CVE-2026-33219Medium5.3no fix yet
Mar 24NATS has pre-auth server panic via leafnode handling
CVE-2026-33218High7.5no fix yet

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.