Skip to main content

Boteraser | Website and Server Security Solutions

🛡️ CVE-2026-39804 — bandit

🟠 CVSS 8.0 — High ✅ No Known Exploit CWE-770 NVD
8.0
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Bandit's unbounded WebSocket inflate causes BEAM OOM with a single frame

Summary

When a Bandit-fronted server has explicitly enabled WebSocket permessage-deflate (compress: true), an unauthenticated client can OOM the BEAM with a single ~6 MiB WebSocket frame. Bandit's inflate step has no output-size cap, so a small high-ratio compressed frame (e.g. zeros, ~1024:1 ratio) decompresses unbounded into the connection process before any application code runs. Phoenix and LiveView are not vulnerable by default — they ship with compress: false. Affected apps are those that have deliberately opted in to permessage-deflate.

Details

In lib/bandit/websocket/permessage_deflate.ex:111-115, :zlib.inflate/2 is called without an output-size limit, and IO.iodata_to_binary/1 then materializes the entire decompressed payload as one contiguous binary in the connection process's heap.

websocket_options.max_frame_size only bounds the on-the-wire (compressed) frame, not the decompressed output. With ~1024:1 compression on uniform data, an attacker can stay well under any wire-size cap while still forcing GiB-scale allocations. There is no {:more, ...} resumable path on inflate, so upstream callers cannot interpose a 413/close before the allocation completes.

The bug is gated by two server-side flags being true at the same time:

  • Bandit's global websocket_options.compress (defaults to true per bandit.ex:198-201).
  • The per-upgrade connection_opts.compress passed to WebSockAdapter.upgrade/4 (defaults to false per websock_adapter.ex:42-43; Phoenix's default is also false per phoenix/lib/phoenix/transports/websocket.ex:33).

Both must be true for the handshake at bandit/lib/bandit/websocket/handshake.ex:22 to negotiate permessage-deflate. So the bug is only reachable on apps that explicitly opt in (e.g. socket "/ws", MySocket, websocket: [compress: true] in a Phoenix endpoint, or WebSockAdapter.upgrade(conn, ..., compress: true) in a plain Plug app).

Suggested fix: thread a maximum-output-size through to inflate and either error out or return resumable chunks once exceeded, mirroring how the HTTP content-length path bounds reads via :length.

PoC

A fully self-contained reproducer is attached below. It boots a local Bandit server that performs a WebSockAdapter.upgrade(conn, EchoSocket, %{}, compress: true), opens one WebSocket connection, and sends a single text frame whose ~6 MiB compressed payload inflates to 6 GiB of zeros. Run it with elixir ws_permessage_deflate_bomb.exs.

Observed on a 16 GiB Mac (Bandit 1.10.4, Elixir 1.18, otherwise default config):

  • Frame on the wire: ~6 MiB.
  • BEAM RSS climbed from ~80 MiB to ~12 GiB peak during inflate (6 GiB inflated payload + a transient 6 GiB copy held by IO.iodata_to_binary/1), then settled at ~6 GiB until the connection process was GC'd.
  • Tuning @target_decompressed_bytes upward, or opening N parallel connections, OOM-kills the BEAM outright.

A separate observation worth flagging: in the default setup, something upstream caps wire-side frames at ~8 MiB even though Bandit's documented max_frame_size default is 0 (unlimited). The bug is reachable below that cap regardless, but the source of that effective cap is worth confirming.

Impact

Unauthenticated, pre-application-code denial-of-service via memory exhaustion. A single frame from a single client is sufficient to drive a small host to OOM; concurrent connections amplify linearly. The attacker needs only that the server accepts a WebSocket connection — no authentication, no valid route, no application cooperation.

Affected: any Bandit-fronted application that explicitly enables permessage-deflate on its WebSocket upgrade. Stock Phoenix and LiveView apps are not affected — both default to compress: false. Apps that opt in (typically for bandwidth savings on large payloads) inherit an unbounded-inflate DoS that the documentation does not warn about.

```elixir

# Bandit WebSocket permessage-deflate bomb PoC.

#

# lib/bandit/websocket/permessage_deflate.ex:111-115 calls :zlib.inflate/2

# with no output-size cap. A small (~4 MiB) compressed frame inflates to

# multiple GiB on the BEAM heap before any application code sees it.

#

# Note: in the default setup something upstream caps wire-side frames at

# ~8 MiB even though Bandit's documented max_frame_size default is 0

# (unlimited). The bug is reachable below that cap regardless.

#

# Run: elixir scripts/bandit/ws_permessage_deflate_bomb.exs

Mix.install([

{:bandit, "~> 1.10"},

{:plug, "~> 1.19"},

{:websock_adapter, "~> 0.5"}

])

defmodule EchoSocket do

@behaviour WebSock

def init(_opts), do: {:ok, %{}}

def handle_in(_message, state), do: {:ok, state}

def handle_info(_message, state), do: {:ok, state}

def terminate(_reason, state), do: {:ok, state}

end

defmodule DemoApp do

@behaviour Plug

def init(opts), do: opts

def call(conn, _opts) do

conn

|> WebSockAdapter.upgr

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. Rated impact: confidentiality none, integrity none, availability high.

CVSS metrics in full

The score comes from this vector: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

  • Attack vector: Network — reachable from anywhere that can route to the service.
  • Attack complexity: Low — the attack works reliably, with no preparation.
  • Attack requirements: Present — the target has to be in a particular state for the attack to work.
  • Privileges required: None — an unauthenticated stranger can try it.
  • User interaction: None — nobody has to be tricked into anything.
  • Confidentiality impact: None.
  • Integrity impact: None.
  • Availability impact: High — total loss, or loss the attacker controls.

Weakness class

CVE-2026-39804 is classified as CWE-770: Allocation of Resources Without Limits. Resources are allocated on request with no cap, so a client can exhaust them.

Affected software

CVE-2026-39804 is recorded against 2 packages.

  • bandit
  • unknown

Timeline and source

Published on 7 May 2026 and last revised on 30 July 2026. No public exploit is currently recorded for this entry. A vendor advisory or fix has been published. Record sourced from NVD.

References

github.com (Web)
nvd.nist.gov (Advisory)
github.com (Web)
cna.erlef.org (Web)
github.com (Package)
osv.dev (Web)

Other advisories for this package

bandit has other advisories on record. If you are patching this one, these are worth checking on the same host:

Same weakness in other software

These advisories are the same class of weakness (CWE-770: Allocation of Resources Without Limits) in other software:

Details

Severity HIGH
CVSS Score 8.0
CVSS Vector CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
CWE CWE-770
Public Exploit ✅ No
Source NVD
Published 2026-05-07
Updated 2026-08-20
Modified 2026-07-30

Affected Packages

Software From version Fixed in
bandit
unknown

Similar Threats

Site Security Check

Is bandit part of your stack?

CVE-2026-39804 is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesCVECVE 2026