CVE-2026-39803 Overview
CVE-2026-39803 is an unauthenticated denial-of-service vulnerability in the Bandit HTTP server for Elixir, maintained by mtrudel. The flaw lives in the chunked clause of Elixir.Bandit.HTTP1.Socket.read_data/2 in lib/bandit/http1/socket.ex. The function ignores the caller-supplied :length option when reading HTTP/1 chunked request bodies. Attackers can send a single Transfer-Encoding: chunked POST to any path and force the BEAM process to exhaust memory until the OS Out-Of-Memory (OOM) killer terminates it. The vulnerability affects Bandit versions from 1.4.0 before 1.11.1 and is tracked under [CWE-770: Allocation of Resources Without Limits or Throttling].
Critical Impact
An unauthenticated remote attacker can crash any Phoenix or Plug application running Bandit by sending one oversized chunked HTTP request, with no valid route or credentials required.
Affected Products
- mtrudel Bandit 1.4.0 through 1.11.0
- Phoenix endpoints using Bandit as the HTTP server
- Plug-based Elixir applications relying on Plug.Parsers defaults
Discovery Timeline
- 2026-05-13 - CVE-2026-39803 published to NVD
- 2026-05-13 - Last updated in NVD database
Technical Details for CVE-2026-39803
Vulnerability Analysis
The vulnerability sits in do_read_chunked_data!/5, the helper invoked by the chunked branch of read_data/2. When a client sends an HTTP/1.1 request with Transfer-Encoding: chunked, Bandit reads each chunk off the socket and appends it to an iolist accumulator. The function never compares the accumulated length against the caller-supplied :length option that Plug.Parsers passes in (default 8 MB). Once the stream ends, Bandit materializes the entire iolist into a single binary and returns {:ok, body, ...} regardless of size.
Because the function always returns :ok, upstream callers cannot intercept the condition and respond with HTTP 413 Payload Too Large. Plug.Parsers runs early in the standard Phoenix endpoint pipeline, before routing and authentication plugs execute. An attacker therefore needs no valid URL, no session, and no credentials. The content-length branch of the same function correctly enforces the limit and is unaffected.
Root Cause
The root cause is missing bounds enforcement in the chunked-decoding path. The :length option, which exists specifically to cap accumulated body size, is silently dropped. This is a classic [CWE-770] resource allocation flaw where untrusted input directly drives memory growth.
Attack Vector
Exploitation requires one HTTPS or HTTP request to any path served by a vulnerable Bandit instance. The attacker sets Transfer-Encoding: chunked and streams arbitrarily many chunks. The BEAM virtual machine grows until the kernel OOM killer terminates the entire Erlang node, taking down every application running on that VM. See the GitHub Security Advisory GHSA-9q9q-324x-93r2 and the fix commit for the technical details of the patched flow.
Detection Methods for CVE-2026-39803
Indicators of Compromise
- HTTP/1.1 requests carrying Transfer-Encoding: chunked with sustained, large chunk streams against arbitrary paths, including endpoints that should not receive POST traffic.
- Sudden BEAM virtual machine restarts correlated with kernel logs showing oom-killer terminating the beam.smp process.
- Memory usage on application nodes climbing sharply during a single inbound connection rather than across many concurrent sessions.
Detection Strategies
- Inspect web server and reverse-proxy access logs for chunked POST requests with no Content-Length and unusually long transfer durations from a single source IP.
- Correlate Linux dmesg or journalctl OOM-killer events against Phoenix or Bandit application crash logs to confirm exploitation attempts.
- Add Bandit version fingerprinting to software bill of materials (SBOM) scans to flag any deployment running 1.4.0 through 1.11.0.
Monitoring Recommendations
- Alert on rapid resident memory growth of beam.smp processes exceeding a defined threshold within a short window.
- Track per-source-IP rates of chunked-encoded POST requests at the load balancer or web application firewall (WAF) layer.
- Monitor restart counts and crash loops of Phoenix nodes in orchestration platforms such as Kubernetes or systemd.
How to Mitigate CVE-2026-39803
Immediate Actions Required
- Upgrade Bandit to version 1.11.1 or later in mix.exs and redeploy all Elixir services that embed it.
- Place a reverse proxy or WAF in front of Bandit to enforce a maximum request body size on chunked transfers until the upgrade is complete.
- Audit running Phoenix releases to confirm the Bandit dependency version pinned in mix.lock matches the patched release.
Patch Information
The fix is included in Bandit 1.11.1. The patched do_read_chunked_data!/5 now honors the :length option and returns an error tuple when the accumulated body exceeds the configured cap, allowing callers to respond with HTTP 413. Review the upstream commit and the CNA advisory for full release notes.
Workarounds
- Terminate TLS at a proxy such as nginx or HAProxy and set client_max_body_size to enforce an upper bound before traffic reaches Bandit.
- Reject Transfer-Encoding: chunked requests at the edge for endpoints that only need to accept fixed-length payloads.
- Run Bandit nodes under a memory cgroup limit so a single compromised process is contained without taking down the host.
# nginx body-size cap as a temporary mitigation in front of Bandit
http {
client_max_body_size 8m;
client_body_buffer_size 128k;
server {
listen 443 ssl;
location / {
proxy_pass http://bandit_upstream;
proxy_request_buffering on;
}
}
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

