eef-cve-2026-115269

CVSS 8.7 osv_hex
Description

## Summary Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 request body reader of mtrudel bandit allows an unauthenticated remote attacker to hold stream processes open indefinitely via WINDOW_UPDATE or empty SETTINGS frames sent while a Plug reads the request body. `Bandit.HTTP2.Stream.do_read_data/4` in `lib/bandit/http2/stream.ex` waits for each frame with a fresh `read_timeout`. A stream WINDOW_UPDATE, or the zero-delta window update that every empty SETTINGS frame delivers to all streams, ends the wait without delivering any body data, and the loop starts a new full countdown. A client that sends one of these 9-byte frames before each countdown expires keeps `read_body` blocked, together with the stream process and whatever the application holds while reading, for as long as it likes. This issue affects bandit: from 1.4.0 before 1.14.0. ## Details **1. Per-receive timeout.** `Bandit.HTTP2.Stream.read_data/2` passes a flat `read_timeout` (default 15,000 ms) to `do_read_data/4`, which blocks in `do_recv/2`'s `receive ... after timeout`. **2. Non-progress frames end the wait.** A stream WINDOW_UPDATE, delivered through `Bandit.HTTP2.Connection.handle_frame/3`, or an empty SETTINGS frame, which delivers a zero-delta window update to every stream, is matched inside that `receive`. `do_recv_send_window_update/2` returns a bare `%Stream{}`, and `do_read_data/4` recurses with the identical `timeout`, restarting the countdown. **3. Nothing else bounds the read.** The transport read timeout fires only when no bytes arrive, `max_bytes` is decremented only by DATA frames, and neither WINDOW_UPDATE nor SETTINGS frames are rate limited. The send path in `do_send_data/4` uses a progress-based deadline; the read path does not. ## Proof of concept 1. Run Bandit with a Plug that calls `Plug.Conn.read_body/2`. 2. Open an HTTP/2 connection and send HEADERS for `POST /` on stream 1 without END_STREAM. 3. Every 10 seconds, send a WINDOW_UPDATE on stream 1 with an increment of 1. 4. The stream stays open for 120 s, eight times the default 15 s `read_timeout`. Without the WINDOW_UPDATE frames, the server ends the read after about 15 s. ## Impact An unauthenticated client can keep a Bandit HTTP/2 stream, and whatever its request handler holds while reading the body, occupied indefinitely for 9 bytes per `read_timeout`, and repeat this across many streams and connections. This affects any Bandit server with HTTP/2 enabled whose routes read request bodies, which `Plug.Parsers` does for POST, PUT and PATCH. ## Workarounds Disable HTTP/2 with `http_2_options: [enabled: false]`, so that the vulnerable read path never runs.

Timeline
Published
unknown
Last Modified
unknown
CVSS Details

CVSS details not available.

Affected Products

No product information available.

Weaknesses (CWE)
References
Linked Vulnerabilities

No linked vulnerabilities found.

{
  "affected": [
    {
      "database_specific": {
        "source": "https://cna.erlef.org/osv/EEF-CVE-2026-115269.json"
      },
      "package": {
        "ecosystem": "Hex",
        "name": "bandit",
        "purl": "pkg:hex/bandit"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.4.0"
            },
            {
              "fixed": "1.14.0"
            }
          ],
          "type": "SEMVER"
        }
      ],
      "versions": [
        "1.10.0",
        "1.10.1",
        "1.10.2",
        "1.10.3",
        "1.10.4",
        "1.11.0",
        "1.11.1",
        "1.12.0",
        "1.12.1",
        "1.12.2",
        "1.12.3",
        "1.12.4",
        "1.12.5",
        "1.13.0",
        "1.13.1",
        "1.4.0",
        "1.4.1",
        "1.4.2",
        "1.5.0",
        "1.5.1",
        "1.5.2",
        "1.5.3",
        "1.5.4",
        "1.5.5",
        "1.5.6",
        "1.5.7",
        "1.6.0",
        "1.6.1",
        "1.6.10",
        "1.6.11",
        "1.6.2",
        "1.6.3",
        "1.6.4",
        "1.6.5",
        "1.6.6",
        "1.6.7",
        "1.6.8",
        "1.6.9",
        "1.7.0",
        "1.8.0",
        "1.9.0"
      ]
    },
    {
      "database_specific": {
        "source": "https://cna.erlef.org/osv/EEF-CVE-2026-115269.json"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "fff06efe3be962b484bd4d3eb339372fda5a231f"
            },
            {
              "fixed": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "type": "GIT"
        }
      ],
      "versions": [
        "1.13.1",
        "1.13.0",
        "1.12.5",
        "1.12.4",
        "1.12.3",
        "1.12.1",
        "1.12.0",
        "1.11.1",
        "1.11.0",
        "1.10.4",
        "1.10.3",
        "1.10.2",
        "1.10.1",
        "1.10.0",
        "1.9.0",
        "1.8.0",
        "1.7.0",
        "1.6.11",
        "1.6.10",
        "1.6.9",
        "1.6.8",
        "1.6.7",
        "1.6.6",
        "1.6.5",
        "1.6.4",
        "1.6.3",
        "1.6.2",
        "1.6.1",
        "1.6.0",
        "1.5.7",
        "1.5.6",
        "1.5.5",
        "1.5.4",
        "1.5.3",
        "1.5.2",
        "1.5.1",
        "1.5.0",
        "1.4.2",
        "1.4.1",
        "1.4.0"
      ]
    }
  ],
  "aliases": [
    "CVE-2026-115269",
    "GHSA-p2qv-vx88-385x"
  ],
  "credits": [
    {
      "name": "Peter Ullrich",
      "type": "ANALYST"
    },
    {
      "name": "Daniel Coles",
      "type": "REPORTER"
    },
    {
      "name": "Mat Trudel",
      "type": "REMEDIATION_DEVELOPER"
    },
    {
      "name": "Jonatan Männchen / EEF",
      "type": "ANALYST"
    }
  ],
  "database_specific": {
    "capec_ids": [
      "CAPEC-469"
    ],
    "cpe_ids": [
      "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
    ],
    "cwe_ids": [
      "CWE-835",
      "CWE-770"
    ]
  },
  "details": "## Summary\n\nAllocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 request body reader of mtrudel bandit allows an unauthenticated remote attacker to hold stream processes open indefinitely via WINDOW_UPDATE or empty SETTINGS frames sent while a Plug reads the request body.\n\n`Bandit.HTTP2.Stream.do_read_data/4` in `lib/bandit/http2/stream.ex` waits for each frame with a fresh `read_timeout`. A stream WINDOW_UPDATE, or the zero-delta window update that every empty SETTINGS frame delivers to all streams, ends the wait without delivering any body data, and the loop starts a new full countdown. A client that sends one of these 9-byte frames before each countdown expires keeps `read_body` blocked, together with the stream process and whatever the application holds while reading, for as long as it likes.\n\nThis issue affects bandit: from 1.4.0 before 1.14.0.\n\n## Details\n\n**1. Per-receive timeout.** `Bandit.HTTP2.Stream.read_data/2` passes a flat `read_timeout` (default 15,000 ms) to `do_read_data/4`, which blocks in `do_recv/2`'s `receive ... after timeout`.\n\n**2. Non-progress frames end the wait.** A stream WINDOW_UPDATE, delivered through `Bandit.HTTP2.Connection.handle_frame/3`, or an empty SETTINGS frame, which delivers a zero-delta window update to every stream, is matched inside that `receive`. `do_recv_send_window_update/2` returns a bare `%Stream{}`, and `do_read_data/4` recurses with the identical `timeout`, restarting the countdown.\n\n**3. Nothing else bounds the read.** The transport read timeout fires only when no bytes arrive, `max_bytes` is decremented only by DATA frames, and neither WINDOW_UPDATE nor SETTINGS frames are rate limited. The send path in `do_send_data/4` uses a progress-based deadline; the read path does not.\n\n## Proof of concept\n\n1. Run Bandit with a Plug that calls `Plug.Conn.read_body/2`.\n2. Open an HTTP/2 connection and send HEADERS for `POST /` on stream 1 without END_STREAM.\n3. Every 10 seconds, send a WINDOW_UPDATE on stream 1 with an increment of 1.\n4. The stream stays open for 120 s, eight times the default 15 s `read_timeout`. Without the WINDOW_UPDATE frames, the server ends the read after about 15 s.\n\n## Impact\n\nAn unauthenticated client can keep a Bandit HTTP/2 stream, and whatever its request handler holds while reading the body, occupied indefinitely for 9 bytes per `read_timeout`, and repeat this across many streams and connections. This affects any Bandit server with HTTP/2 enabled whose routes read request bodies, which `Plug.Parsers` does for POST, PUT and PATCH.\n\n## Workarounds\n\nDisable HTTP/2 with `http_2_options: [enabled: false]`, so that the vulnerable read path never runs.",
  "id": "EEF-CVE-2026-115269",
  "modified": "2026-10-26T13:56:07.167679161Z",
  "published": "2026-10-26T13:30:24.621Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-p2qv-vx88-385x"
    },
    {
      "type": "WEB",
      "url": "https://cna.erlef.org/cves/CVE-2026-115269.html"
    },
    {
      "type": "WEB",
      "url": "https://github.com/mtrudel/bandit/commit/fff06efe3be962b484bd4d3eb339372fda5a231f"
    },
    {
      "type": "FIX",
      "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
    },
    {
      "type": "PACKAGE",
      "url": "https://hex.pm/packages/bandit"
    }
  ],
  "schema_version": "1.9.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "HTTP/2 WINDOW_UPDATE and SETTINGS frames reset the request body read timeout in Bandit"
}
View JSON API Download JSON