Refine your search

24 vulnerabilities found for bandit by mtrudel

CVE-2026-116078 (GCVE-0-2026-116078)
Vulnerability from cvelistv5
Published
2026-10-28 13:26
Modified
2026-10-28 13:26
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
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 exhaust server memory via a request body sent as many 1-byte DATA frames. do_read_data/5 in lib/bandit/http2/stream.ex keeps every DATA frame payload as its own list element until the read returns. The :length option of read_body/2 counts body bytes, not frames, so a body of 1-byte frames costs a list cell and a binary term per byte. Interleaving the frames across up to 500 concurrent streams per connection keeps each stream consuming its frames as they arrive; in the reporter's test, 100 streams held 25 to 40 MiB of stream process memory for 5 MB sent. Only routes whose Plug reads the request body are affected, which includes any route behind Plug.Parsers. This issue affects bandit: from 0.3.1 before 1.14.1.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.3.1   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: c8dc1e97050cf359f61ed6cdd110f3cdaf1d6f5f
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.1",
              "status": "affected",
              "version": "0.3.1",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "327bb02ff84171237e1aeff787930debd16155ee",
              "status": "affected",
              "version": "c8dc1e97050cf359f61ed6cdd110f3cdaf1d6f5f",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.1",
                  "versionStartIncluding": "0.3.1",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Neil Berkman / Xuku LLC"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-28T13:06:40.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAllocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 request body reader of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a request body sent as many 1-byte DATA frames.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edo_read_data/5\u003c/code\u003e in \u003ccode\u003elib/bandit/http2/stream.ex\u003c/code\u003e keeps every DATA frame payload as its own list element until the read returns. The \u003ccode\u003e:length\u003c/code\u003e option of \u003ccode\u003eread_body/2\u003c/code\u003e counts body bytes, not frames, so a body of 1-byte frames costs a list cell and a binary term per byte. Interleaving the frames across up to 500 concurrent streams per connection keeps each stream consuming its frames as they arrive; in the reporter\u0027s test, 100 streams held 25 to 40 MiB of stream process memory for 5 MB sent. Only routes whose Plug reads the request body are affected, which includes any route behind \u003ccode\u003ePlug.Parsers\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.3.1 before 1.14.1.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 request body reader of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a request body sent as many 1-byte DATA frames.\n\n`do_read_data/5` in `lib/bandit/http2/stream.ex` keeps every DATA frame payload as its own list element until the read returns. The `:length` option of `read_body/2` counts body bytes, not frames, so a body of 1-byte frames costs a list cell and a binary term per byte. Interleaving the frames across up to 500 concurrent streams per connection keeps each stream consuming its frames as they arrive; in the reporter\u0027s test, 100 streams held 25 to 40 MiB of stream process memory for 5 MB sent. Only routes whose Plug reads the request body are affected, which includes any route behind `Plug.Parsers`.\n\nThis issue affects bandit: from 0.3.1 before 1.14.1."
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 request body reader of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a request body sent as many 1-byte DATA frames.\n\ndo_read_data/5 in lib/bandit/http2/stream.ex keeps every DATA frame payload as its own list element until the read returns. The :length option of read_body/2 counts body bytes, not frames, so a body of 1-byte frames costs a list cell and a binary term per byte. Interleaving the frames across up to 500 concurrent streams per connection keeps each stream consuming its frames as they arrive; in the reporter\u0027s test, 100 streams held 25 to 40 MiB of stream process memory for 5 MB sent. Only routes whose Plug reads the request body are affected, which includes any route behind Plug.Parsers.\n\nThis issue affects bandit: from 0.3.1 before 1.14.1."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated client can make a Bandit HTTP/2 server hold about 5 to 8 times the bytes it sends in stream process memory, across up to 500 streams per connection and many connections, for as long as each body read runs. This affects any Bandit server with HTTP/2 enabled whose routes read request bodies, which \u003ccode\u003ePlug.Parsers\u003c/code\u003e does for POST, PUT and PATCH.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated client can make a Bandit HTTP/2 server hold about 5 to 8 times the bytes it sends in stream process memory, across up to 500 streams per connection and many connections, for as long as each body read runs. This affects any Bandit server with HTTP/2 enabled whose routes read request bodies, which `Plug.Parsers` does for POST, PUT and PATCH."
                }
              ],
              "value": "An unauthenticated client can make a Bandit HTTP/2 server hold about 5 to 8 times the bytes it sends in stream process memory, across up to 500 streams per connection and many connections, for as long as each body read runs. This affects any Bandit server with HTTP/2 enabled whose routes read request bodies, which Plug.Parsers does for POST, PUT and PATCH."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-28T13:26:25.372Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "HTTP/2 request bodies made of 1-byte DATA frames amplify memory use in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-pxjf-g536-7r93"
        },
        {
          "name": "EEF CNA record for CVE-2026-116078",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-116078.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-116078",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-116078"
        },
        {
          "name": "Introducing commit c8dc1e9 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/c8dc1e97050cf359f61ed6cdd110f3cdaf1d6f5f"
        },
        {
          "name": "Fix commit 327bb02 in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/327bb02ff84171237e1aeff787930debd16155ee"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "HTTP/2 request bodies made of 1-byte DATA frames amplify memory use in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eFrom 0.7.0, disable HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e, so that the vulnerable body reader never runs.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "From 0.7.0, disable HTTP/2 with `http_2_options: [enabled: false]`, so that the vulnerable body reader never runs."
            }
          ],
          "value": "From 0.7.0, disable HTTP/2 with http_2_options: [enabled: false], so that the vulnerable body reader never runs."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eRun Bandit with HTTP/2 enabled and a Plug that calls \u003ccode\u003ePlug.Conn.read_body(conn)\u003c/code\u003e with default options.\u003c/li\u003e\n\u003cli\u003eOpen an h2c connection, exchange prefaces and SETTINGS, and send HEADERS for \u003ccode\u003ePOST /\u003c/code\u003e without END_STREAM on 100 streams (IDs 1, 3, 5, ...).\u003c/li\u003e\n\u003cli\u003eSend 5,000 rounds of 1-byte DATA frames, one frame per stream per round, batched into writes of 100 frames.\u003c/li\u003e\n\u003cli\u003eThree seconds later, while the reads are still open, the 100 stream processes hold 25 to 40 MiB combined with empty mailboxes, for about 5 MB sent.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Run Bandit with HTTP/2 enabled and a Plug that calls `Plug.Conn.read_body(conn)` with default options.\n2. Open an h2c connection, exchange prefaces and SETTINGS, and send HEADERS for `POST /` without END_STREAM on 100 streams (IDs 1, 3, 5, ...).\n3. Send 5,000 rounds of 1-byte DATA frames, one frame per stream per round, batched into writes of 100 frames.\n4. Three seconds later, while the reads are still open, the 100 stream processes hold 25 to 40 MiB combined with empty mailboxes, for about 5 MB sent."
            }
          ],
          "value": "* Run Bandit with HTTP/2 enabled and a Plug that calls Plug.Conn.read_body(conn) with default options.\n* Open an h2c connection, exchange prefaces and SETTINGS, and send HEADERS for POST / without END_STREAM on 100 streams (IDs 1, 3, 5, ...).\n* Send 5,000 rounds of 1-byte DATA frames, one frame per stream per round, batched into writes of 100 frames.\n* Three seconds later, while the reads are still open, the 100 stream processes hold 25 to 40 MiB combined with empty mailboxes, for about 5 MB sent."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Per-frame accumulation.\u003c/strong\u003e \u003ccode\u003eBandit.HTTP2.Stream.read_data/2\u003c/code\u003e calls \u003ccode\u003edo_read_data/5\u003c/code\u003e in \u003ccode\u003elib/bandit/http2/stream.ex\u003c/code\u003e, which prepends each \u003ccode\u003e{:data, data, stream}\u003c/code\u003e payload to a list (\u003ccode\u003e[data | acc]\u003c/code\u003e) and reverses it when the read returns. Each 1-byte payload arrives as a separate message from the connection process, so the stream heap holds a cons cell and a heap binary per frame.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Byte-only limit.\u003c/strong\u003e \u003ccode\u003e:length\u003c/code\u003e (default 8,000,000) is decremented by \u003ccode\u003ebyte_size(data)\u003c/code\u003e and bounds body bytes, not the number of frames. A read ends at \u003ccode\u003e:length\u003c/code\u003e or at its \u003ccode\u003eread_timeout\u003c/code\u003e deadline, and 1-byte frames add no measurable deadline credit, so a stream holds whatever arrives within the deadline at the per-frame overhead ratio. \u003ccode\u003ePlug.Parsers\u003c/code\u003e answers 413 on \u003ccode\u003e:more\u003c/code\u003e, and the client continues on a new stream.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Interleaving defeats connection buffering.\u003c/strong\u003e The connection buffers DATA for a stream only once that stream has 16 messages outstanding. A stream that keeps up never reaches this, and interleaving one frame per stream per round across many streams keeps every stream under it regardless of TCP segment size.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Measurements on 1.14.0.\u003c/strong\u003e 100 streams of 5,000 interleaved 1-byte frames (5,000,000 bytes on the wire, 488 KiB of body) held 25 to 40 MiB in stream processes, with empty mailboxes. One stream of 60,000 frames sent one per write held 2 to 4 MiB for 58 KiB of body.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Per-frame accumulation.** `Bandit.HTTP2.Stream.read_data/2` calls `do_read_data/5` in `lib/bandit/http2/stream.ex`, which prepends each `{:data, data, stream}` payload to a list (`[data | acc]`) and reverses it when the read returns. Each 1-byte payload arrives as a separate message from the connection process, so the stream heap holds a cons cell and a heap binary per frame.\n\n**2. Byte-only limit.** `:length` (default 8,000,000) is decremented by `byte_size(data)` and bounds body bytes, not the number of frames. A read ends at `:length` or at its `read_timeout` deadline, and 1-byte frames add no measurable deadline credit, so a stream holds whatever arrives within the deadline at the per-frame overhead ratio. `Plug.Parsers` answers 413 on `:more`, and the client continues on a new stream.\n\n**3. Interleaving defeats connection buffering.** The connection buffers DATA for a stream only once that stream has 16 messages outstanding. A stream that keeps up never reaches this, and interleaving one frame per stream per round across many streams keeps every stream under it regardless of TCP segment size.\n\n**4. Measurements on 1.14.0.** 100 streams of 5,000 interleaved 1-byte frames (5,000,000 bytes on the wire, 488 KiB of body) held 25 to 40 MiB in stream processes, with empty mailboxes. One stream of 60,000 frames sent one per write held 2 to 4 MiB for 58 KiB of body."
            }
          ],
          "value": "1. Per-frame accumulation. Bandit.HTTP2.Stream.read_data/2 calls do_read_data/5 in lib/bandit/http2/stream.ex, which prepends each {:data, data, stream} payload to a list ([data | acc]) and reverses it when the read returns. Each 1-byte payload arrives as a separate message from the connection process, so the stream heap holds a cons cell and a heap binary per frame.\n\n2. Byte-only limit. :length (default 8,000,000) is decremented by byte_size(data) and bounds body bytes, not the number of frames. A read ends at :length or at its read_timeout deadline, and 1-byte frames add no measurable deadline credit, so a stream holds whatever arrives within the deadline at the per-frame overhead ratio. Plug.Parsers answers 413 on :more, and the client continues on a new stream.\n\n3. Interleaving defeats connection buffering. The connection buffers DATA for a stream only once that stream has 16 messages outstanding. A stream that keeps up never reaches this, and interleaving one frame per stream per round across many streams keeps every stream under it regardless of TCP segment size.\n\n4. Measurements on 1.14.0. 100 streams of 5,000 interleaved 1-byte frames (5,000,000 bytes on the wire, 488 KiB of body) held 25 to 40 MiB in stream processes, with empty mailboxes. One stream of 60,000 frames sent one per write held 2 to 4 MiB for 58 KiB of body."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-116078",
    "datePublished": "2026-10-28T13:26:25.372Z",
    "dateReserved": "2026-10-25T12:30:01.606Z",
    "dateUpdated": "2026-10-28T13:26:25.372Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-108529 (GCVE-0-2026-108529)
Vulnerability from cvelistv5
Published
2026-10-26 13:35
Modified
2026-10-26 15:35
CWE
  • CWE-407 - Inefficient Algorithmic Complexity
Summary
Inefficient Algorithmic Complexity vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server CPU via small SETTINGS or connection-level WINDOW_UPDATE frames. The attacker opens thousands of streams whose responses block on the connection send window, then sends cheap frames that each make the server walk the whole queue of blocked sends. In the reporter's run, with 3,000 blocked streams a single 9-byte SETTINGS frame took 133 ms to process. do_pending_sends/2 in lib/bandit/http2/connection.ex retries every queued send and, before each retry, removes that entry from the queue with List.delete/2, so one frame costs O(n²) list operations for n blocked streams. max_concurrent_streams defaults to :infinity, so the attacker chooses n. This issue affects bandit: from 0.3.4 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.3.4   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 6feadb31189d18b7dcda6c663112cd3bbaf63a46
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-108529",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:01:20.277475Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:50.781Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "0.3.4",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "6feadb31189d18b7dcda6c663112cd3bbaf63a46",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "0.3.4",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T10:25:55.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eInefficient Algorithmic Complexity vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server CPU via small SETTINGS or connection-level WINDOW_UPDATE frames. The attacker opens thousands of streams whose responses block on the connection send window, then sends cheap frames that each make the server walk the whole queue of blocked sends. In the reporter\u0027s run, with 3,000 blocked streams a single 9-byte SETTINGS frame took 133 ms to process.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edo_pending_sends/2\u003c/code\u003e in \u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e retries every queued send and, before each retry, removes that entry from the queue with \u003ccode\u003eList.delete/2\u003c/code\u003e, so one frame costs O(n\u00b2) list operations for n blocked streams. \u003ccode\u003emax_concurrent_streams\u003c/code\u003e defaults to \u003ccode\u003e:infinity\u003c/code\u003e, so the attacker chooses n.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.3.4 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Inefficient Algorithmic Complexity vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server CPU via small SETTINGS or connection-level WINDOW_UPDATE frames. The attacker opens thousands of streams whose responses block on the connection send window, then sends cheap frames that each make the server walk the whole queue of blocked sends. In the reporter\u0027s run, with 3,000 blocked streams a single 9-byte SETTINGS frame took 133 ms to process.\n\n`do_pending_sends/2` in `lib/bandit/http2/connection.ex` retries every queued send and, before each retry, removes that entry from the queue with `List.delete/2`, so one frame costs O(n\u00b2) list operations for n blocked streams. `max_concurrent_streams` defaults to `:infinity`, so the attacker chooses n.\n\nThis issue affects bandit: from 0.3.4 before 1.14.0."
            }
          ],
          "value": "Inefficient Algorithmic Complexity vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server CPU via small SETTINGS or connection-level WINDOW_UPDATE frames. The attacker opens thousands of streams whose responses block on the connection send window, then sends cheap frames that each make the server walk the whole queue of blocked sends. In the reporter\u0027s run, with 3,000 blocked streams a single 9-byte SETTINGS frame took 133 ms to process.\n\ndo_pending_sends/2 in lib/bandit/http2/connection.ex retries every queued send and, before each retry, removes that entry from the queue with List.delete/2, so one frame costs O(n\u00b2) list operations for n blocked streams. max_concurrent_streams defaults to :infinity, so the attacker chooses n.\n\nThis issue affects bandit: from 0.3.4 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated remote client that keeps thousands of HTTP/2 response streams blocked makes each 9-byte SETTINGS or stream-0 WINDOW_UPDATE frame cost the server quadratic CPU work. A steady trickle of such frames keeps the connection process, and the scheduler running it, busy for as long as the attacker continues.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated remote client that keeps thousands of HTTP/2 response streams blocked makes each 9-byte SETTINGS or stream-0 WINDOW_UPDATE frame cost the server quadratic CPU work. A steady trickle of such frames keeps the connection process, and the scheduler running it, busy for as long as the attacker continues."
                }
              ],
              "value": "An unauthenticated remote client that keeps thousands of HTTP/2 response streams blocked makes each 9-byte SETTINGS or stream-0 WINDOW_UPDATE frame cost the server quadratic CPU work. A steady trickle of such frames keeps the connection process, and the scheduler running it, busy for as long as the attacker continues."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-407",
              "description": "CWE-407 Inefficient Algorithmic Complexity",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:35:26.080Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "SETTINGS and stream-0 WINDOW_UPDATE frames trigger quadratic work over blocked streams",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-j363-h72p-296m"
        },
        {
          "name": "EEF CNA record for CVE-2026-108529",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-108529.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-108529",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-108529"
        },
        {
          "name": "Introducing commit 6feadb3 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/6feadb31189d18b7dcda6c663112cd3bbaf63a46"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "SETTINGS and stream-0 WINDOW_UPDATE frames trigger quadratic work over blocked streams in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eFrom 1.9.0, bound the number of streams a client can open, for example with \u003ccode\u003ehttp_2_options: [default_local_settings: [max_concurrent_streams: 100]]\u003c/code\u003e, which bounds the work each frame can cause. From 0.7.0, disabling HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e removes the vulnerable path.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "From 1.9.0, bound the number of streams a client can open, for example with `http_2_options: [default_local_settings: [max_concurrent_streams: 100]]`, which bounds the work each frame can cause. From 0.7.0, disabling HTTP/2 with `http_2_options: [enabled: false]` removes the vulnerable path."
            }
          ],
          "value": "From 1.9.0, bound the number of streams a client can open, for example with http_2_options: [default_local_settings: [max_concurrent_streams: 100]], which bounds the work each frame can cause. From 0.7.0, disabling HTTP/2 with http_2_options: [enabled: false] removes the vulnerable path."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eOn one HTTP/2 connection, send 3,000 requests to an endpoint with a 20 KB response body, and send no connection-level \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e, so the responses queue in \u003ccode\u003epending_sends\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSend an empty SETTINGS frame followed by a PING.\u003c/li\u003e\n\u003cli\u003eTime the PING acknowledgement. In the reporter\u0027s run, the single 9-byte SETTINGS frame delayed it by 133 ms. Repeating the frame keeps the connection process busy.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. On one HTTP/2 connection, send 3,000 requests to an endpoint with a 20 KB response body, and send no connection-level `WINDOW_UPDATE`, so the responses queue in `pending_sends`.\n2. Send an empty SETTINGS frame followed by a PING.\n3. Time the PING acknowledgement. In the reporter\u0027s run, the single 9-byte SETTINGS frame delayed it by 133 ms. Repeating the frame keeps the connection process busy."
            }
          ],
          "value": "* On one HTTP/2 connection, send 3,000 requests to an endpoint with a 20 KB response body, and send no connection-level WINDOW_UPDATE, so the responses queue in pending_sends.\n* Send an empty SETTINGS frame followed by a PING.\n* Time the PING acknowledgement. In the reporter\u0027s run, the single 9-byte SETTINGS frame delayed it by 133 ms. Repeating the frame keeps the connection process busy."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Blocked sends\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eA response that outruns the connection send window is queued in \u003ccode\u003epending_sends\u003c/code\u003e. With \u003ccode\u003emax_concurrent_streams\u003c/code\u003e defaulting to \u003ccode\u003e:infinity\u003c/code\u003e and no connection-level \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e from the client, every one of n streams ends up queued.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Trigger\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003eEvery non-ACK SETTINGS frame and every \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e on stream 0 calls \u003ccode\u003edo_pending_sends/2\u003c/code\u003e. SETTINGS handling also sends \u003ccode\u003edeliver_send_window_update\u003c/code\u003e to every stream even when the window delta is 0, so unread messages pile up in the mailboxes of blocked streams.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Quadratic drain\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edo_pending_sends/2\u003c/code\u003e walks the queue and calls \u003ccode\u003eList.delete/2\u003c/code\u003e on the whole list before retrying each entry, putting it back at the front if the send is still blocked. One frame therefore costs O(n\u00b2) list operations, about 10^8 at n = 10,000.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Blocked sends**\n\nA response that outruns the connection send window is queued in `pending_sends`. With `max_concurrent_streams` defaulting to `:infinity` and no connection-level `WINDOW_UPDATE` from the client, every one of n streams ends up queued.\n\n**2. Trigger** (`lib/bandit/http2/connection.ex`)\n\nEvery non-ACK SETTINGS frame and every `WINDOW_UPDATE` on stream 0 calls `do_pending_sends/2`. SETTINGS handling also sends `deliver_send_window_update` to every stream even when the window delta is 0, so unread messages pile up in the mailboxes of blocked streams.\n\n**3. Quadratic drain**\n\n`do_pending_sends/2` walks the queue and calls `List.delete/2` on the whole list before retrying each entry, putting it back at the front if the send is still blocked. One frame therefore costs O(n\u00b2) list operations, about 10^8 at n = 10,000."
            }
          ],
          "value": "1. Blocked sends\n\nA response that outruns the connection send window is queued in pending_sends. With max_concurrent_streams defaulting to :infinity and no connection-level WINDOW_UPDATE from the client, every one of n streams ends up queued.\n\n2. Trigger (lib/bandit/http2/connection.ex)\n\nEvery non-ACK SETTINGS frame and every WINDOW_UPDATE on stream 0 calls do_pending_sends/2. SETTINGS handling also sends deliver_send_window_update to every stream even when the window delta is 0, so unread messages pile up in the mailboxes of blocked streams.\n\n3. Quadratic drain\n\ndo_pending_sends/2 walks the queue and calls List.delete/2 on the whole list before retrying each entry, putting it back at the front if the send is still blocked. One frame therefore costs O(n\u00b2) list operations, about 10^8 at n = 10,000."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-108529",
    "datePublished": "2026-10-26T13:35:26.080Z",
    "dateReserved": "2026-10-12T16:15:01.628Z",
    "dateUpdated": "2026-10-26T15:35:50.781Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-110499 (GCVE-0-2026-110499)
Vulnerability from cvelistv5
Published
2026-10-26 13:32
Modified
2026-10-26 15:35
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to grow per-connection memory and CPU cost without bound via a large SETTINGS_HEADER_TABLE_SIZE. Bandit resizes its HPACK encoder table to whatever size the client advertises, up to 2^32-1 bytes, and indexes response headers into it. Because responses carry distinct values such as date or content-length, nothing is evicted and the table grows with every response for the life of the connection, while each lookup scans it linearly. The SETTINGS handling in Bandit.HTTP2.Connection (lib/bandit/http2/connection.ex) passes remote_settings.header_table_size to HPAX.resize/2 with no server-side cap, and encode_headers encodes every response header as :store. Serving N responses on one connection therefore costs O(N²) CPU in the connection process. This issue affects bandit: from 0.3.6 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.3.6   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 0e68313fab9d204257ebad5c447e4cd9cea8e0ea
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-110499",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:02:00.207666Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:50.929Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "0.3.6",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "0e68313fab9d204257ebad5c447e4cd9cea8e0ea",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "0.3.6",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T10:42:03.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAllocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to grow per-connection memory and CPU cost without bound via a large \u003ccode\u003eSETTINGS_HEADER_TABLE_SIZE\u003c/code\u003e. Bandit resizes its HPACK encoder table to whatever size the client advertises, up to 2^32-1 bytes, and indexes response headers into it. Because responses carry distinct values such as \u003ccode\u003edate\u003c/code\u003e or \u003ccode\u003econtent-length\u003c/code\u003e, nothing is evicted and the table grows with every response for the life of the connection, while each lookup scans it linearly.\u003c/p\u003e\n\u003cp\u003eThe SETTINGS handling in \u003ccode\u003eBandit.HTTP2.Connection\u003c/code\u003e (\u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e) passes \u003ccode\u003eremote_settings.header_table_size\u003c/code\u003e to \u003ccode\u003eHPAX.resize/2\u003c/code\u003e with no server-side cap, and \u003ccode\u003eencode_headers\u003c/code\u003e encodes every response header as \u003ccode\u003e:store\u003c/code\u003e. Serving N responses on one connection therefore costs O(N\u00b2) CPU in the connection process.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.3.6 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to grow per-connection memory and CPU cost without bound via a large `SETTINGS_HEADER_TABLE_SIZE`. Bandit resizes its HPACK encoder table to whatever size the client advertises, up to 2^32-1 bytes, and indexes response headers into it. Because responses carry distinct values such as `date` or `content-length`, nothing is evicted and the table grows with every response for the life of the connection, while each lookup scans it linearly.\n\nThe SETTINGS handling in `Bandit.HTTP2.Connection` (`lib/bandit/http2/connection.ex`) passes `remote_settings.header_table_size` to `HPAX.resize/2` with no server-side cap, and `encode_headers` encodes every response header as `:store`. Serving N responses on one connection therefore costs O(N\u00b2) CPU in the connection process.\n\nThis issue affects bandit: from 0.3.6 before 1.14.0."
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to grow per-connection memory and CPU cost without bound via a large SETTINGS_HEADER_TABLE_SIZE. Bandit resizes its HPACK encoder table to whatever size the client advertises, up to 2^32-1 bytes, and indexes response headers into it. Because responses carry distinct values such as date or content-length, nothing is evicted and the table grows with every response for the life of the connection, while each lookup scans it linearly.\n\nThe SETTINGS handling in Bandit.HTTP2.Connection (lib/bandit/http2/connection.ex) passes remote_settings.header_table_size to HPAX.resize/2 with no server-side cap, and encode_headers encodes every response header as :store. Serving N responses on one connection therefore costs O(N\u00b2) CPU in the connection process.\n\nThis issue affects bandit: from 0.3.6 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-125",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated remote client that keeps an HTTP/2 connection busy with cheap requests makes every response on it progressively more expensive, because the encoder table it enlarged never shrinks. Holding several such connections open ties up server CPU and memory for as long as the attacker keeps them alive.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated remote client that keeps an HTTP/2 connection busy with cheap requests makes every response on it progressively more expensive, because the encoder table it enlarged never shrinks. Holding several such connections open ties up server CPU and memory for as long as the attacker keeps them alive."
                }
              ],
              "value": "An unauthenticated remote client that keeps an HTTP/2 connection busy with cheap requests makes every response on it progressively more expensive, because the encoder table it enlarged never shrinks. Holding several such connections open ties up server CPU and memory for as long as the attacker keeps them alive."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:32:38.613Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "Client-chosen HPACK encoder table size is applied uncapped, growing per-connection state with every response in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-47x4-3fxf-82fr"
        },
        {
          "name": "EEF CNA record for CVE-2026-110499",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-110499.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-110499",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-110499"
        },
        {
          "name": "Introducing commit 0e68313 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/0e68313fab9d204257ebad5c447e4cd9cea8e0ea"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "Client-chosen HPACK encoder table size is applied uncapped, growing per-connection state with every response in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eFrom 0.7.0, cap the requests one HTTP/2 connection may serve, for example with \u003ccode\u003ehttp_2_options: [max_requests: 1000]\u003c/code\u003e, so each connection and its encoder table is closed before the cost grows large. Alternatively, disable HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "From 0.7.0, cap the requests one HTTP/2 connection may serve, for example with `http_2_options: [max_requests: 1000]`, so each connection and its encoder table is closed before the cost grows large. Alternatively, disable HTTP/2 with `http_2_options: [enabled: false]`."
            }
          ],
          "value": "From 0.7.0, cap the requests one HTTP/2 connection may serve, for example with http_2_options: [max_requests: 1000], so each connection and its encoder table is closed before the cost grows large. Alternatively, disable HTTP/2 with http_2_options: [enabled: false]."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eOpen an HTTP/2 connection and advertise a \u003ccode\u003eSETTINGS_HEADER_TABLE_SIZE\u003c/code\u003e of 4294967295 in the initial SETTINGS frame.\u003c/li\u003e\n\u003cli\u003eSend cheap requests one after another on that connection, each to an endpoint whose response carries a header value that changes per request.\u003c/li\u003e\n\u003cli\u003eTime the round trips. In the reporter\u0027s run, the last 1,000 of 12,000 requests took 3.68 times as long as the first 1,000.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Open an HTTP/2 connection and advertise a `SETTINGS_HEADER_TABLE_SIZE` of 4294967295 in the initial SETTINGS frame.\n2. Send cheap requests one after another on that connection, each to an endpoint whose response carries a header value that changes per request.\n3. Time the round trips. In the reporter\u0027s run, the last 1,000 of 12,000 requests took 3.68 times as long as the first 1,000."
            }
          ],
          "value": "* Open an HTTP/2 connection and advertise a SETTINGS_HEADER_TABLE_SIZE of 4294967295 in the initial SETTINGS frame.\n* Send cheap requests one after another on that connection, each to an endpoint whose response carries a header value that changes per request.\n* Time the round trips. In the reporter\u0027s run, the last 1,000 of 12,000 requests took 3.68 times as long as the first 1,000."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Table size\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003eOn every SETTINGS frame, \u003ccode\u003eBandit.HTTP2.Connection\u003c/code\u003e passes the peer\u0027s \u003ccode\u003eSETTINGS_HEADER_TABLE_SIZE\u003c/code\u003e straight to \u003ccode\u003eHPAX.resize/2\u003c/code\u003e for the encoder table, accepting values up to 2^32-1. No server-side limit is applied.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Indexing\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eencode_headers\u003c/code\u003e encodes every response header as \u003ccode\u003e:store\u003c/code\u003e, adding it to the dynamic table. Headers whose values differ per response (\u003ccode\u003edate\u003c/code\u003e, \u003ccode\u003econtent-length\u003c/code\u003e, \u003ccode\u003eset-cookie\u003c/code\u003e, \u003ccode\u003ex-request-id\u003c/code\u003e, a reflected \u003ccode\u003elocation\u003c/code\u003e) produce new entries, and with a limit of about 4 GB none are evicted.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Cost\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eHPAX\u0027s \u003ccode\u003elookup_by_header\u003c/code\u003e scans the table linearly for each header it encodes, so every response costs more than the one before it. Serving N responses on one connection costs O(N\u00b2) CPU in the connection process, and the table holds every entry for as long as the connection stays open.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Table size** (`lib/bandit/http2/connection.ex`)\n\nOn every SETTINGS frame, `Bandit.HTTP2.Connection` passes the peer\u0027s `SETTINGS_HEADER_TABLE_SIZE` straight to `HPAX.resize/2` for the encoder table, accepting values up to 2^32-1. No server-side limit is applied.\n\n**2. Indexing**\n\n`encode_headers` encodes every response header as `:store`, adding it to the dynamic table. Headers whose values differ per response (`date`, `content-length`, `set-cookie`, `x-request-id`, a reflected `location`) produce new entries, and with a limit of about 4 GB none are evicted.\n\n**3. Cost**\n\nHPAX\u0027s `lookup_by_header` scans the table linearly for each header it encodes, so every response costs more than the one before it. Serving N responses on one connection costs O(N\u00b2) CPU in the connection process, and the table holds every entry for as long as the connection stays open."
            }
          ],
          "value": "1. Table size (lib/bandit/http2/connection.ex)\n\nOn every SETTINGS frame, Bandit.HTTP2.Connection passes the peer\u0027s SETTINGS_HEADER_TABLE_SIZE straight to HPAX.resize/2 for the encoder table, accepting values up to 2^32-1. No server-side limit is applied.\n\n2. Indexing\n\nencode_headers encodes every response header as :store, adding it to the dynamic table. Headers whose values differ per response (date, content-length, set-cookie, x-request-id, a reflected location) produce new entries, and with a limit of about 4 GB none are evicted.\n\n3. Cost\n\nHPAX\u0027s lookup_by_header scans the table linearly for each header it encodes, so every response costs more than the one before it. Serving N responses on one connection costs O(N\u00b2) CPU in the connection process, and the table holds every entry for as long as the connection stays open."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-110499",
    "datePublished": "2026-10-26T13:32:38.613Z",
    "dateReserved": "2026-10-22T09:30:02.077Z",
    "dateUpdated": "2026-10-26T15:35:50.929Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-109584 (GCVE-0-2026-109584)
Vulnerability from cvelistv5
Published
2026-10-26 13:32
Modified
2026-10-26 15:35
CWE
  • CWE-409 - Improper Handling of Highly Compressed Data (Data Amplification)
Summary
Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a small HPACK-compressed HEADERS block. The block stores one large cookie value in the HPACK dynamic table and then references it tens of thousands of times with 1-byte indexed fields. In the reporter's run, a 49 KB block made a server with default settings allocate a 183 MB cookie binary before any application code ran, and every concurrent stream can repeat the cost. Bandit.HTTP2.Connection caps only the compressed block (max_header_block_size), and its check of the decoded list against max_header_list_size never fires because that setting defaults to :infinity. combine_cookie_crumbs/1 in lib/bandit/http2/stream.ex then joins every crumb into one binary. This issue affects bandit: from 0.3.3 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.3.3   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 44bf969cd59851622fb6ee4d16227fbc39a77fb4
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-109584",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:11:12.403348Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:51.081Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "0.3.3",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "44bf969cd59851622fb6ee4d16227fbc39a77fb4",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "0.3.3",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T10:29:49.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eImproper Handling of Highly Compressed Data (Data Amplification) vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a small HPACK-compressed HEADERS block. The block stores one large \u003ccode\u003ecookie\u003c/code\u003e value in the HPACK dynamic table and then references it tens of thousands of times with 1-byte indexed fields. In the reporter\u0027s run, a 49 KB block made a server with default settings allocate a 183 MB cookie binary before any application code ran, and every concurrent stream can repeat the cost.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eBandit.HTTP2.Connection\u003c/code\u003e caps only the compressed block (\u003ccode\u003emax_header_block_size\u003c/code\u003e), and its check of the decoded list against \u003ccode\u003emax_header_list_size\u003c/code\u003e never fires because that setting defaults to \u003ccode\u003e:infinity\u003c/code\u003e. \u003ccode\u003ecombine_cookie_crumbs/1\u003c/code\u003e in \u003ccode\u003elib/bandit/http2/stream.ex\u003c/code\u003e then joins every crumb into one binary.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.3.3 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a small HPACK-compressed HEADERS block. The block stores one large `cookie` value in the HPACK dynamic table and then references it tens of thousands of times with 1-byte indexed fields. In the reporter\u0027s run, a 49 KB block made a server with default settings allocate a 183 MB cookie binary before any application code ran, and every concurrent stream can repeat the cost.\n\n`Bandit.HTTP2.Connection` caps only the compressed block (`max_header_block_size`), and its check of the decoded list against `max_header_list_size` never fires because that setting defaults to `:infinity`. `combine_cookie_crumbs/1` in `lib/bandit/http2/stream.ex` then joins every crumb into one binary.\n\nThis issue affects bandit: from 0.3.3 before 1.14.0."
            }
          ],
          "value": "Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a small HPACK-compressed HEADERS block. The block stores one large cookie value in the HPACK dynamic table and then references it tens of thousands of times with 1-byte indexed fields. In the reporter\u0027s run, a 49 KB block made a server with default settings allocate a 183 MB cookie binary before any application code ran, and every concurrent stream can repeat the cost.\n\nBandit.HTTP2.Connection caps only the compressed block (max_header_block_size), and its check of the decoded list against max_header_list_size never fires because that setting defaults to :infinity. combine_cookie_crumbs/1 in lib/bandit/http2/stream.ex then joins every crumb into one binary.\n\nThis issue affects bandit: from 0.3.3 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-491",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated remote client can make any Bandit server with default HTTP/2 settings allocate a cookie binary thousands of times larger than the header block it sent, before any application code runs. Repeating it across concurrent streams exhausts the BEAM node\u0027s memory, taking down every application on the node.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated remote client can make any Bandit server with default HTTP/2 settings allocate a cookie binary thousands of times larger than the header block it sent, before any application code runs. Repeating it across concurrent streams exhausts the BEAM node\u0027s memory, taking down every application on the node."
                }
              ],
              "value": "An unauthenticated remote client can make any Bandit server with default HTTP/2 settings allocate a cookie binary thousands of times larger than the header block it sent, before any application code runs. Repeating it across concurrent streams exhausts the BEAM node\u0027s memory, taking down every application on the node."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-409",
              "description": "CWE-409 Improper Handling of Highly Compressed Data (Data Amplification)",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:32:22.359Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "HTTP/2 cookie crumb join expands a small HPACK header block into an unbounded allocation in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-7gfj-7gvq-3xqj"
        },
        {
          "name": "EEF CNA record for CVE-2026-109584",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-109584.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-109584",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-109584"
        },
        {
          "name": "Introducing commit 44bf969 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/44bf969cd59851622fb6ee4d16227fbc39a77fb4"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "HTTP/2 cookie crumb join expands a small HPACK header block into an unbounded allocation in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eOn 1.12.2 and later, set a finite decoded header list limit, for example \u003ccode\u003ehttp_2_options: [default_local_settings: [max_header_list_size: 65_536]]\u003c/code\u003e, which resets the stream before the cookie crumbs are joined. From 0.7.0, disabling HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e removes the vulnerable path.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "On 1.12.2 and later, set a finite decoded header list limit, for example `http_2_options: [default_local_settings: [max_header_list_size: 65_536]]`, which resets the stream before the cookie crumbs are joined. From 0.7.0, disabling HTTP/2 with `http_2_options: [enabled: false]` removes the vulnerable path."
            }
          ],
          "value": "On 1.12.2 and later, set a finite decoded header list limit, for example http_2_options: [default_local_settings: [max_header_list_size: 65_536]], which resets the stream before the cookie crumbs are joined. From 0.7.0, disabling HTTP/2 with http_2_options: [enabled: false] removes the vulnerable path."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eAgainst a Bandit server with default HTTP/2 settings, build an HPACK header block with the request pseudo-headers plus a 4,058-byte \u003ccode\u003ecookie\u003c/code\u003e stored in the dynamic table and then referenced about 45,000 times with indexed representations. The block is about 49 KB.\u003c/li\u003e\n\u003cli\u003eSend it on one stream as a HEADERS frame followed by CONTINUATION frames of at most 16,384 bytes, with \u003ccode\u003eEND_HEADERS\u003c/code\u003e on the last.\u003c/li\u003e\n\u003cli\u003eThe request reaches the plug with a \u003ccode\u003ecookie\u003c/code\u003e header of about 183 MB, and VM memory grows by about the same amount. Repeat on further streams to multiply it.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Against a Bandit server with default HTTP/2 settings, build an HPACK header block with the request pseudo-headers plus a 4,058-byte `cookie` stored in the dynamic table and then referenced about 45,000 times with indexed representations. The block is about 49 KB.\n2. Send it on one stream as a HEADERS frame followed by CONTINUATION frames of at most 16,384 bytes, with `END_HEADERS` on the last.\n3. The request reaches the plug with a `cookie` header of about 183 MB, and VM memory grows by about the same amount. Repeat on further streams to multiply it."
            }
          ],
          "value": "* Against a Bandit server with default HTTP/2 settings, build an HPACK header block with the request pseudo-headers plus a 4,058-byte cookie stored in the dynamic table and then referenced about 45,000 times with indexed representations. The block is about 49 KB.\n* Send it on one stream as a HEADERS frame followed by CONTINUATION frames of at most 16,384 bytes, with END_HEADERS on the last.\n* The request reaches the plug with a cookie header of about 183 MB, and VM memory grows by about the same amount. Repeat on further streams to multiply it."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Compression\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe attacker\u0027s header block, split across HEADERS and CONTINUATION frames, stays under the 50,000-byte \u003ccode\u003emax_header_block_size\u003c/code\u003e. It inserts one 4,058-byte \u003ccode\u003ecookie\u003c/code\u003e into the 4,096-byte dynamic table, the largest value that fits, then references it about 45,000 times with 1-byte indexed representations. \u003ccode\u003eHPAX.decode\u003c/code\u003e returns a list whose entries all point at the same binary, so decoding itself stays cheap.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Missing decoded limit\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e, \u003ccode\u003elib/bandit/http2/settings.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003eAfter decoding, the connection compares \u003ccode\u003eheader_list_size(headers)\u003c/code\u003e against \u003ccode\u003elocal_settings.max_header_list_size\u003c/code\u003e. That setting defaults to \u003ccode\u003e:infinity\u003c/code\u003e, and in Erlang term order every integer sorts below an atom, so the comparison never rejects a list. The limit is enforced only when an operator sets it.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Expansion\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/stream.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eread_headers\u003c/code\u003e calls \u003ccode\u003ecombine_cookie_crumbs/1\u003c/code\u003e, which joins every crumb with \u003ccode\u003eEnum.map_join/3\u003c/code\u003e into a single binary of about 183 MB before \u003ccode\u003ePlug.call/2\u003c/code\u003e runs. \u003ccode\u003emax_concurrent_streams\u003c/code\u003e also defaults to \u003ccode\u003e:infinity\u003c/code\u003e, so N streams allocate N times as much.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Compression**\n\nThe attacker\u0027s header block, split across HEADERS and CONTINUATION frames, stays under the 50,000-byte `max_header_block_size`. It inserts one 4,058-byte `cookie` into the 4,096-byte dynamic table, the largest value that fits, then references it about 45,000 times with 1-byte indexed representations. `HPAX.decode` returns a list whose entries all point at the same binary, so decoding itself stays cheap.\n\n**2. Missing decoded limit** (`lib/bandit/http2/connection.ex`, `lib/bandit/http2/settings.ex`)\n\nAfter decoding, the connection compares `header_list_size(headers)` against `local_settings.max_header_list_size`. That setting defaults to `:infinity`, and in Erlang term order every integer sorts below an atom, so the comparison never rejects a list. The limit is enforced only when an operator sets it.\n\n**3. Expansion** (`lib/bandit/http2/stream.ex`)\n\n`read_headers` calls `combine_cookie_crumbs/1`, which joins every crumb with `Enum.map_join/3` into a single binary of about 183 MB before `Plug.call/2` runs. `max_concurrent_streams` also defaults to `:infinity`, so N streams allocate N times as much."
            }
          ],
          "value": "1. Compression\n\nThe attacker\u0027s header block, split across HEADERS and CONTINUATION frames, stays under the 50,000-byte max_header_block_size. It inserts one 4,058-byte cookie into the 4,096-byte dynamic table, the largest value that fits, then references it about 45,000 times with 1-byte indexed representations. HPAX.decode returns a list whose entries all point at the same binary, so decoding itself stays cheap.\n\n2. Missing decoded limit (lib/bandit/http2/connection.ex, lib/bandit/http2/settings.ex)\n\nAfter decoding, the connection compares header_list_size(headers) against local_settings.max_header_list_size. That setting defaults to :infinity, and in Erlang term order every integer sorts below an atom, so the comparison never rejects a list. The limit is enforced only when an operator sets it.\n\n3. Expansion (lib/bandit/http2/stream.ex)\n\nread_headers calls combine_cookie_crumbs/1, which joins every crumb with Enum.map_join/3 into a single binary of about 183 MB before Plug.call/2 runs. max_concurrent_streams also defaults to :infinity, so N streams allocate N times as much."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-109584",
    "datePublished": "2026-10-26T13:32:22.359Z",
    "dateReserved": "2026-10-22T10:15:01.793Z",
    "dateUpdated": "2026-10-26T15:35:51.081Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-111103 (GCVE-0-2026-111103)
Vulnerability from cvelistv5
Published
2026-10-26 13:32
Modified
2026-10-26 15:35
CWE
  • CWE-460 - Improper Cleanup on Thrown Exception
Summary
Improper Cleanup on Thrown Exception vulnerability in the HTTP/2 handler of mtrudel bandit allows an unauthenticated remote attacker to bypass per-connection resource limits via a single TCP write that carries several frames, one of which raises a stream error. When a frame raises StreamError partway through a chunk, Bandit.HTTP2.Handler.handle_data/3 sends RST_STREAM and continues with the connection state from before the chunk, dropping the rest of it. Streams opened by earlier frames in the chunk keep running their Plug calls, but the stream counts, last_stream_id, the rapid-reset timestamps, the HPACK decoder state and the buffer roll back, so max_concurrent_streams, max_requests and the rapid-reset limiter never see those streams. A client that sends one HEADERS frame more than max_concurrent_streams in each write has the extra refused and the others forgotten: with a limit of 10, five such writes run 50 Plug calls at once. This issue affects bandit: from 1.9.0 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.9.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 3ad49c80c268891e7cc412e69649115456638e59
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-111103",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:11:27.499001Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:51.229Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "1.9.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "3ad49c80c268891e7cc412e69649115456638e59",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "1.9.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T12:27:58.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eImproper Cleanup on Thrown Exception vulnerability in the HTTP/2 handler of mtrudel bandit allows an unauthenticated remote attacker to bypass per-connection resource limits via a single TCP write that carries several frames, one of which raises a stream error.\u003c/p\u003e\n\u003cp\u003eWhen a frame raises \u003ccode\u003eStreamError\u003c/code\u003e partway through a chunk, \u003ccode\u003eBandit.HTTP2.Handler.handle_data/3\u003c/code\u003e sends RST_STREAM and continues with the connection state from before the chunk, dropping the rest of it. Streams opened by earlier frames in the chunk keep running their Plug calls, but the stream counts, \u003ccode\u003elast_stream_id\u003c/code\u003e, the rapid-reset timestamps, the HPACK decoder state and the buffer roll back, so \u003ccode\u003emax_concurrent_streams\u003c/code\u003e, \u003ccode\u003emax_requests\u003c/code\u003e and the rapid-reset limiter never see those streams. A client that sends one HEADERS frame more than \u003ccode\u003emax_concurrent_streams\u003c/code\u003e in each write has the extra refused and the others forgotten: with a limit of 10, five such writes run 50 Plug calls at once.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 1.9.0 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Improper Cleanup on Thrown Exception vulnerability in the HTTP/2 handler of mtrudel bandit allows an unauthenticated remote attacker to bypass per-connection resource limits via a single TCP write that carries several frames, one of which raises a stream error.\n\nWhen a frame raises `StreamError` partway through a chunk, `Bandit.HTTP2.Handler.handle_data/3` sends RST_STREAM and continues with the connection state from before the chunk, dropping the rest of it. Streams opened by earlier frames in the chunk keep running their Plug calls, but the stream counts, `last_stream_id`, the rapid-reset timestamps, the HPACK decoder state and the buffer roll back, so `max_concurrent_streams`, `max_requests` and the rapid-reset limiter never see those streams. A client that sends one HEADERS frame more than `max_concurrent_streams` in each write has the extra refused and the others forgotten: with a limit of 10, five such writes run 50 Plug calls at once.\n\nThis issue affects bandit: from 1.9.0 before 1.14.0."
            }
          ],
          "value": "Improper Cleanup on Thrown Exception vulnerability in the HTTP/2 handler of mtrudel bandit allows an unauthenticated remote attacker to bypass per-connection resource limits via a single TCP write that carries several frames, one of which raises a stream error.\n\nWhen a frame raises StreamError partway through a chunk, Bandit.HTTP2.Handler.handle_data/3 sends RST_STREAM and continues with the connection state from before the chunk, dropping the rest of it. Streams opened by earlier frames in the chunk keep running their Plug calls, but the stream counts, last_stream_id, the rapid-reset timestamps, the HPACK decoder state and the buffer roll back, so max_concurrent_streams, max_requests and the rapid-reset limiter never see those streams. A client that sends one HEADERS frame more than max_concurrent_streams in each write has the extra refused and the others forgotten: with a limit of 10, five such writes run 50 Plug calls at once.\n\nThis issue affects bandit: from 1.9.0 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated client can run more concurrent Plug calls on one HTTP/2 connection than the configured \u003ccode\u003emax_concurrent_streams\u003c/code\u003e allows (five times the limit in the reporter\u0027s test), tying up the server processes, CPU and memory the limit is meant to cap. Servers that serve HTTP/2 with a finite \u003ccode\u003emax_concurrent_streams\u003c/code\u003e are affected.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated client can run more concurrent Plug calls on one HTTP/2 connection than the configured `max_concurrent_streams` allows (five times the limit in the reporter\u0027s test), tying up the server processes, CPU and memory the limit is meant to cap. Servers that serve HTTP/2 with a finite `max_concurrent_streams` are affected."
                }
              ],
              "value": "An unauthenticated client can run more concurrent Plug calls on one HTTP/2 connection than the configured max_concurrent_streams allows (five times the limit in the reporter\u0027s test), tying up the server processes, CPU and memory the limit is meant to cap. Servers that serve HTTP/2 with a finite max_concurrent_streams are affected."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "PRESENT",
            "attackVector": "NETWORK",
            "baseScore": 8.2,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-460",
              "description": "CWE-460 Improper Cleanup on Thrown Exception",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:32:06.340Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "HTTP/2 StreamError recovery rolls back connection state and bypasses per-connection limits in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-2238-pgfc-w82m"
        },
        {
          "name": "EEF CNA record for CVE-2026-111103",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-111103.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-111103",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-111103"
        },
        {
          "name": "Introducing commit 3ad49c8 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/3ad49c80c268891e7cc412e69649115456638e59"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "HTTP/2 StreamError recovery rolls back connection state and bypasses per-connection limits in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eDisable HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e, so that the vulnerable HTTP/2 handler never runs.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Disable HTTP/2 with `http_2_options: [enabled: false]`, so that the vulnerable HTTP/2 handler never runs."
            }
          ],
          "value": "Disable HTTP/2 with http_2_options: [enabled: false], so that the vulnerable HTTP/2 handler never runs."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eRun Bandit with a Plug that blocks until released and with \u003ccode\u003ehttp_2_options: [default_local_settings: [max_concurrent_streams: 10]]\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eOpen an HTTP/2 connection with prior knowledge and complete the SETTINGS exchange.\u003c/li\u003e\n\u003cli\u003eIn one TCP write, send 11 HEADERS frames with END_HEADERS and END_STREAM on streams 1, 3, ..., 21, each carrying a header block encoded without indexing. The eleventh is refused with REFUSED_STREAM, and the server forgets the ten streams before it.\u003c/li\u003e\n\u003cli\u003eSend the same write four more times. The Plug runs 50 times concurrently against a limit of 10.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Run Bandit with a Plug that blocks until released and with `http_2_options: [default_local_settings: [max_concurrent_streams: 10]]`.\n2. Open an HTTP/2 connection with prior knowledge and complete the SETTINGS exchange.\n3. In one TCP write, send 11 HEADERS frames with END_HEADERS and END_STREAM on streams 1, 3, ..., 21, each carrying a header block encoded without indexing. The eleventh is refused with REFUSED_STREAM, and the server forgets the ten streams before it.\n4. Send the same write four more times. The Plug runs 50 times concurrently against a limit of 10."
            }
          ],
          "value": "* Run Bandit with a Plug that blocks until released and with http_2_options: [default_local_settings: [max_concurrent_streams: 10]].\n* Open an HTTP/2 connection with prior knowledge and complete the SETTINGS exchange.\n* In one TCP write, send 11 HEADERS frames with END_HEADERS and END_STREAM on streams 1, 3, ..., 21, each carrying a header block encoded without indexing. The eleventh is refused with REFUSED_STREAM, and the server forgets the ten streams before it.\n* Send the same write four more times. The Plug runs 50 times concurrently against a limit of 10."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Function-level rescue.\u003c/strong\u003e \u003ccode\u003eBandit.HTTP2.Handler.handle_data/3\u003c/code\u003e in \u003ccode\u003elib/bandit/http2/handler.ex\u003c/code\u003e deserializes every frame in a TCP chunk inside an \u003ccode\u003eEnum.reduce_while/3\u003c/code\u003e and wraps the whole function in a \u003ccode\u003erescue\u003c/code\u003e. Inside that \u003ccode\u003erescue\u003c/code\u003e, \u003ccode\u003estate\u003c/code\u003e is the original function argument, not the \u003ccode\u003ereduce_while\u003c/code\u003e accumulator.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Rollback on StreamError.\u003c/strong\u003e When \u003ccode\u003eBandit.HTTP2.Connection.handle_frame/3\u003c/code\u003e raises \u003ccode\u003eStreamError\u003c/code\u003e partway through the chunk, \u003ccode\u003erescue_stream_error/3\u003c/code\u003e sends RST_STREAM and returns \u003ccode\u003e{:continue, state}\u003c/code\u003e with the connection as it was before the chunk. The rest of the chunk is dropped.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Lost bookkeeping.\u003c/strong\u003e Frames already handled keep their side effects: their stream processes are running the Plug. But the stream counts, \u003ccode\u003elast_stream_id\u003c/code\u003e, \u003ccode\u003ereset_stream_timestamps\u003c/code\u003e, \u003ccode\u003erecv_hpack_state\u003c/code\u003e and \u003ccode\u003ebuffer\u003c/code\u003e are rolled back, so \u003ccode\u003emax_concurrent_streams\u003c/code\u003e, \u003ccode\u003emax_requests\u003c/code\u003e and the rapid-reset limiter no longer count those streams. The same rollback drops HPACK table updates, which can mix up headers between requests, misaligns frame parsing, and sends a stale \u003ccode\u003elast_stream_id\u003c/code\u003e in GOAWAY.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Function-level rescue.** `Bandit.HTTP2.Handler.handle_data/3` in `lib/bandit/http2/handler.ex` deserializes every frame in a TCP chunk inside an `Enum.reduce_while/3` and wraps the whole function in a `rescue`. Inside that `rescue`, `state` is the original function argument, not the `reduce_while` accumulator.\n\n**2. Rollback on StreamError.** When `Bandit.HTTP2.Connection.handle_frame/3` raises `StreamError` partway through the chunk, `rescue_stream_error/3` sends RST_STREAM and returns `{:continue, state}` with the connection as it was before the chunk. The rest of the chunk is dropped.\n\n**3. Lost bookkeeping.** Frames already handled keep their side effects: their stream processes are running the Plug. But the stream counts, `last_stream_id`, `reset_stream_timestamps`, `recv_hpack_state` and `buffer` are rolled back, so `max_concurrent_streams`, `max_requests` and the rapid-reset limiter no longer count those streams. The same rollback drops HPACK table updates, which can mix up headers between requests, misaligns frame parsing, and sends a stale `last_stream_id` in GOAWAY."
            }
          ],
          "value": "1. Function-level rescue. Bandit.HTTP2.Handler.handle_data/3 in lib/bandit/http2/handler.ex deserializes every frame in a TCP chunk inside an Enum.reduce_while/3 and wraps the whole function in a rescue. Inside that rescue, state is the original function argument, not the reduce_while accumulator.\n\n2. Rollback on StreamError. When Bandit.HTTP2.Connection.handle_frame/3 raises StreamError partway through the chunk, rescue_stream_error/3 sends RST_STREAM and returns {:continue, state} with the connection as it was before the chunk. The rest of the chunk is dropped.\n\n3. Lost bookkeeping. Frames already handled keep their side effects: their stream processes are running the Plug. But the stream counts, last_stream_id, reset_stream_timestamps, recv_hpack_state and buffer are rolled back, so max_concurrent_streams, max_requests and the rapid-reset limiter no longer count those streams. The same rollback drops HPACK table updates, which can mix up headers between requests, misaligns frame parsing, and sends a stale last_stream_id in GOAWAY."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-111103",
    "datePublished": "2026-10-26T13:32:06.340Z",
    "dateReserved": "2026-10-23T10:30:01.888Z",
    "dateUpdated": "2026-10-26T15:35:51.229Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-111101 (GCVE-0-2026-111101)
Vulnerability from cvelistv5
Published
2026-10-26 13:31
Modified
2026-10-26 15:35
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory and CPU via a request body made of many 1-byte chunks. The chunked reader in lib/bandit/http1/socket.ex keeps every chunk as its own list cell and binary term until IO.iodata_to_binary/1 joins them, and nothing limits the number of chunks. The :length option of read_body/2 counts only data bytes, so tiny chunks cost far more memory than their payload: 48 MB of 1-byte chunks grew one handler process to about 500 MB and took about 6 s of CPU. The route does not need to read the body, because on keep-alive connections ensure_completed/1 drains unread bodies through the same reader. This issue affects bandit: from 0.1.0 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.1.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 7201270c34b90f6c9e15c0386c2bb802c0c72ab4
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-111101",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:10:33.224480Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:51.401Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "0.1.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "7201270c34b90f6c9e15c0386c2bb802c0c72ab4",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "0.1.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T12:35:54.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAllocation of Resources Without Limits or Throttling vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory and CPU via a request body made of many 1-byte chunks.\u003c/p\u003e\n\u003cp\u003eThe chunked reader in \u003ccode\u003elib/bandit/http1/socket.ex\u003c/code\u003e keeps every chunk as its own list cell and binary term until \u003ccode\u003eIO.iodata_to_binary/1\u003c/code\u003e joins them, and nothing limits the number of chunks. The \u003ccode\u003e:length\u003c/code\u003e option of \u003ccode\u003eread_body/2\u003c/code\u003e counts only data bytes, so tiny chunks cost far more memory than their payload: 48 MB of 1-byte chunks grew one handler process to about 500 MB and took about 6 s of CPU. The route does not need to read the body, because on keep-alive connections \u003ccode\u003eensure_completed/1\u003c/code\u003e drains unread bodies through the same reader.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.1.0 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory and CPU via a request body made of many 1-byte chunks.\n\nThe chunked reader in `lib/bandit/http1/socket.ex` keeps every chunk as its own list cell and binary term until `IO.iodata_to_binary/1` joins them, and nothing limits the number of chunks. The `:length` option of `read_body/2` counts only data bytes, so tiny chunks cost far more memory than their payload: 48 MB of 1-byte chunks grew one handler process to about 500 MB and took about 6 s of CPU. The route does not need to read the body, because on keep-alive connections `ensure_completed/1` drains unread bodies through the same reader.\n\nThis issue affects bandit: from 0.1.0 before 1.14.0."
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory and CPU via a request body made of many 1-byte chunks.\n\nThe chunked reader in lib/bandit/http1/socket.ex keeps every chunk as its own list cell and binary term until IO.iodata_to_binary/1 joins them, and nothing limits the number of chunks. The :length option of read_body/2 counts only data bytes, so tiny chunks cost far more memory than their payload: 48 MB of 1-byte chunks grew one handler process to about 500 MB and took about 6 s of CPU. The route does not need to read the body, because on keep-alive connections ensure_completed/1 drains unread bodies through the same reader.\n\nThis issue affects bandit: from 0.1.0 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated client can make a single HTTP/1.1 request hold many times its body size in server memory (about 48 MB for a 1 MB body in the reporter\u0027s test), and keep CPU busy while the chunks are joined. This affects any Bandit server that accepts HTTP/1.1 requests, whether or not its routes read the body.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated client can make a single HTTP/1.1 request hold many times its body size in server memory (about 48 MB for a 1 MB body in the reporter\u0027s test), and keep CPU busy while the chunks are joined. This affects any Bandit server that accepts HTTP/1.1 requests, whether or not its routes read the body."
                }
              ],
              "value": "An unauthenticated client can make a single HTTP/1.1 request hold many times its body size in server memory (about 48 MB for a 1 MB body in the reporter\u0027s test), and keep CPU busy while the chunks are joined. This affects any Bandit server that accepts HTTP/1.1 requests, whether or not its routes read the body."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:31:48.184Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "HTTP/1.1 chunked request bodies made of 1-byte chunks amplify memory use in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-jwh3-j29h-qfmw"
        },
        {
          "name": "EEF CNA record for CVE-2026-111101",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-111101.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-111101",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-111101"
        },
        {
          "name": "Introducing commit 7201270 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/7201270c34b90f6c9e15c0386c2bb802c0c72ab4"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "HTTP/1.1 chunked request bodies made of 1-byte chunks amplify memory use in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eFrom 0.7.0, a server whose clients and proxies all speak HTTP/2 can disable HTTP/1 with \u003ccode\u003ehttp_1_options: [enabled: false]\u003c/code\u003e, so that the vulnerable chunked body reader never runs.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "From 0.7.0, a server whose clients and proxies all speak HTTP/2 can disable HTTP/1 with `http_1_options: [enabled: false]`, so that the vulnerable chunked body reader never runs."
            }
          ],
          "value": "From 0.7.0, a server whose clients and proxies all speak HTTP/2 can disable HTTP/1 with http_1_options: [enabled: false], so that the vulnerable chunked body reader never runs."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eRun Bandit with a Plug that calls \u003ccode\u003ePlug.Conn.read_body(conn, length: 1_000_000, read_length: 1_000_000)\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSend \u003ccode\u003ePOST /\u003c/code\u003e with \u003ccode\u003eTransfer-Encoding: chunked\u003c/code\u003e and a body of 1,000,000 chunks of \u003ccode\u003e1\\r\\nA\\r\\n\u003c/code\u003e, followed by \u003ccode\u003e0\\r\\n\\r\\n\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eWhen \u003ccode\u003eread_body\u003c/code\u003e returns the 1,000,000 body bytes, the handler process holds about 48 MB and BEAM memory has grown by about 55 MB, for 6 MB sent.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Run Bandit with a Plug that calls `Plug.Conn.read_body(conn, length: 1_000_000, read_length: 1_000_000)`.\n2. Send `POST /` with `Transfer-Encoding: chunked` and a body of 1,000,000 chunks of `1\\r\\nA\\r\\n`, followed by `0\\r\\n\\r\\n`.\n3. When `read_body` returns the 1,000,000 body bytes, the handler process holds about 48 MB and BEAM memory has grown by about 55 MB, for 6 MB sent."
            }
          ],
          "value": "* Run Bandit with a Plug that calls Plug.Conn.read_body(conn, length: 1_000_000, read_length: 1_000_000).\n* Send POST / with Transfer-Encoding: chunked and a body of 1,000,000 chunks of 1\\r\\nA\\r\\n, followed by 0\\r\\n\\r\\n.\n* When read_body returns the 1,000,000 body bytes, the handler process holds about 48 MB and BEAM memory has grown by about 55 MB, for 6 MB sent."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Per-chunk accumulation.\u003c/strong\u003e \u003ccode\u003edo_read_chunked_data!\u003c/code\u003e in \u003ccode\u003elib/bandit/http1/socket.ex\u003c/code\u003e collects chunks as \u003ccode\u003e[body | chunk]\u003c/code\u003e, adding a cons cell and a binary term for every chunk.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Byte-only limit.\u003c/strong\u003e \u003ccode\u003e:length\u003c/code\u003e limits data bytes, not the number of chunks, so a body of \u003ccode\u003e1\\r\\nx\\r\\n\u003c/code\u003e chunks becomes an 8-million-element improper list before \u003ccode\u003eIO.iodata_to_binary\u003c/code\u003e runs. Sending 48 MB of 1-byte chunks grew the handler heap to about 500 MB and used about 6 s of CPU. The same body as one 8 MB chunk peaked at about 16 MB and took 5 ms.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. No body read needed.\u003c/strong\u003e On keep-alive connections, \u003ccode\u003eensure_completed/1\u003c/code\u003e drains unread bodies through the same path. Against a Plug that never calls \u003ccode\u003eread_body\u003c/code\u003e, the attack used about 417 MB.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Per-chunk accumulation.** `do_read_chunked_data!` in `lib/bandit/http1/socket.ex` collects chunks as `[body | chunk]`, adding a cons cell and a binary term for every chunk.\n\n**2. Byte-only limit.** `:length` limits data bytes, not the number of chunks, so a body of `1\\r\\nx\\r\\n` chunks becomes an 8-million-element improper list before `IO.iodata_to_binary` runs. Sending 48 MB of 1-byte chunks grew the handler heap to about 500 MB and used about 6 s of CPU. The same body as one 8 MB chunk peaked at about 16 MB and took 5 ms.\n\n**3. No body read needed.** On keep-alive connections, `ensure_completed/1` drains unread bodies through the same path. Against a Plug that never calls `read_body`, the attack used about 417 MB."
            }
          ],
          "value": "1. Per-chunk accumulation. do_read_chunked_data! in lib/bandit/http1/socket.ex collects chunks as [body | chunk], adding a cons cell and a binary term for every chunk.\n\n2. Byte-only limit. :length limits data bytes, not the number of chunks, so a body of 1\\r\\nx\\r\\n chunks becomes an 8-million-element improper list before IO.iodata_to_binary runs. Sending 48 MB of 1-byte chunks grew the handler heap to about 500 MB and used about 6 s of CPU. The same body as one 8 MB chunk peaked at about 16 MB and took 5 ms.\n\n3. No body read needed. On keep-alive connections, ensure_completed/1 drains unread bodies through the same path. Against a Plug that never calls read_body, the attack used about 417 MB."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-111101",
    "datePublished": "2026-10-26T13:31:48.184Z",
    "dateReserved": "2026-10-23T12:30:01.834Z",
    "dateUpdated": "2026-10-26T15:35:51.401Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-113436 (GCVE-0-2026-113436)
Vulnerability from cvelistv5
Published
2026-10-26 13:31
Modified
2026-10-26 15:35
CWE
  • CWE-407 - Inefficient Algorithmic Complexity
Summary
Inefficient Algorithmic Complexity vulnerability in the header integer parser of mtrudel bandit allows an unauthenticated remote attacker to consume server CPU via a content-length value or a Host or :authority port made of thousands of digits. Bandit.Headers.parse_integer/2 in lib/bandit/headers.ex builds the number one digit at a time with no limit on the digit count, allocating a new bignum at each step, so one value costs time quadratic in its length. It runs on raw request input over HTTP/1 and HTTP/2, before any range check. Over HTTP/2, a 4,000-digit value can be stored once in the HPACK dynamic table and re-sent on each new stream for about one byte: 124 KB of traffic kept the server busy for 27.9 s. This issue affects bandit: from 1.0.0-pre.7 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.0.0-pre.7   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: e0c8c0c30af9b0f57d8aafac37ddd9294363a8be
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-113436",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:12:04.626946Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:51.556Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "1.0.0-pre.7",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "e0c8c0c30af9b0f57d8aafac37ddd9294363a8be",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "1.0.0-pre.7",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T12:44:41.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eInefficient Algorithmic Complexity vulnerability in the header integer parser of mtrudel bandit allows an unauthenticated remote attacker to consume server CPU via a \u003ccode\u003econtent-length\u003c/code\u003e value or a \u003ccode\u003eHost\u003c/code\u003e or \u003ccode\u003e:authority\u003c/code\u003e port made of thousands of digits.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eBandit.Headers.parse_integer/2\u003c/code\u003e in \u003ccode\u003elib/bandit/headers.ex\u003c/code\u003e builds the number one digit at a time with no limit on the digit count, allocating a new bignum at each step, so one value costs time quadratic in its length. It runs on raw request input over HTTP/1 and HTTP/2, before any range check. Over HTTP/2, a 4,000-digit value can be stored once in the HPACK dynamic table and re-sent on each new stream for about one byte: 124 KB of traffic kept the server busy for 27.9 s.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 1.0.0-pre.7 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Inefficient Algorithmic Complexity vulnerability in the header integer parser of mtrudel bandit allows an unauthenticated remote attacker to consume server CPU via a `content-length` value or a `Host` or `:authority` port made of thousands of digits.\n\n`Bandit.Headers.parse_integer/2` in `lib/bandit/headers.ex` builds the number one digit at a time with no limit on the digit count, allocating a new bignum at each step, so one value costs time quadratic in its length. It runs on raw request input over HTTP/1 and HTTP/2, before any range check. Over HTTP/2, a 4,000-digit value can be stored once in the HPACK dynamic table and re-sent on each new stream for about one byte: 124 KB of traffic kept the server busy for 27.9 s.\n\nThis issue affects bandit: from 1.0.0-pre.7 before 1.14.0."
            }
          ],
          "value": "Inefficient Algorithmic Complexity vulnerability in the header integer parser of mtrudel bandit allows an unauthenticated remote attacker to consume server CPU via a content-length value or a Host or :authority port made of thousands of digits.\n\nBandit.Headers.parse_integer/2 in lib/bandit/headers.ex builds the number one digit at a time with no limit on the digit count, allocating a new bignum at each step, so one value costs time quadratic in its length. It runs on raw request input over HTTP/1 and HTTP/2, before any range check. Over HTTP/2, a 4,000-digit value can be stored once in the HPACK dynamic table and re-sent on each new stream for about one byte: 124 KB of traffic kept the server busy for 27.9 s.\n\nThis issue affects bandit: from 1.0.0-pre.7 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated client can make the server spend CPU time out of all proportion to the traffic it sends (27.9 s for 124 KB in the reporter\u0027s test). This affects any Bandit server reachable by untrusted clients over HTTP/1 or HTTP/2, with no special configuration.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated client can make the server spend CPU time out of all proportion to the traffic it sends (27.9 s for 124 KB in the reporter\u0027s test). This affects any Bandit server reachable by untrusted clients over HTTP/1 or HTTP/2, with no special configuration."
                }
              ],
              "value": "An unauthenticated client can make the server spend CPU time out of all proportion to the traffic it sends (27.9 s for 124 KB in the reporter\u0027s test). This affects any Bandit server reachable by untrusted clients over HTTP/1 or HTTP/2, with no special configuration."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-407",
              "description": "CWE-407 Inefficient Algorithmic Complexity",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:31:35.033Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "Quadratic bignum parsing of Content-Length and Host port values in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-29cr-8477-rg6r"
        },
        {
          "name": "EEF CNA record for CVE-2026-113436",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-113436.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-113436",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-113436"
        },
        {
          "name": "Introducing commit e0c8c0c in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/e0c8c0c30af9b0f57d8aafac37ddd9294363a8be"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "Quadratic bignum parsing of Content-Length and Host port values in Bandit",
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eRun Bandit with any Plug.\u003c/li\u003e\n\u003cli\u003eOpen an HTTP/2 connection with prior knowledge and complete the SETTINGS exchange.\u003c/li\u003e\n\u003cli\u003eIn one write, send 5,000 HEADERS frames for \u003ccode\u003ePOST /\u003c/code\u003e on new streams, each carrying a \u003ccode\u003econtent-length\u003c/code\u003e of 4,000 \u003ccode\u003e9\u003c/code\u003e digits encoded with HPACK indexing, so that every frame after the first refers to the stored value.\u003c/li\u003e\n\u003cli\u003eThe 124,003 bytes take 27.9 s to process before all 5,000 requests reach the Plug.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Run Bandit with any Plug.\n2. Open an HTTP/2 connection with prior knowledge and complete the SETTINGS exchange.\n3. In one write, send 5,000 HEADERS frames for `POST /` on new streams, each carrying a `content-length` of 4,000 `9` digits encoded with HPACK indexing, so that every frame after the first refers to the stored value.\n4. The 124,003 bytes take 27.9 s to process before all 5,000 requests reach the Plug."
            }
          ],
          "value": "* Run Bandit with any Plug.\n* Open an HTTP/2 connection with prior knowledge and complete the SETTINGS exchange.\n* In one write, send 5,000 HEADERS frames for POST / on new streams, each carrying a content-length of 4,000 9 digits encoded with HPACK indexing, so that every frame after the first refers to the stored value.\n* The 124,003 bytes take 27.9 s to process before all 5,000 requests reach the Plug."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Unbounded digit loop.\u003c/strong\u003e \u003ccode\u003eBandit.Headers.parse_integer/2\u003c/code\u003e in \u003ccode\u003elib/bandit/headers.ex\u003c/code\u003e builds the number with \u003ccode\u003etotal * 10 + digit - ?0\u003c/code\u003e and has no limit on the digit count. Each step allocates a new bignum, so parsing one value costs O(n\u00b2). On OTP 28, 10,000 digits take about 10 ms, 50,000 about 250 ms and 80,000 about 560 ms, where \u003ccode\u003eString.to_integer/1\u003c/code\u003e handles the same inputs in 1 to 3 ms.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Reached on raw input.\u003c/strong\u003e It is called from \u003ccode\u003eget_content_length/1\u003c/code\u003e and \u003ccode\u003eparse_hostlike_header!/1\u003c/code\u003e on raw request input, before any range check. An all-digit \u003ccode\u003econtent-length\u003c/code\u003e is returned as \u003ccode\u003e{:ok, huge_int}\u003c/code\u003e, and the \u003ccode\u003eis_port_number\u003c/code\u003e guard only runs after the whole number is built.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. HPACK amplification.\u003c/strong\u003e Over HTTP/2, a 4,000-digit value (about 1.1 ms to parse) can be stored once in the HPACK dynamic table and then re-sent on each new stream for about one byte.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Unbounded digit loop.** `Bandit.Headers.parse_integer/2` in `lib/bandit/headers.ex` builds the number with `total * 10 + digit - ?0` and has no limit on the digit count. Each step allocates a new bignum, so parsing one value costs O(n\u00b2). On OTP 28, 10,000 digits take about 10 ms, 50,000 about 250 ms and 80,000 about 560 ms, where `String.to_integer/1` handles the same inputs in 1 to 3 ms.\n\n**2. Reached on raw input.** It is called from `get_content_length/1` and `parse_hostlike_header!/1` on raw request input, before any range check. An all-digit `content-length` is returned as `{:ok, huge_int}`, and the `is_port_number` guard only runs after the whole number is built.\n\n**3. HPACK amplification.** Over HTTP/2, a 4,000-digit value (about 1.1 ms to parse) can be stored once in the HPACK dynamic table and then re-sent on each new stream for about one byte."
            }
          ],
          "value": "1. Unbounded digit loop. Bandit.Headers.parse_integer/2 in lib/bandit/headers.ex builds the number with total * 10 + digit - ?0 and has no limit on the digit count. Each step allocates a new bignum, so parsing one value costs O(n\u00b2). On OTP 28, 10,000 digits take about 10 ms, 50,000 about 250 ms and 80,000 about 560 ms, where String.to_integer/1 handles the same inputs in 1 to 3 ms.\n\n2. Reached on raw input. It is called from get_content_length/1 and parse_hostlike_header!/1 on raw request input, before any range check. An all-digit content-length is returned as {:ok, huge_int}, and the is_port_number guard only runs after the whole number is built.\n\n3. HPACK amplification. Over HTTP/2, a 4,000-digit value (about 1.1 ms to parse) can be stored once in the HPACK dynamic table and then re-sent on each new stream for about one byte."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-113436",
    "datePublished": "2026-10-26T13:31:35.033Z",
    "dateReserved": "2026-10-23T10:30:01.881Z",
    "dateUpdated": "2026-10-26T15:35:51.556Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-110314 (GCVE-0-2026-110314)
Vulnerability from cvelistv5
Published
2026-10-26 13:31
Modified
2026-10-26 13:31
CWE
  • CWE-602 - Client-Side Enforcement of Server-Side Security
Summary
Client-Side Enforcement of Server-Side Security vulnerability in the WebSocket connection handling of mtrudel bandit allows a remote client to keep running WebSock handler code after the server has stopped its connection, via data frames sent after ignoring the server's Close frame. When a WebSock callback returns {:stop, ...}, Bandit.WebSocket.Connection calls terminate/2, sends Close and shuts down the write side, but keeps the connection process running in a :closing state. handle_frame/3 and handle_info/3 never check that state, so later frames still reach handle_in/2 and later messages reach handle_info/2, with the state from the stop tuple, until the client sends Close, TCP drops or the idle timeout fires. A client refused in init/1 after a failed token check could still run an admin-only action. This issue affects bandit: from 0.5.1 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.5.1   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: bb407f2277dcb65a58dd8cc07913a0b58a80e266
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "0.5.1",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "bb407f2277dcb65a58dd8cc07913a0b58a80e266",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "0.5.1",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T12:47:59.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eClient-Side Enforcement of Server-Side Security vulnerability in the WebSocket connection handling of mtrudel bandit allows a remote client to keep running WebSock handler code after the server has stopped its connection, via data frames sent after ignoring the server\u0027s Close frame.\u003c/p\u003e\n\u003cp\u003eWhen a WebSock callback returns \u003ccode\u003e{:stop, ...}\u003c/code\u003e, \u003ccode\u003eBandit.WebSocket.Connection\u003c/code\u003e calls \u003ccode\u003eterminate/2\u003c/code\u003e, sends Close and shuts down the write side, but keeps the connection process running in a \u003ccode\u003e:closing\u003c/code\u003e state. \u003ccode\u003ehandle_frame/3\u003c/code\u003e and \u003ccode\u003ehandle_info/3\u003c/code\u003e never check that state, so later frames still reach \u003ccode\u003ehandle_in/2\u003c/code\u003e and later messages reach \u003ccode\u003ehandle_info/2\u003c/code\u003e, with the state from the stop tuple, until the client sends Close, TCP drops or the idle timeout fires. A client refused in \u003ccode\u003einit/1\u003c/code\u003e after a failed token check could still run an admin-only action.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.5.1 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Client-Side Enforcement of Server-Side Security vulnerability in the WebSocket connection handling of mtrudel bandit allows a remote client to keep running WebSock handler code after the server has stopped its connection, via data frames sent after ignoring the server\u0027s Close frame.\n\nWhen a WebSock callback returns `{:stop, ...}`, `Bandit.WebSocket.Connection` calls `terminate/2`, sends Close and shuts down the write side, but keeps the connection process running in a `:closing` state. `handle_frame/3` and `handle_info/3` never check that state, so later frames still reach `handle_in/2` and later messages reach `handle_info/2`, with the state from the stop tuple, until the client sends Close, TCP drops or the idle timeout fires. A client refused in `init/1` after a failed token check could still run an admin-only action.\n\nThis issue affects bandit: from 0.5.1 before 1.14.0."
            }
          ],
          "value": "Client-Side Enforcement of Server-Side Security vulnerability in the WebSocket connection handling of mtrudel bandit allows a remote client to keep running WebSock handler code after the server has stopped its connection, via data frames sent after ignoring the server\u0027s Close frame.\n\nWhen a WebSock callback returns {:stop, ...}, Bandit.WebSocket.Connection calls terminate/2, sends Close and shuts down the write side, but keeps the connection process running in a :closing state. handle_frame/3 and handle_info/3 never check that state, so later frames still reach handle_in/2 and later messages reach handle_info/2, with the state from the stop tuple, until the client sends Close, TCP drops or the idle timeout fires. A client refused in init/1 after a failed token check could still run an admin-only action.\n\nThis issue affects bandit: from 0.5.1 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-202",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eA remote client that ignores the Close frame can keep invoking a WebSock application\u0027s handlers after the application refused or ended its session, bypassing an authorization check in \u003ccode\u003einit/1\u003c/code\u003e or a kick or ban. This affects WebSock applications that rely on \u003ccode\u003e{:stop, ...}\u003c/code\u003e to end a session.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "A remote client that ignores the Close frame can keep invoking a WebSock application\u0027s handlers after the application refused or ended its session, bypassing an authorization check in `init/1` or a kick or ban. This affects WebSock applications that rely on `{:stop, ...}` to end a session."
                }
              ],
              "value": "A remote client that ignores the Close frame can keep invoking a WebSock application\u0027s handlers after the application refused or ended its session, bypassing an authorization check in init/1 or a kick or ban. This affects WebSock applications that rely on {:stop, ...} to end a session."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "PRESENT",
            "attackVector": "NETWORK",
            "baseScore": 6.3,
            "baseSeverity": "MEDIUM",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
            "version": "4.0",
            "vulnAvailabilityImpact": "NONE",
            "vulnConfidentialityImpact": "LOW",
            "vulnIntegrityImpact": "LOW",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-602",
              "description": "CWE-602 Client-Side Enforcement of Server-Side Security",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:31:18.257Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "WebSocket frames keep reaching the WebSock after a server-initiated close in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-2v5v-8f9v-xh85"
        },
        {
          "name": "EEF CNA record for CVE-2026-110314",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-110314.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-110314",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-110314"
        },
        {
          "name": "Introducing commit bb407f2 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/bb407f2277dcb65a58dd8cc07913a0b58a80e266"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "WebSocket frames keep reaching the WebSock after a server-initiated close in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eMark the WebSock state as stopped whenever a callback returns \u003ccode\u003e{:stop, ...}\u003c/code\u003e, and have \u003ccode\u003ehandle_in/2\u003c/code\u003e and \u003ccode\u003ehandle_info/2\u003c/code\u003e return \u003ccode\u003e{:ok, state}\u003c/code\u003e without acting while that mark is set.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Mark the WebSock state as stopped whenever a callback returns `{:stop, ...}`, and have `handle_in/2` and `handle_info/2` return `{:ok, state}` without acting while that mark is set."
            }
          ],
          "value": "Mark the WebSock state as stopped whenever a callback returns {:stop, ...}, and have handle_in/2 and handle_info/2 return {:ok, state} without acting while that mark is set."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eServe a WebSock whose \u003ccode\u003einit/1\u003c/code\u003e returns \u003ccode\u003e{:stop, :normal, {4401, \"unauthorized\"}, state}\u003c/code\u003e for a wrong token, and whose \u003ccode\u003ehandle_in/2\u003c/code\u003e runs an admin-only \u003ccode\u003edelete_all_users\u003c/code\u003e action when \u003ccode\u003euser_id\u003c/code\u003e is \u003ccode\u003eadmin\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eUpgrade with \u003ccode\u003e?user_id=admin\u0026amp;token=wrong\u003c/code\u003e. The server calls \u003ccode\u003eterminate/2\u003c/code\u003e and sends a Close frame with code 4401.\u003c/li\u003e\n\u003cli\u003eIgnore the Close frame and send a masked text frame \u003ccode\u003edelete_all_users\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ehandle_in/2\u003c/code\u003e runs \u003ccode\u003edelete_all_users\u003c/code\u003e as \u003ccode\u003eadmin\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Serve a WebSock whose `init/1` returns `{:stop, :normal, {4401, \"unauthorized\"}, state}` for a wrong token, and whose `handle_in/2` runs an admin-only `delete_all_users` action when `user_id` is `admin`.\n2. Upgrade with `?user_id=admin\u0026token=wrong`. The server calls `terminate/2` and sends a Close frame with code 4401.\n3. Ignore the Close frame and send a masked text frame `delete_all_users`.\n4. `handle_in/2` runs `delete_all_users` as `admin`."
            }
          ],
          "value": "* Serve a WebSock whose init/1 returns {:stop, :normal, {4401, \"unauthorized\"}, state} for a wrong token, and whose handle_in/2 runs an admin-only delete_all_users action when user_id is admin.\n* Upgrade with ?user_id=admin\u0026token=wrong. The server calls terminate/2 and sends a Close frame with code 4401.\n* Ignore the Close frame and send a masked text frame delete_all_users.\n* handle_in/2 runs delete_all_users as admin."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Stop without close.\u003c/strong\u003e In \u003ccode\u003eBandit.WebSocket.Connection\u003c/code\u003e, \u003ccode\u003edo_stop/4\u003c/code\u003e calls \u003ccode\u003eterminate/2\u003c/code\u003e, sends the Close frame and runs \u003ccode\u003eshutdown(:write)\u003c/code\u003e. It then returns \u003ccode\u003e{:continue, %{connection | state: :closing}}\u003c/code\u003e, so \u003ccode\u003eBandit.WebSocket.Handler\u003c/code\u003e keeps the process alive.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. No state check.\u003c/strong\u003e \u003ccode\u003ehandle_frame/3\u003c/code\u003e and \u003ccode\u003ehandle_info/3\u003c/code\u003e never check \u003ccode\u003econnection.state\u003c/code\u003e. Every later data frame still reaches \u003ccode\u003ehandle_in/2\u003c/code\u003e, and every later message still reaches \u003ccode\u003ehandle_info/2\u003c/code\u003e, with the state from the stop tuple.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Client-controlled lifetime.\u003c/strong\u003e The connection ends only when the client sends Close, TCP drops, or the idle timeout fires, and each client frame resets that timeout. All stops through \u003ccode\u003edo_stop\u003c/code\u003e are affected: \u003ccode\u003e{:stop, :normal, ...}\u003c/code\u003e, \u003ccode\u003e{:stop, {:shutdown, :disconnected}, ...}\u003c/code\u003e and \u003ccode\u003e{:stop, {:shutdown, :restart}, ...}\u003c/code\u003e, which is what Phoenix\u0027s \u003ccode\u003e:socket_drain\u003c/code\u003e returns. A banned user who was kicked kept posting for longer than the idle timeout.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Stop without close.** In `Bandit.WebSocket.Connection`, `do_stop/4` calls `terminate/2`, sends the Close frame and runs `shutdown(:write)`. It then returns `{:continue, %{connection | state: :closing}}`, so `Bandit.WebSocket.Handler` keeps the process alive.\n\n**2. No state check.** `handle_frame/3` and `handle_info/3` never check `connection.state`. Every later data frame still reaches `handle_in/2`, and every later message still reaches `handle_info/2`, with the state from the stop tuple.\n\n**3. Client-controlled lifetime.** The connection ends only when the client sends Close, TCP drops, or the idle timeout fires, and each client frame resets that timeout. All stops through `do_stop` are affected: `{:stop, :normal, ...}`, `{:stop, {:shutdown, :disconnected}, ...}` and `{:stop, {:shutdown, :restart}, ...}`, which is what Phoenix\u0027s `:socket_drain` returns. A banned user who was kicked kept posting for longer than the idle timeout."
            }
          ],
          "value": "1. Stop without close. In Bandit.WebSocket.Connection, do_stop/4 calls terminate/2, sends the Close frame and runs shutdown(:write). It then returns {:continue, %{connection | state: :closing}}, so Bandit.WebSocket.Handler keeps the process alive.\n\n2. No state check. handle_frame/3 and handle_info/3 never check connection.state. Every later data frame still reaches handle_in/2, and every later message still reaches handle_info/2, with the state from the stop tuple.\n\n3. Client-controlled lifetime. The connection ends only when the client sends Close, TCP drops, or the idle timeout fires, and each client frame resets that timeout. All stops through do_stop are affected: {:stop, :normal, ...}, {:stop, {:shutdown, :disconnected}, ...} and {:stop, {:shutdown, :restart}, ...}, which is what Phoenix\u0027s :socket_drain returns. A banned user who was kicked kept posting for longer than the idle timeout."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-110314",
    "datePublished": "2026-10-26T13:31:18.257Z",
    "dateReserved": "2026-10-23T12:45:01.107Z",
    "dateUpdated": "2026-10-26T13:31:18.257Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-114213 (GCVE-0-2026-114213)
Vulnerability from cvelistv5
Published
2026-10-26 13:30
Modified
2026-10-26 15:35
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in the WebSocket fragment reassembly of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a fragmented message made of many 1-byte continuation frames. Bandit.WebSocket.Connection checks max_fragmented_message_size against the payload bytes received, but keeps every non-final continuation frame as a new list cell around its own binary, and nothing limits the number of fragments. A 1-byte fragment costs 7 bytes on the wire but about 150 to 170 bytes of server memory, so one connection can hold about 150 times the configured limit, roughly 1.2 to 1.4 GB at the default 8 MB. Ping frames between fragments keep the connection alive past the idle timeout. This issue affects bandit: from 1.11.0 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.11.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 21612c7c7b1ce43eccd36d3af3a2299d23513667
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-114213",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:03:01.722486Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:51.785Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "1.11.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "21612c7c7b1ce43eccd36d3af3a2299d23513667",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "1.11.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T12:53:37.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAllocation of Resources Without Limits or Throttling vulnerability in the WebSocket fragment reassembly of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a fragmented message made of many 1-byte continuation frames.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eBandit.WebSocket.Connection\u003c/code\u003e checks \u003ccode\u003emax_fragmented_message_size\u003c/code\u003e against the payload bytes received, but keeps every non-final continuation frame as a new list cell around its own binary, and nothing limits the number of fragments. A 1-byte fragment costs 7 bytes on the wire but about 150 to 170 bytes of server memory, so one connection can hold about 150 times the configured limit, roughly 1.2 to 1.4 GB at the default 8 MB. Ping frames between fragments keep the connection alive past the idle timeout.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 1.11.0 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in the WebSocket fragment reassembly of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a fragmented message made of many 1-byte continuation frames.\n\n`Bandit.WebSocket.Connection` checks `max_fragmented_message_size` against the payload bytes received, but keeps every non-final continuation frame as a new list cell around its own binary, and nothing limits the number of fragments. A 1-byte fragment costs 7 bytes on the wire but about 150 to 170 bytes of server memory, so one connection can hold about 150 times the configured limit, roughly 1.2 to 1.4 GB at the default 8 MB. Ping frames between fragments keep the connection alive past the idle timeout.\n\nThis issue affects bandit: from 1.11.0 before 1.14.0."
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in the WebSocket fragment reassembly of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a fragmented message made of many 1-byte continuation frames.\n\nBandit.WebSocket.Connection checks max_fragmented_message_size against the payload bytes received, but keeps every non-final continuation frame as a new list cell around its own binary, and nothing limits the number of fragments. A 1-byte fragment costs 7 bytes on the wire but about 150 to 170 bytes of server memory, so one connection can hold about 150 times the configured limit, roughly 1.2 to 1.4 GB at the default 8 MB. Ping frames between fragments keep the connection alive past the idle timeout.\n\nThis issue affects bandit: from 1.11.0 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated client can make one WebSocket connection hold about 150 times \u003ccode\u003emax_fragmented_message_size\u003c/code\u003e in server memory (about 82 MB for 3.5 MB of traffic in the reporter\u0027s test). This affects any Bandit server that accepts WebSocket upgrades with default settings, including Phoenix LiveView and Channels apps.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated client can make one WebSocket connection hold about 150 times `max_fragmented_message_size` in server memory (about 82 MB for 3.5 MB of traffic in the reporter\u0027s test). This affects any Bandit server that accepts WebSocket upgrades with default settings, including Phoenix LiveView and Channels apps."
                }
              ],
              "value": "An unauthenticated client can make one WebSocket connection hold about 150 times max_fragmented_message_size in server memory (about 82 MB for 3.5 MB of traffic in the reporter\u0027s test). This affects any Bandit server that accepts WebSocket upgrades with default settings, including Phoenix LiveView and Channels apps."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:30:58.262Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "WebSocket 1-byte continuation frames bypass `max_fragmented_message_size` in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-cpx7-qf6w-jc63"
        },
        {
          "name": "EEF CNA record for CVE-2026-114213",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-114213.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-114213",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-114213"
        },
        {
          "name": "Introducing commit 21612c7 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/21612c7c7b1ce43eccd36d3af3a2299d23513667"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "WebSocket 1-byte continuation frames bypass `max_fragmented_message_size` in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eLower \u003ccode\u003ewebsocket_options: [max_fragmented_message_size: ...]\u003c/code\u003e to the largest fragmented message the application needs, for example \u003ccode\u003e65_536\u003c/code\u003e. Memory per connection stays at about 150 times this limit, so a lower limit lowers the ceiling.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Lower `websocket_options: [max_fragmented_message_size: ...]` to the largest fragmented message the application needs, for example `65_536`. Memory per connection stays at about 150 times this limit, so a lower limit lowers the ceiling."
            }
          ],
          "value": "Lower websocket_options: [max_fragmented_message_size: ...] to the largest fragmented message the application needs, for example 65_536. Memory per connection stays at about 150 times this limit, so a lower limit lowers the ceiling."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eRun Bandit with default settings and a WebSock that accepts every connection.\u003c/li\u003e\n\u003cli\u003eComplete the WebSocket upgrade and send a non-final text frame with a 1-byte payload.\u003c/li\u003e\n\u003cli\u003eSend 500,000 masked non-final continuation frames with 1-byte payloads, 3.5 MB in all.\u003c/li\u003e\n\u003cli\u003eSend a Ping. The server answers with a Pong, and BEAM memory has grown by about 82 MB while the message is still far below the 8 MB cap.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Run Bandit with default settings and a WebSock that accepts every connection.\n2. Complete the WebSocket upgrade and send a non-final text frame with a 1-byte payload.\n3. Send 500,000 masked non-final continuation frames with 1-byte payloads, 3.5 MB in all.\n4. Send a Ping. The server answers with a Pong, and BEAM memory has grown by about 82 MB while the message is still far below the 8 MB cap."
            }
          ],
          "value": "* Run Bandit with default settings and a WebSock that accepts every connection.\n* Complete the WebSocket upgrade and send a non-final text frame with a 1-byte payload.\n* Send 500,000 masked non-final continuation frames with 1-byte payloads, 3.5 MB in all.\n* Send a Ping. The server answers with a Pong, and BEAM memory has grown by about 82 MB while the message is still far below the 8 MB cap."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Byte-only limit.\u003c/strong\u003e In \u003ccode\u003eBandit.WebSocket.Connection\u003c/code\u003e (\u003ccode\u003elib/bandit/websocket/connection.ex\u003c/code\u003e), the fragment limit is checked against \u003ccode\u003efragment_size\u003c/code\u003e, which only adds up payload bytes (\u003ccode\u003eIO.iodata_length(frame.data)\u003c/code\u003e).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Per-fragment accumulation.\u003c/strong\u003e Each non-final continuation frame is stored as a new cons cell around its own binary, \u003ccode\u003e[connection.fragment_frame.data | frame.data]\u003c/code\u003e. Only zero-length fragments are kept out, so 1-byte fragments are accepted. Each costs 7 bytes on the wire but keeps about 150 to 170 bytes in memory. Against 1.12.5, 1M fragments kept about 169 MB and 3M kept about 450 MB, growing linearly, and about 56 MB of traffic reaches the 8 MB payload cap.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Kept alive.\u003c/strong\u003e Ping frames sent between fragments are handled by \u003ccode\u003ehandle_control_frame\u003c/code\u003e without touching the fragment state, so they keep the connection alive past the idle timeout.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Byte-only limit.** In `Bandit.WebSocket.Connection` (`lib/bandit/websocket/connection.ex`), the fragment limit is checked against `fragment_size`, which only adds up payload bytes (`IO.iodata_length(frame.data)`).\n\n**2. Per-fragment accumulation.** Each non-final continuation frame is stored as a new cons cell around its own binary, `[connection.fragment_frame.data | frame.data]`. Only zero-length fragments are kept out, so 1-byte fragments are accepted. Each costs 7 bytes on the wire but keeps about 150 to 170 bytes in memory. Against 1.12.5, 1M fragments kept about 169 MB and 3M kept about 450 MB, growing linearly, and about 56 MB of traffic reaches the 8 MB payload cap.\n\n**3. Kept alive.** Ping frames sent between fragments are handled by `handle_control_frame` without touching the fragment state, so they keep the connection alive past the idle timeout."
            }
          ],
          "value": "1. Byte-only limit. In Bandit.WebSocket.Connection (lib/bandit/websocket/connection.ex), the fragment limit is checked against fragment_size, which only adds up payload bytes (IO.iodata_length(frame.data)).\n\n2. Per-fragment accumulation. Each non-final continuation frame is stored as a new cons cell around its own binary, [connection.fragment_frame.data | frame.data]. Only zero-length fragments are kept out, so 1-byte fragments are accepted. Each costs 7 bytes on the wire but keeps about 150 to 170 bytes in memory. Against 1.12.5, 1M fragments kept about 169 MB and 3M kept about 450 MB, growing linearly, and about 56 MB of traffic reaches the 8 MB payload cap.\n\n3. Kept alive. Ping frames sent between fragments are handled by handle_control_frame without touching the fragment state, so they keep the connection alive past the idle timeout."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-114213",
    "datePublished": "2026-10-26T13:30:58.262Z",
    "dateReserved": "2026-10-23T09:15:01.838Z",
    "dateUpdated": "2026-10-26T15:35:51.785Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-114216 (GCVE-0-2026-114216)
Vulnerability from cvelistv5
Published
2026-10-26 13:30
Modified
2026-10-26 15:35
CWE
  • CWE-444 - Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
Summary
Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling') vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to smuggle a second request past a front-end proxy via a chunk extension that contains a bare LF. The chunked reader in lib/bandit/http1/socket.ex takes the chunk-size line to be everything up to the first CRLF and discards everything after ; without checking it against the RFC 9112 chunk-ext grammar, so bare LF, bare CR and other control bytes pass. A proxy that ends the size line at a bare LF reads 2;\nxx\r\n40\r\n... as one request whose second chunk holds a GET /admin, while Bandit ends the body early and dispatches GET /admin as a separate pipelined request. Deployments without such a proxy in front of Bandit are not affected. This issue affects bandit: from 1.12.4 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.12.4   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: c31e9e3c76dcbfc239a96eabed5341ced7885ae5
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-114216",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:03:20.382433Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:51.933Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "1.12.4",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "c31e9e3c76dcbfc239a96eabed5341ced7885ae5",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "1.12.4",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T12:59:00.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eInconsistent Interpretation of HTTP Requests (\u0027HTTP Request/Response Smuggling\u0027) vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to smuggle a second request past a front-end proxy via a chunk extension that contains a bare LF.\u003c/p\u003e\n\u003cp\u003eThe chunked reader in \u003ccode\u003elib/bandit/http1/socket.ex\u003c/code\u003e takes the chunk-size line to be everything up to the first CRLF and discards everything after \u003ccode\u003e;\u003c/code\u003e without checking it against the RFC 9112 \u003ccode\u003echunk-ext\u003c/code\u003e grammar, so bare LF, bare CR and other control bytes pass. A proxy that ends the size line at a bare LF reads \u003ccode\u003e2;\\nxx\\r\\n40\\r\\n...\u003c/code\u003e as one request whose second chunk holds a \u003ccode\u003eGET /admin\u003c/code\u003e, while Bandit ends the body early and dispatches \u003ccode\u003eGET /admin\u003c/code\u003e as a separate pipelined request. Deployments without such a proxy in front of Bandit are not affected.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 1.12.4 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Inconsistent Interpretation of HTTP Requests (\u0027HTTP Request/Response Smuggling\u0027) vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to smuggle a second request past a front-end proxy via a chunk extension that contains a bare LF.\n\nThe chunked reader in `lib/bandit/http1/socket.ex` takes the chunk-size line to be everything up to the first CRLF and discards everything after `;` without checking it against the RFC 9112 `chunk-ext` grammar, so bare LF, bare CR and other control bytes pass. A proxy that ends the size line at a bare LF reads `2;\\nxx\\r\\n40\\r\\n...` as one request whose second chunk holds a `GET /admin`, while Bandit ends the body early and dispatches `GET /admin` as a separate pipelined request. Deployments without such a proxy in front of Bandit are not affected.\n\nThis issue affects bandit: from 1.12.4 before 1.14.0."
            }
          ],
          "value": "Inconsistent Interpretation of HTTP Requests (\u0027HTTP Request/Response Smuggling\u0027) vulnerability in the HTTP/1.1 chunked body reader of mtrudel bandit allows an unauthenticated remote attacker to smuggle a second request past a front-end proxy via a chunk extension that contains a bare LF.\n\nThe chunked reader in lib/bandit/http1/socket.ex takes the chunk-size line to be everything up to the first CRLF and discards everything after ; without checking it against the RFC 9112 chunk-ext grammar, so bare LF, bare CR and other control bytes pass. A proxy that ends the size line at a bare LF reads 2;\\nxx\\r\\n40\\r\\n... as one request whose second chunk holds a GET /admin, while Bandit ends the body early and dispatches GET /admin as a separate pipelined request. Deployments without such a proxy in front of Bandit are not affected.\n\nThis issue affects bandit: from 1.12.4 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-33",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eBehind a front-end proxy that treats a bare LF as a line ending and forwards chunked bodies unchanged, an unauthenticated attacker can have Bandit run requests the proxy never saw, getting past the proxy\u0027s access controls.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Behind a front-end proxy that treats a bare LF as a line ending and forwards chunked bodies unchanged, an unauthenticated attacker can have Bandit run requests the proxy never saw, getting past the proxy\u0027s access controls."
                }
              ],
              "value": "Behind a front-end proxy that treats a bare LF as a line ending and forwards chunked bodies unchanged, an unauthenticated attacker can have Bandit run requests the proxy never saw, getting past the proxy\u0027s access controls."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "PRESENT",
            "attackVector": "NETWORK",
            "baseScore": 6.3,
            "baseSeverity": "MEDIUM",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "LOW",
            "subIntegrityImpact": "LOW",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:L/SI:L/SA:N",
            "version": "4.0",
            "vulnAvailabilityImpact": "NONE",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "LOW",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-444",
              "description": "CWE-444 Inconsistent Interpretation of HTTP Requests (\u0027HTTP Request/Response Smuggling\u0027)",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:30:41.225Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "Chunk extensions accept bare LF and CR, enabling request smuggling in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-w5wc-g2hc-g7m2"
        },
        {
          "name": "EEF CNA record for CVE-2026-114216",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-114216.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-114216",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-114216"
        },
        {
          "name": "Introducing commit c31e9e3 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/c31e9e3c76dcbfc239a96eabed5341ced7885ae5"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "Chunk extensions accept bare LF and CR, enabling request smuggling in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eWhere the front-end proxy can reach Bandit over HTTP/2, disable HTTP/1 with \u003ccode\u003ehttp_1_options: [enabled: false]\u003c/code\u003e, so that the chunked body reader never runs.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Where the front-end proxy can reach Bandit over HTTP/2, disable HTTP/1 with `http_1_options: [enabled: false]`, so that the chunked body reader never runs."
            }
          ],
          "value": "Where the front-end proxy can reach Bandit over HTTP/2, disable HTTP/1 with http_1_options: [enabled: false], so that the chunked body reader never runs."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eRun Bandit with a Plug that reads the body and records each request\u0027s method and path.\u003c/li\u003e\n\u003cli\u003eOn one connection, send \u003ccode\u003ePOST /public\u003c/code\u003e with \u003ccode\u003eTransfer-Encoding: chunked\u003c/code\u003e and the body \u003ccode\u003e2;\\nxx\\r\\n40\\r\\n0\\r\\n\\r\\n\u003c/code\u003e, followed directly by \u003ccode\u003eGET /admin HTTP/1.1\u003c/code\u003e with \u003ccode\u003eContent-Length: 7\u003c/code\u003e and the body \u003ccode\u003e\\r\\n0\\r\\n\\r\\n\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eBandit dispatches both \u003ccode\u003ePOST /public\u003c/code\u003e with the body \u003ccode\u003e40\u003c/code\u003e and \u003ccode\u003eGET /admin\u003c/code\u003e. A proxy that ends lines at a bare LF would have forwarded the same bytes as the single \u003ccode\u003ePOST /public\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Run Bandit with a Plug that reads the body and records each request\u0027s method and path.\n2. On one connection, send `POST /public` with `Transfer-Encoding: chunked` and the body `2;\\nxx\\r\\n40\\r\\n0\\r\\n\\r\\n`, followed directly by `GET /admin HTTP/1.1` with `Content-Length: 7` and the body `\\r\\n0\\r\\n\\r\\n`.\n3. Bandit dispatches both `POST /public` with the body `40` and `GET /admin`. A proxy that ends lines at a bare LF would have forwarded the same bytes as the single `POST /public`."
            }
          ],
          "value": "* Run Bandit with a Plug that reads the body and records each request\u0027s method and path.\n* On one connection, send POST /public with Transfer-Encoding: chunked and the body 2;\\nxx\\r\\n40\\r\\n0\\r\\n\\r\\n, followed directly by GET /admin HTTP/1.1 with Content-Length: 7 and the body \\r\\n0\\r\\n\\r\\n.\n* Bandit dispatches both POST /public with the body 40 and GET /admin. A proxy that ends lines at a bare LF would have forwarded the same bytes as the single POST /public."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Unchecked extension.\u003c/strong\u003e In \u003ccode\u003elib/bandit/http1/socket.ex\u003c/code\u003e, the chunked body reader takes the chunk-size line to be everything up to the first CRLF. Anything after \u003ccode\u003e;\u003c/code\u003e is thrown away without being checked against the RFC 9112 \u00a77.1.1 \u003ccode\u003echunk-ext\u003c/code\u003e grammar, so bare LF, bare CR and other control bytes are accepted. The 16-byte size-line cap does not help, because the payload needs only 5 bytes.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Bandit\u0027s reading.\u003c/strong\u003e For the body \u003ccode\u003e2;\\nxx\\r\\n40\\r\\n0\\r\\n\\r\\nGET /admin ...\u003c/code\u003e, Bandit takes \u003ccode\u003e2;\\nxx\u003c/code\u003e as the size line and \u003ccode\u003e40\u003c/code\u003e as the 2-byte chunk, and the embedded \u003ccode\u003e0\\r\\n\\r\\n\u003c/code\u003e ends the body. Bandit then runs \u003ccode\u003eGET /admin\u003c/code\u003e as a second pipelined request.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. The proxy\u0027s reading.\u003c/strong\u003e A proxy that ends the size line at the bare LF sees only one POST, with the chunk \u003ccode\u003exx\u003c/code\u003e and a 0x40-byte chunk that holds the \u003ccode\u003eGET /admin\u003c/code\u003e bytes.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Unchecked extension.** In `lib/bandit/http1/socket.ex`, the chunked body reader takes the chunk-size line to be everything up to the first CRLF. Anything after `;` is thrown away without being checked against the RFC 9112 \u00a77.1.1 `chunk-ext` grammar, so bare LF, bare CR and other control bytes are accepted. The 16-byte size-line cap does not help, because the payload needs only 5 bytes.\n\n**2. Bandit\u0027s reading.** For the body `2;\\nxx\\r\\n40\\r\\n0\\r\\n\\r\\nGET /admin ...`, Bandit takes `2;\\nxx` as the size line and `40` as the 2-byte chunk, and the embedded `0\\r\\n\\r\\n` ends the body. Bandit then runs `GET /admin` as a second pipelined request.\n\n**3. The proxy\u0027s reading.** A proxy that ends the size line at the bare LF sees only one POST, with the chunk `xx` and a 0x40-byte chunk that holds the `GET /admin` bytes."
            }
          ],
          "value": "1. Unchecked extension. In lib/bandit/http1/socket.ex, the chunked body reader takes the chunk-size line to be everything up to the first CRLF. Anything after ; is thrown away without being checked against the RFC 9112 \u00a77.1.1 chunk-ext grammar, so bare LF, bare CR and other control bytes are accepted. The 16-byte size-line cap does not help, because the payload needs only 5 bytes.\n\n2. Bandit\u0027s reading. For the body 2;\\nxx\\r\\n40\\r\\n0\\r\\n\\r\\nGET /admin ..., Bandit takes 2;\\nxx as the size line and 40 as the 2-byte chunk, and the embedded 0\\r\\n\\r\\n ends the body. Bandit then runs GET /admin as a second pipelined request.\n\n3. The proxy\u0027s reading. A proxy that ends the size line at the bare LF sees only one POST, with the chunk xx and a 0x40-byte chunk that holds the GET /admin bytes."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-114216",
    "datePublished": "2026-10-26T13:30:41.225Z",
    "dateReserved": "2026-10-23T10:45:02.529Z",
    "dateUpdated": "2026-10-26T15:35:51.933Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-115269 (GCVE-0-2026-115269)
Vulnerability from cvelistv5
Published
2026-10-26 13:30
Modified
2026-10-26 15:35
CWE
  • CWE-835 - Loop with Unreachable Exit Condition ('Infinite Loop')
  • CWE-770 - Allocation of Resources Without Limits or Throttling
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.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.4.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: fff06efe3be962b484bd4d3eb339372fda5a231f
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-115269",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:02:19.174445Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:52.079Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "1.4.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "fff06efe3be962b484bd4d3eb339372fda5a231f",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "1.4.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "analyst",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "reporter",
          "value": "Daniel Coles"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T13:25:29.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAllocation 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.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eBandit.HTTP2.Stream.do_read_data/4\u003c/code\u003e in \u003ccode\u003elib/bandit/http2/stream.ex\u003c/code\u003e waits for each frame with a fresh \u003ccode\u003eread_timeout\u003c/code\u003e. 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 \u003ccode\u003eread_body\u003c/code\u003e blocked, together with the stream process and whatever the application holds while reading, for as long as it likes.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 1.4.0 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "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.\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."
            }
          ],
          "value": "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.\n\nBandit.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."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-469",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn 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 \u003ccode\u003eread_timeout\u003c/code\u003e, and repeat this across many streams and connections. This affects any Bandit server with HTTP/2 enabled whose routes read request bodies, which \u003ccode\u003ePlug.Parsers\u003c/code\u003e does for POST, PUT and PATCH.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "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."
                }
              ],
              "value": "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."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-835",
              "description": "CWE-835 Loop with Unreachable Exit Condition (\u0027Infinite Loop\u0027)",
              "lang": "en",
              "type": "CWE"
            }
          ]
        },
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:30:24.621Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "HTTP/2 WINDOW_UPDATE and SETTINGS frames reset the request body read timeout in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-p2qv-vx88-385x"
        },
        {
          "name": "EEF CNA record for CVE-2026-115269",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-115269.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-115269",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-115269"
        },
        {
          "name": "Introducing commit fff06ef in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/fff06efe3be962b484bd4d3eb339372fda5a231f"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "HTTP/2 WINDOW_UPDATE and SETTINGS frames reset the request body read timeout in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eDisable HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e, so that the vulnerable read path never runs.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Disable HTTP/2 with `http_2_options: [enabled: false]`, so that the vulnerable read path never runs."
            }
          ],
          "value": "Disable HTTP/2 with http_2_options: [enabled: false], so that the vulnerable read path never runs."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eRun Bandit with a Plug that calls \u003ccode\u003ePlug.Conn.read_body/2\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eOpen an HTTP/2 connection and send HEADERS for \u003ccode\u003ePOST /\u003c/code\u003e on stream 1 without END_STREAM.\u003c/li\u003e\n\u003cli\u003eEvery 10 seconds, send a WINDOW_UPDATE on stream 1 with an increment of 1.\u003c/li\u003e\n\u003cli\u003eThe stream stays open for 120 s, eight times the default 15 s \u003ccode\u003eread_timeout\u003c/code\u003e. Without the WINDOW_UPDATE frames, the server ends the read after about 15 s.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. 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."
            }
          ],
          "value": "* Run Bandit with a Plug that calls Plug.Conn.read_body/2.\n* Open an HTTP/2 connection and send HEADERS for POST / on stream 1 without END_STREAM.\n* Every 10 seconds, send a WINDOW_UPDATE on stream 1 with an increment of 1.\n* 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."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Per-receive timeout.\u003c/strong\u003e \u003ccode\u003eBandit.HTTP2.Stream.read_data/2\u003c/code\u003e passes a flat \u003ccode\u003eread_timeout\u003c/code\u003e (default 15,000 ms) to \u003ccode\u003edo_read_data/4\u003c/code\u003e, which blocks in \u003ccode\u003edo_recv/2\u003c/code\u003e\u0027s \u003ccode\u003ereceive ... after timeout\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Non-progress frames end the wait.\u003c/strong\u003e A stream WINDOW_UPDATE, delivered through \u003ccode\u003eBandit.HTTP2.Connection.handle_frame/3\u003c/code\u003e, or an empty SETTINGS frame, which delivers a zero-delta window update to every stream, is matched inside that \u003ccode\u003ereceive\u003c/code\u003e. \u003ccode\u003edo_recv_send_window_update/2\u003c/code\u003e returns a bare \u003ccode\u003e%Stream{}\u003c/code\u003e, and \u003ccode\u003edo_read_data/4\u003c/code\u003e recurses with the identical \u003ccode\u003etimeout\u003c/code\u003e, restarting the countdown.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Nothing else bounds the read.\u003c/strong\u003e The transport read timeout fires only when no bytes arrive, \u003ccode\u003emax_bytes\u003c/code\u003e is decremented only by DATA frames, and neither WINDOW_UPDATE nor SETTINGS frames are rate limited. The send path in \u003ccode\u003edo_send_data/4\u003c/code\u003e uses a progress-based deadline; the read path does not.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**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`\u0027s `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."
            }
          ],
          "value": "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\u0027s receive ... after timeout.\n\n2. 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\n3. 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."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-115269",
    "datePublished": "2026-10-26T13:30:24.621Z",
    "dateReserved": "2026-10-23T10:15:01.872Z",
    "dateUpdated": "2026-10-26T15:35:52.079Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-108525 (GCVE-0-2026-108525)
Vulnerability from cvelistv5
Published
2026-10-26 13:29
Modified
2026-10-26 15:35
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via DATA frames sent past the advertised receive window. The attacker requests a response larger than the 65,535-byte initial window and withholds WINDOW_UPDATE, so the stream process blocks while sending and stops reading, then floods small DATA frames on that stream. Every frame is queued in the stream's mailbox until the BEAM node runs out of memory. Bandit.HTTP2.Connection.handle_frame/3 in lib/bandit/http2/connection.ex forwards each DATA frame to the stream process without consulting the receive window, and Bandit.HTTP2.FlowControl.compute_recv_window/2 replenishes the window as data arrives rather than as it is consumed, and never answers a violation with FLOW_CONTROL_ERROR. This issue affects bandit: from 0.3.1 before 1.14.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.3.1   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: c8dc1e97050cf359f61ed6cdd110f3cdaf1d6f5f
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-108525",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-26T15:04:12.110479Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-26T15:35:52.227Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.14.0",
              "status": "affected",
              "version": "0.3.1",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "220b9dfd27f9a21305be2afc75cbe585ddc24e7a",
              "status": "affected",
              "version": "c8dc1e97050cf359f61ed6cdd110f3cdaf1d6f5f",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.14.0",
                  "versionStartIncluding": "0.3.1",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "reporter",
          "value": "Daniel Coles"
        },
        {
          "lang": "en",
          "type": "reporter",
          "value": "Neil Berkman"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "dateAssigned": "2026-10-23T10:13:11.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAllocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via DATA frames sent past the advertised receive window. The attacker requests a response larger than the 65,535-byte initial window and withholds \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e, so the stream process blocks while sending and stops reading, then floods small DATA frames on that stream. Every frame is queued in the stream\u0027s mailbox until the BEAM node runs out of memory.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eBandit.HTTP2.Connection.handle_frame/3\u003c/code\u003e in \u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e forwards each DATA frame to the stream process without consulting the receive window, and \u003ccode\u003eBandit.HTTP2.FlowControl.compute_recv_window/2\u003c/code\u003e replenishes the window as data arrives rather than as it is consumed, and never answers a violation with \u003ccode\u003eFLOW_CONTROL_ERROR\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.3.1 before 1.14.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via DATA frames sent past the advertised receive window. The attacker requests a response larger than the 65,535-byte initial window and withholds `WINDOW_UPDATE`, so the stream process blocks while sending and stops reading, then floods small DATA frames on that stream. Every frame is queued in the stream\u0027s mailbox until the BEAM node runs out of memory.\n\n`Bandit.HTTP2.Connection.handle_frame/3` in `lib/bandit/http2/connection.ex` forwards each DATA frame to the stream process without consulting the receive window, and `Bandit.HTTP2.FlowControl.compute_recv_window/2` replenishes the window as data arrives rather than as it is consumed, and never answers a violation with `FLOW_CONTROL_ERROR`.\n\nThis issue affects bandit: from 0.3.1 before 1.14.0."
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via DATA frames sent past the advertised receive window. The attacker requests a response larger than the 65,535-byte initial window and withholds WINDOW_UPDATE, so the stream process blocks while sending and stops reading, then floods small DATA frames on that stream. Every frame is queued in the stream\u0027s mailbox until the BEAM node runs out of memory.\n\nBandit.HTTP2.Connection.handle_frame/3 in lib/bandit/http2/connection.ex forwards each DATA frame to the stream process without consulting the receive window, and Bandit.HTTP2.FlowControl.compute_recv_window/2 replenishes the window as data arrives rather than as it is consumed, and never answers a violation with FLOW_CONTROL_ERROR.\n\nThis issue affects bandit: from 0.3.1 before 1.14.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-125",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated remote client can exhaust the memory of any Bandit server with HTTP/2 enabled by flooding DATA frames on a stream that is not reading its request body, such as one stalled sending a large response. The queued frames live in the BEAM node\u0027s memory, so exhausting it takes down every application on the node, not just the HTTP listener.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated remote client can exhaust the memory of any Bandit server with HTTP/2 enabled by flooding DATA frames on a stream that is not reading its request body, such as one stalled sending a large response. The queued frames live in the BEAM node\u0027s memory, so exhausting it takes down every application on the node, not just the HTTP listener."
                }
              ],
              "value": "An unauthenticated remote client can exhaust the memory of any Bandit server with HTTP/2 enabled by flooding DATA frames on a stream that is not reading its request body, such as one stalled sending a large response. The queued frames live in the BEAM node\u0027s memory, so exhausting it takes down every application on the node, not just the HTTP listener."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-26T13:29:42.520Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "HTTP/2 inbound flow control is not enforced, letting a client queue unbounded DATA in stream processes in Bandit",
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-c3g7-2pg5-qvwh"
        },
        {
          "name": "EEF CNA record for CVE-2026-108525",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-108525.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-108525",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-108525"
        },
        {
          "name": "Introducing commit c8dc1e9 in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/c8dc1e97050cf359f61ed6cdd110f3cdaf1d6f5f"
        },
        {
          "name": "Fix commit 220b9df in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "HTTP/2 inbound flow control is not enforced, letting a client queue unbounded DATA in stream processes in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eFrom 0.7.0, disable HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e, which removes the vulnerable path.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "From 0.7.0, disable HTTP/2 with `http_2_options: [enabled: false]`, which removes the vulnerable path."
            }
          ],
          "value": "From 0.7.0, disable HTTP/2 with http_2_options: [enabled: false], which removes the vulnerable path."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eOn an HTTP/2 connection, request a resource whose response exceeds 65,535 bytes and send no \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e. The server sends one window\u0027s worth and the stream blocks in \u003ccode\u003esend_data/3\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSend 1-byte DATA frames on that stream in batches, ignoring the server\u0027s receive windows.\u003c/li\u003e\n\u003cli\u003eBetween batches, at intervals under 15 seconds, send a stream \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e with an increment of 1 to keep the blocked send alive.\u003c/li\u003e\n\u003cli\u003eSend a PING. It is acknowledged, and the server has sent neither \u003ccode\u003eFLOW_CONTROL_ERROR\u003c/code\u003e nor \u003ccode\u003eGOAWAY\u003c/code\u003e while its memory grows with every frame.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. On an HTTP/2 connection, request a resource whose response exceeds 65,535 bytes and send no `WINDOW_UPDATE`. The server sends one window\u0027s worth and the stream blocks in `send_data/3`.\n2. Send 1-byte DATA frames on that stream in batches, ignoring the server\u0027s receive windows.\n3. Between batches, at intervals under 15 seconds, send a stream `WINDOW_UPDATE` with an increment of 1 to keep the blocked send alive.\n4. Send a PING. It is acknowledged, and the server has sent neither `FLOW_CONTROL_ERROR` nor `GOAWAY` while its memory grows with every frame."
            }
          ],
          "value": "* On an HTTP/2 connection, request a resource whose response exceeds 65,535 bytes and send no WINDOW_UPDATE. The server sends one window\u0027s worth and the stream blocks in send_data/3.\n* Send 1-byte DATA frames on that stream in batches, ignoring the server\u0027s receive windows.\n* Between batches, at intervals under 15 seconds, send a stream WINDOW_UPDATE with an increment of 1 to keep the blocked send alive.\n* Send a PING. It is acknowledged, and the server has sent neither FLOW_CONTROL_ERROR nor GOAWAY while its memory grows with every frame."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Connection-level window\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e, \u003ccode\u003elib/bandit/http2/flow_control.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003ehandle_frame/3\u003c/code\u003e clause for DATA frames sends the payload to the stream process before any window is checked. It then calls \u003ccode\u003ecompute_recv_window/2\u003c/code\u003e, which subtracts the frame size and, once the window drops below 2^30, advertises a \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e back up to 2^31-1. A negative window is accepted, so a peer that overruns the window is never refused, and the window refills when data arrives rather than when it is consumed.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Stream-level window\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/stream.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003eThe stream receive window is debited only when the stream process reads the DATA message, and it never rejects a violation either.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Accumulation\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWhile \u003ccode\u003esend_data/3\u003c/code\u003e waits for send window, its selective \u003ccode\u003ereceive\u003c/code\u003e matches only window updates and resets, so DATA messages pile up unread in the mailbox. A stream \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e of 1 byte, sent at intervals under the 15-second send timeout, keeps the stream alive while the attacker floods 1-byte DATA frames.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Connection-level window** (`lib/bandit/http2/connection.ex`, `lib/bandit/http2/flow_control.ex`)\n\nThe `handle_frame/3` clause for DATA frames sends the payload to the stream process before any window is checked. It then calls `compute_recv_window/2`, which subtracts the frame size and, once the window drops below 2^30, advertises a `WINDOW_UPDATE` back up to 2^31-1. A negative window is accepted, so a peer that overruns the window is never refused, and the window refills when data arrives rather than when it is consumed.\n\n**2. Stream-level window** (`lib/bandit/http2/stream.ex`)\n\nThe stream receive window is debited only when the stream process reads the DATA message, and it never rejects a violation either.\n\n**3. Accumulation**\n\nWhile `send_data/3` waits for send window, its selective `receive` matches only window updates and resets, so DATA messages pile up unread in the mailbox. A stream `WINDOW_UPDATE` of 1 byte, sent at intervals under the 15-second send timeout, keeps the stream alive while the attacker floods 1-byte DATA frames."
            }
          ],
          "value": "1. Connection-level window (lib/bandit/http2/connection.ex, lib/bandit/http2/flow_control.ex)\n\nThe handle_frame/3 clause for DATA frames sends the payload to the stream process before any window is checked. It then calls compute_recv_window/2, which subtracts the frame size and, once the window drops below 2^30, advertises a WINDOW_UPDATE back up to 2^31-1. A negative window is accepted, so a peer that overruns the window is never refused, and the window refills when data arrives rather than when it is consumed.\n\n2. Stream-level window (lib/bandit/http2/stream.ex)\n\nThe stream receive window is debited only when the stream process reads the DATA message, and it never rejects a violation either.\n\n3. Accumulation\n\nWhile send_data/3 waits for send window, its selective receive matches only window updates and resets, so DATA messages pile up unread in the mailbox. A stream WINDOW_UPDATE of 1 byte, sent at intervals under the 15-second send timeout, keeps the stream alive while the attacker floods 1-byte DATA frames."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-108525",
    "datePublished": "2026-10-26T13:29:42.520Z",
    "dateReserved": "2026-10-15T10:45:02.078Z",
    "dateUpdated": "2026-10-26T15:35:52.227Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-105660 (GCVE-0-2026-105660)
Vulnerability from cvelistv5
Published
2026-10-15 18:37
Modified
2026-10-16 20:02
CWE
  • CWE-1286 - Improper Validation of Syntactic Correctness of Input
Summary
Improper Validation of Syntactic Correctness of Input vulnerability in mtrudel bandit allows an unauthenticated remote attacker to set conn.host and conn.port to values outside the RFC 3986 host grammar via the authority of an HTTP/1 absolute-form or authority-form request target. Bandit validates the Host header and answers 400 when it is malformed, but when the request target carries its own authority that authority takes precedence, as RFC 9112 requires, and Bandit.Pipeline.determine_host_and_port!/4 in lib/bandit/pipeline.ex passes it to the application unchecked. A request line such as GET http://good.example@evil.example/x HTTP/1.1 with a valid Host header yields a conn.host of good.example@evil.example, and the same path admits NUL, DEL and non-ASCII bytes, invalid IP literals and ports outside 1..65535. The HTTP/1.0 clause needs no Host header at all, and CONNECT places the unparsed port string in conn.port. Applications that build absolute URLs from conn.host, such as through Plug.Conn.request_url/1 or Phoenix URL helpers, emit links whose authority the attacker chose. Message framing is unaffected, and no CR or LF can reach conn.host. This issue affects bandit: from 0.6.0 before 1.13.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.6.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 0e3edda0edf6322e9164b827c6d3ddecd080ff2e
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-105660",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-16T20:02:09.521673Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-16T20:02:38.784Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-v53v-9hvg-37jf"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.Pipeline\u0027",
            "\u0027Elixir.Bandit.HTTPTransport.Bandit.HTTP1.Socket\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/pipeline.ex",
            "lib/bandit/http1/socket.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.Pipeline\u0027:determine_host_and_port!/4"
            },
            {
              "name": "\u0027Elixir.Bandit.Pipeline\u0027:build_conn!/6"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTPTransport.Bandit.HTTP1.Socket\u0027:resolve_request_target!/2"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTPTransport.Bandit.HTTP1.Socket\u0027:validate_request_line!/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.13.0",
              "status": "affected",
              "version": "0.6.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.Pipeline\u0027",
            "\u0027Elixir.Bandit.HTTPTransport.Bandit.HTTP1.Socket\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/pipeline.ex",
            "lib/bandit/http1/socket.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.Pipeline\u0027:determine_host_and_port!/4"
            },
            {
              "name": "\u0027Elixir.Bandit.Pipeline\u0027:build_conn!/6"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTPTransport.Bandit.HTTP1.Socket\u0027:resolve_request_target!/2"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTPTransport.Bandit.HTTP1.Socket\u0027:validate_request_line!/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "6ea80210137b3f69caf65f67398934259dc365d0",
              "status": "affected",
              "version": "0e3edda0edf6322e9164b827c6d3ddecd080ff2e",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.13.0",
                  "versionStartIncluding": "0.6.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "coordinator",
          "value": "Jonatan M\u00e4nnchen / EEF"
        },
        {
          "lang": "en",
          "type": "finder",
          "value": "Eurico Nicacio"
        },
        {
          "lang": "en",
          "type": "reporter",
          "value": "Eurico Nicacio"
        }
      ],
      "dateAssigned": "2026-10-14T21:42:16.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eImproper Validation of Syntactic Correctness of Input vulnerability in mtrudel bandit allows an unauthenticated remote attacker to set \u003ccode\u003econn.host\u003c/code\u003e and \u003ccode\u003econn.port\u003c/code\u003e to values outside the RFC 3986 host grammar via the authority of an HTTP/1 absolute-form or authority-form request target. Bandit validates the \u003ccode\u003eHost\u003c/code\u003e header and answers 400 when it is malformed, but when the request target carries its own authority that authority takes precedence, as RFC 9112 requires, and \u003ccode\u003eBandit.Pipeline.determine_host_and_port!/4\u003c/code\u003e in \u003ccode\u003elib/bandit/pipeline.ex\u003c/code\u003e passes it to the application unchecked. A request line such as \u003ccode\u003eGET http://good.example@evil.example/x HTTP/1.1\u003c/code\u003e with a valid \u003ccode\u003eHost\u003c/code\u003e header yields a \u003ccode\u003econn.host\u003c/code\u003e of \u003ccode\u003egood.example@evil.example\u003c/code\u003e, and the same path admits NUL, DEL and non-ASCII bytes, invalid IP literals and ports outside 1..65535. The HTTP/1.0 clause needs no \u003ccode\u003eHost\u003c/code\u003e header at all, and \u003ccode\u003eCONNECT\u003c/code\u003e places the unparsed port string in \u003ccode\u003econn.port\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eApplications that build absolute URLs from \u003ccode\u003econn.host\u003c/code\u003e, such as through \u003ccode\u003ePlug.Conn.request_url/1\u003c/code\u003e or Phoenix URL helpers, emit links whose authority the attacker chose. Message framing is unaffected, and no CR or LF can reach \u003ccode\u003econn.host\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.6.0 before 1.13.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Improper Validation of Syntactic Correctness of Input vulnerability in mtrudel bandit allows an unauthenticated remote attacker to set `conn.host` and `conn.port` to values outside the RFC 3986 host grammar via the authority of an HTTP/1 absolute-form or authority-form request target. Bandit validates the `Host` header and answers 400 when it is malformed, but when the request target carries its own authority that authority takes precedence, as RFC 9112 requires, and `Bandit.Pipeline.determine_host_and_port!/4` in `lib/bandit/pipeline.ex` passes it to the application unchecked. A request line such as `GET http://good.example@evil.example/x HTTP/1.1` with a valid `Host` header yields a `conn.host` of `good.example@evil.example`, and the same path admits NUL, DEL and non-ASCII bytes, invalid IP literals and ports outside 1..65535. The HTTP/1.0 clause needs no `Host` header at all, and `CONNECT` places the unparsed port string in `conn.port`.\n\nApplications that build absolute URLs from `conn.host`, such as through `Plug.Conn.request_url/1` or Phoenix URL helpers, emit links whose authority the attacker chose. Message framing is unaffected, and no CR or LF can reach `conn.host`.\n\nThis issue affects bandit: from 0.6.0 before 1.13.0."
            }
          ],
          "value": "Improper Validation of Syntactic Correctness of Input vulnerability in mtrudel bandit allows an unauthenticated remote attacker to set conn.host and conn.port to values outside the RFC 3986 host grammar via the authority of an HTTP/1 absolute-form or authority-form request target. Bandit validates the Host header and answers 400 when it is malformed, but when the request target carries its own authority that authority takes precedence, as RFC 9112 requires, and Bandit.Pipeline.determine_host_and_port!/4 in lib/bandit/pipeline.ex passes it to the application unchecked. A request line such as GET http://good.example@evil.example/x HTTP/1.1 with a valid Host header yields a conn.host of good.example@evil.example, and the same path admits NUL, DEL and non-ASCII bytes, invalid IP literals and ports outside 1..65535. The HTTP/1.0 clause needs no Host header at all, and CONNECT places the unparsed port string in conn.port.\n\nApplications that build absolute URLs from conn.host, such as through Plug.Conn.request_url/1 or Phoenix URL helpers, emit links whose authority the attacker chose. Message framing is unaffected, and no CR or LF can reach conn.host.\n\nThis issue affects bandit: from 0.6.0 before 1.13.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-220",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eAn unauthenticated client can make the application see any host string and any port number as the request\u0027s authority, bypassing the validation applied to the \u003ccode\u003eHost\u003c/code\u003e header. An application that builds absolute URLs from \u003ccode\u003econn.host\u003c/code\u003e, such as password-reset or verification links, emits links whose host the attacker chose.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "An unauthenticated client can make the application see any host string and any port number as the request\u0027s authority, bypassing the validation applied to the `Host` header. An application that builds absolute URLs from `conn.host`, such as password-reset or verification links, emits links whose host the attacker chose."
                }
              ],
              "value": "An unauthenticated client can make the application see any host string and any port number as the request\u0027s authority, bypassing the validation applied to the Host header. An application that builds absolute URLs from conn.host, such as password-reset or verification links, emits links whose host the attacker chose."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 6.9,
            "baseSeverity": "MEDIUM",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
            "version": "4.0",
            "vulnAvailabilityImpact": "NONE",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "LOW",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-1286",
              "description": "CWE-1286 Improper Validation of Syntactic Correctness of Input",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-15T18:37:02.485Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "GitHub Advisory",
          "tags": [
            "related",
            "vendor-advisory"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-v53v-9hvg-37jf"
        },
        {
          "name": "EEF CNA record for CVE-2026-105660",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-105660.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-105660",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-105660"
        },
        {
          "name": "Introducing commit 0e3edda in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/0e3edda0edf6322e9164b827c6d3ddecd080ff2e"
        },
        {
          "name": "Fix commit 6ea8021 in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/6ea80210137b3f69caf65f67398934259dc365d0"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "Unvalidated HTTP/1 request-target authority lets a client choose conn.host and conn.port in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAdd a plug ahead of any code that reads \u003ccode\u003econn.host\u003c/code\u003e or \u003ccode\u003econn.port\u003c/code\u003e that compares \u003ccode\u003econn.host\u003c/code\u003e against an allow-list of the hostnames the application serves, checks that \u003ccode\u003econn.port\u003c/code\u003e is an integer, and halts the request with a 400 response otherwise. Do not derive absolute URLs from \u003ccode\u003econn.host\u003c/code\u003e until that check has passed.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Add a plug ahead of any code that reads `conn.host` or `conn.port` that compares `conn.host` against an allow-list of the hostnames the application serves, checks that `conn.port` is an integer, and halts the request with a 400 response otherwise. Do not derive absolute URLs from `conn.host` until that check has passed."
            }
          ],
          "value": "Add a plug ahead of any code that reads conn.host or conn.port that compares conn.host against an allow-list of the hostnames the application serves, checks that conn.port is an integer, and halts the request with a 400 response otherwise. Do not derive absolute URLs from conn.host until that check has passed."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003col\u003e\n\u003cli\u003eStart Bandit over plaintext HTTP with a plug that returns \u003ccode\u003econn.host\u003c/code\u003e and \u003ccode\u003econn.port\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eOpen a raw TCP connection to the listener and send \u003ccode\u003eGET http://good.example@evil.example/x HTTP/1.1\u003c/code\u003e followed by \u003ccode\u003eHost: good.example\u003c/code\u003e and an empty line. The response is 200 and \u003ccode\u003econn.host\u003c/code\u003e is \u003ccode\u003egood.example@evil.example\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eRepeat with \u003ccode\u003eGET http://ex\\x00am.com/x HTTP/1.1\u003c/code\u003e (a NUL byte in the authority) or \u003ccode\u003eGET http://ex.com:99999/x HTTP/1.1\u003c/code\u003e. Both are accepted, with the NUL byte in \u003ccode\u003econn.host\u003c/code\u003e and 99999 in \u003ccode\u003econn.port\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSend the same bytes in the \u003ccode\u003eHost\u003c/code\u003e header of an origin-form request instead. Bandit answers 400 for the NUL byte and the out-of-range port on 1.12.5, and for every payload once the \u003ccode\u003eHost\u003c/code\u003e grammar tightening on main is released.\u003c/li\u003e\n\u003cli\u003eSend \u003ccode\u003eGET http://good.example@evil.example/x HTTP/1.0\u003c/code\u003e with no \u003ccode\u003eHost\u003c/code\u003e header, or \u003ccode\u003eCONNECT good.example@evil.example:443 HTTP/1.1\u003c/code\u003e. Both are accepted, and the \u003ccode\u003eCONNECT\u003c/code\u003e form leaves \u003ccode\u003econn.port\u003c/code\u003e as the binary \u003ccode\u003e\"443\"\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "1. Start Bandit over plaintext HTTP with a plug that returns `conn.host` and `conn.port`.\n2. Open a raw TCP connection to the listener and send `GET http://good.example@evil.example/x HTTP/1.1` followed by `Host: good.example` and an empty line. The response is 200 and `conn.host` is `good.example@evil.example`.\n3. Repeat with `GET http://ex\\x00am.com/x HTTP/1.1` (a NUL byte in the authority) or `GET http://ex.com:99999/x HTTP/1.1`. Both are accepted, with the NUL byte in `conn.host` and 99999 in `conn.port`.\n4. Send the same bytes in the `Host` header of an origin-form request instead. Bandit answers 400 for the NUL byte and the out-of-range port on 1.12.5, and for every payload once the `Host` grammar tightening on main is released.\n5. Send `GET http://good.example@evil.example/x HTTP/1.0` with no `Host` header, or `CONNECT good.example@evil.example:443 HTTP/1.1`. Both are accepted, and the `CONNECT` form leaves `conn.port` as the binary `\"443\"`."
            }
          ],
          "value": "* Start Bandit over plaintext HTTP with a plug that returns conn.host and conn.port.\n* Open a raw TCP connection to the listener and send GET http://good.example@evil.example/x HTTP/1.1 followed by Host: good.example and an empty line. The response is 200 and conn.host is good.example@evil.example.\n* Repeat with GET http://ex\\x00am.com/x HTTP/1.1 (a NUL byte in the authority) or GET http://ex.com:99999/x HTTP/1.1. Both are accepted, with the NUL byte in conn.host and 99999 in conn.port.\n* Send the same bytes in the Host header of an origin-form request instead. Bandit answers 400 for the NUL byte and the out-of-range port on 1.12.5, and for every payload once the Host grammar tightening on main is released.\n* Send GET http://good.example@evil.example/x HTTP/1.0 with no Host header, or CONNECT good.example@evil.example:443 HTTP/1.1. Both are accepted, and the CONNECT form leaves conn.port as the binary \"443\"."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Request-target parsing\u003c/strong\u003e (\u003ccode\u003elib/bandit/http1/socket.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e:erlang.decode_packet/3\u003c/code\u003e returns an absolute-form target as \u003ccode\u003e{:absoluteURI, scheme, host, port, path}\u003c/code\u003e and an authority-form (\u003ccode\u003eCONNECT\u003c/code\u003e) target as \u003ccode\u003e{:scheme, host, port}\u003c/code\u003e. \u003ccode\u003eresolve_request_target!/2\u003c/code\u003e unwraps both into a \u003ccode\u003e{scheme, host, port, path}\u003c/code\u003e tuple and forwards \u003ccode\u003ehost\u003c/code\u003e and \u003ccode\u003eport\u003c/code\u003e as received. \u003ccode\u003evalidate_request_line!/2\u003c/code\u003e only rejects bytes at or below \u003ccode\u003e0x20\u003c/code\u003e and \u003ccode\u003e0x7F\u003c/code\u003e, so userinfo, delimiters, backslashes, bytes \u003ccode\u003e0x80\u003c/code\u003e to \u003ccode\u003e0xFF\u003c/code\u003e, invalid IP literals and out-of-range ports pass. For \u003ccode\u003eCONNECT\u003c/code\u003e, \u003ccode\u003eport\u003c/code\u003e is the unparsed text after the first colon.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Host selection\u003c/strong\u003e (\u003ccode\u003elib/bandit/pipeline.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edetermine_host_and_port!/4\u003c/code\u003e has three clauses. The HTTP/1.1 clause calls \u003ccode\u003erequired_host_header!/1\u003c/code\u003e, which parses the \u003ccode\u003eHost\u003c/code\u003e header with \u003ccode\u003eBandit.Headers.parse_hostlike_header!/1\u003c/code\u003e and raises a 400 on a malformed value, and then discards that result whenever \u003ccode\u003etarget_host\u003c/code\u003e is set, returning \u003ccode\u003eto_string(target_host)\u003c/code\u003e and \u003ccode\u003etarget_port\u003c/code\u003e instead. The fallback clause used for HTTP/1.0 and for \u003ccode\u003eCONNECT\u003c/code\u003e returns the target host and port directly and never consults the \u003ccode\u003eHost\u003c/code\u003e header. The precedence itself follows RFC 9112 section 3.2.2; the defect is that the value which wins is the one never validated.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Effect\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ebuild_conn!/6\u003c/code\u003e places the pair into the \u003ccode\u003eURI\u003c/code\u003e from which \u003ccode\u003ePlug.Conn.Adapter.conn/5\u003c/code\u003e builds \u003ccode\u003econn.host\u003c/code\u003e and \u003ccode\u003econn.port\u003c/code\u003e. The application therefore sees host values Bandit rejects on every other route into the same field, and for \u003ccode\u003eCONNECT\u003c/code\u003e a binary where \u003ccode\u003ePlug.Conn.port_number/0\u003c/code\u003e promises an integer, which makes \u003ccode\u003ePlug.Conn.request_url/1\u003c/code\u003e raise.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Request-target parsing** (`lib/bandit/http1/socket.ex`)\n\n`:erlang.decode_packet/3` returns an absolute-form target as `{:absoluteURI, scheme, host, port, path}` and an authority-form (`CONNECT`) target as `{:scheme, host, port}`. `resolve_request_target!/2` unwraps both into a `{scheme, host, port, path}` tuple and forwards `host` and `port` as received. `validate_request_line!/2` only rejects bytes at or below `0x20` and `0x7F`, so userinfo, delimiters, backslashes, bytes `0x80` to `0xFF`, invalid IP literals and out-of-range ports pass. For `CONNECT`, `port` is the unparsed text after the first colon.\n\n**2. Host selection** (`lib/bandit/pipeline.ex`)\n\n`determine_host_and_port!/4` has three clauses. The HTTP/1.1 clause calls `required_host_header!/1`, which parses the `Host` header with `Bandit.Headers.parse_hostlike_header!/1` and raises a 400 on a malformed value, and then discards that result whenever `target_host` is set, returning `to_string(target_host)` and `target_port` instead. The fallback clause used for HTTP/1.0 and for `CONNECT` returns the target host and port directly and never consults the `Host` header. The precedence itself follows RFC 9112 section 3.2.2; the defect is that the value which wins is the one never validated.\n\n**3. Effect**\n\n`build_conn!/6` places the pair into the `URI` from which `Plug.Conn.Adapter.conn/5` builds `conn.host` and `conn.port`. The application therefore sees host values Bandit rejects on every other route into the same field, and for `CONNECT` a binary where `Plug.Conn.port_number/0` promises an integer, which makes `Plug.Conn.request_url/1` raise."
            }
          ],
          "value": "1. Request-target parsing (lib/bandit/http1/socket.ex)\n\n:erlang.decode_packet/3 returns an absolute-form target as {:absoluteURI, scheme, host, port, path} and an authority-form (CONNECT) target as {:scheme, host, port}. resolve_request_target!/2 unwraps both into a {scheme, host, port, path} tuple and forwards host and port as received. validate_request_line!/2 only rejects bytes at or below 0x20 and 0x7F, so userinfo, delimiters, backslashes, bytes 0x80 to 0xFF, invalid IP literals and out-of-range ports pass. For CONNECT, port is the unparsed text after the first colon.\n\n2. Host selection (lib/bandit/pipeline.ex)\n\ndetermine_host_and_port!/4 has three clauses. The HTTP/1.1 clause calls required_host_header!/1, which parses the Host header with Bandit.Headers.parse_hostlike_header!/1 and raises a 400 on a malformed value, and then discards that result whenever target_host is set, returning to_string(target_host) and target_port instead. The fallback clause used for HTTP/1.0 and for CONNECT returns the target host and port directly and never consults the Host header. The precedence itself follows RFC 9112 section 3.2.2; the defect is that the value which wins is the one never validated.\n\n3. Effect\n\nbuild_conn!/6 places the pair into the URI from which Plug.Conn.Adapter.conn/5 builds conn.host and conn.port. The application therefore sees host values Bandit rejects on every other route into the same field, and for CONNECT a binary where Plug.Conn.port_number/0 promises an integer, which makes Plug.Conn.request_url/1 raise."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-105660",
    "datePublished": "2026-10-15T18:37:02.485Z",
    "dateReserved": "2026-10-10T13:15:02.320Z",
    "dateUpdated": "2026-10-16T20:02:38.784Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-105659 (GCVE-0-2026-105659)
Vulnerability from cvelistv5
Published
2026-10-14 16:54
Modified
2026-10-16 20:07
CWE
  • CWE-400 - Uncontrolled Resource Consumption
Summary
Uncontrolled Resource Consumption vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin HTTP/2 stream processes indefinitely via a trickle of small WINDOW_UPDATE frames. A stream whose response outruns the connection or stream send window blocks until the peer grants more window, and each block carries a 15 second deadline. Both deadlines restart whenever the peer makes partial progress. Bandit.HTTP2.Connection.finish_data/5 re-queues a partially advanced send in pending_sends with a fresh expires_at, so every WINDOW_UPDATE on stream 0, and every SETTINGS frame, resets the clock. Bandit.HTTP2.Stream.send_data/3 recurses on each stream WINDOW_UPDATE and re-arms a fresh after read_timeout. A peer that grants a few bytes every few seconds keeps the blocked stream process, its Plug state and any resource it holds mid-response alive for as long as it keeps dripping, and repeats this across streams and connections to exhaust server processes and memory. The two paths are independent. The stream-level block is reached on its own by widening the connection window with a large WINDOW_UPDATE on stream 0 and shrinking the per-stream window with a small SETTINGS_INITIAL_WINDOW_SIZE, so closing only the connection-level path leaves it exploitable. This issue affects bandit: from 1.4.0 before 1.13.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.4.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: fff06efe3be962b484bd4d3eb339372fda5a231f
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-105659",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-16T20:06:31.239514Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-10-16T20:07:01.914Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-69m6-wcv4-rwcf"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Connection\u0027",
            "\u0027Elixir.Bandit.HTTP2.Stream\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/connection.ex",
            "lib/bandit/http2/stream.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:finish_data/5"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:do_pending_sends/2"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:handle_frame/3"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Stream\u0027:send_data/3"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.13.0",
              "status": "affected",
              "version": "1.4.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Connection\u0027",
            "\u0027Elixir.Bandit.HTTP2.Stream\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/connection.ex",
            "lib/bandit/http2/stream.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:finish_data/5"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:do_pending_sends/2"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:handle_frame/3"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Stream\u0027:send_data/3"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "d88b21953c65061f21136ea76a89a4f95a2ac7a1",
              "status": "affected",
              "version": "fff06efe3be962b484bd4d3eb339372fda5a231f",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.13.0",
                  "versionStartIncluding": "1.4.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Thomas Cioppettini"
        },
        {
          "lang": "en",
          "type": "reporter",
          "value": "Thomas Cioppettini"
        },
        {
          "lang": "en",
          "type": "coordinator",
          "value": "Jonatan M\u00e4nnchen / EEF"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        }
      ],
      "dateAssigned": "2026-10-12T16:13:25.000Z",
      "datePublic": "2026-10-14T16:54:46.000Z",
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eUncontrolled Resource Consumption vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin HTTP/2 stream processes indefinitely via a trickle of small \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e frames. A stream whose response outruns the connection or stream send window blocks until the peer grants more window, and each block carries a 15 second deadline. Both deadlines restart whenever the peer makes partial progress. \u003ccode\u003eBandit.HTTP2.Connection.finish_data/5\u003c/code\u003e re-queues a partially advanced send in \u003ccode\u003epending_sends\u003c/code\u003e with a fresh \u003ccode\u003eexpires_at\u003c/code\u003e, so every \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e on stream 0, and every \u003ccode\u003eSETTINGS\u003c/code\u003e frame, resets the clock. \u003ccode\u003eBandit.HTTP2.Stream.send_data/3\u003c/code\u003e recurses on each stream \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e and re-arms a fresh \u003ccode\u003eafter read_timeout\u003c/code\u003e. A peer that grants a few bytes every few seconds keeps the blocked stream process, its Plug state and any resource it holds mid-response alive for as long as it keeps dripping, and repeats this across streams and connections to exhaust server processes and memory.\u003c/p\u003e\n\u003cp\u003eThe two paths are independent. The stream-level block is reached on its own by widening the connection window with a large \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e on stream 0 and shrinking the per-stream window with a small \u003ccode\u003eSETTINGS_INITIAL_WINDOW_SIZE\u003c/code\u003e, so closing only the connection-level path leaves it exploitable.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 1.4.0 before 1.13.0.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Uncontrolled Resource Consumption vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin HTTP/2 stream processes indefinitely via a trickle of small `WINDOW_UPDATE` frames. A stream whose response outruns the connection or stream send window blocks until the peer grants more window, and each block carries a 15 second deadline. Both deadlines restart whenever the peer makes partial progress. `Bandit.HTTP2.Connection.finish_data/5` re-queues a partially advanced send in `pending_sends` with a fresh `expires_at`, so every `WINDOW_UPDATE` on stream 0, and every `SETTINGS` frame, resets the clock. `Bandit.HTTP2.Stream.send_data/3` recurses on each stream `WINDOW_UPDATE` and re-arms a fresh `after read_timeout`. A peer that grants a few bytes every few seconds keeps the blocked stream process, its Plug state and any resource it holds mid-response alive for as long as it keeps dripping, and repeats this across streams and connections to exhaust server processes and memory.\n\nThe two paths are independent. The stream-level block is reached on its own by widening the connection window with a large `WINDOW_UPDATE` on stream 0 and shrinking the per-stream window with a small `SETTINGS_INITIAL_WINDOW_SIZE`, so closing only the connection-level path leaves it exploitable.\n\nThis issue affects bandit: from 1.4.0 before 1.13.0."
            }
          ],
          "value": "Uncontrolled Resource Consumption vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin HTTP/2 stream processes indefinitely via a trickle of small WINDOW_UPDATE frames. A stream whose response outruns the connection or stream send window blocks until the peer grants more window, and each block carries a 15 second deadline. Both deadlines restart whenever the peer makes partial progress. Bandit.HTTP2.Connection.finish_data/5 re-queues a partially advanced send in pending_sends with a fresh expires_at, so every WINDOW_UPDATE on stream 0, and every SETTINGS frame, resets the clock. Bandit.HTTP2.Stream.send_data/3 recurses on each stream WINDOW_UPDATE and re-arms a fresh after read_timeout. A peer that grants a few bytes every few seconds keeps the blocked stream process, its Plug state and any resource it holds mid-response alive for as long as it keeps dripping, and repeats this across streams and connections to exhaust server processes and memory.\n\nThe two paths are independent. The stream-level block is reached on its own by widening the connection window with a large WINDOW_UPDATE on stream 0 and shrinking the per-stream window with a small SETTINGS_INITIAL_WINDOW_SIZE, so closing only the connection-level path leaves it exploitable.\n\nThis issue affects bandit: from 1.4.0 before 1.13.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-227",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eA remote, unauthenticated peer requests a response larger than a send window, then drips tiny \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e frames every few seconds. Each blocked stream process, with its Plug state and any upstream connection or file handle it holds, stays alive for as long as the trickle continues. Repeated across many streams and connections this exhausts server processes and memory.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "A remote, unauthenticated peer requests a response larger than a send window, then drips tiny `WINDOW_UPDATE` frames every few seconds. Each blocked stream process, with its Plug state and any upstream connection or file handle it holds, stays alive for as long as the trickle continues. Repeated across many streams and connections this exhausts server processes and memory."
                }
              ],
              "value": "A remote, unauthenticated peer requests a response larger than a send window, then drips tiny WINDOW_UPDATE frames every few seconds. Each blocked stream process, with its Plug state and any upstream connection or file handle it holds, stays alive for as long as the trickle continues. Repeated across many streams and connections this exhausts server processes and memory."
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-400",
              "description": "CWE-400 Uncontrolled Resource Consumption",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-15T18:37:59.434Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "name": "GitHub Advisory",
          "tags": [
            "related",
            "vendor-advisory"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-69m6-wcv4-rwcf"
        },
        {
          "name": "EEF CNA record for CVE-2026-105659",
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-105659.html"
        },
        {
          "name": "OSV record EEF-CVE-2026-105659",
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-105659"
        },
        {
          "name": "Introducing commit fff06ef in mtrudel/bandit",
          "tags": [
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/fff06efe3be962b484bd4d3eb339372fda5a231f"
        },
        {
          "name": "Fix commit d88b219 in mtrudel/bandit",
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/d88b21953c65061f21136ea76a89a4f95a2ac7a1"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "HTTP/2 blocked-send deadlines reset on every WINDOW_UPDATE, letting a trickle pin stream processes indefinitely in Bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eDisable HTTP/2 with \u003ccode\u003ehttp_2_options: [enabled: false]\u003c/code\u003e, which removes both vulnerable paths. Alternatively, terminate HTTP/2 at a reverse proxy that enforces its own send timeouts and speaks HTTP/1 to Bandit.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Disable HTTP/2 with `http_2_options: [enabled: false]`, which removes both vulnerable paths. Alternatively, terminate HTTP/2 at a reverse proxy that enforces its own send timeouts and speaks HTTP/1 to Bandit."
            }
          ],
          "value": "Disable HTTP/2 with http_2_options: [enabled: false], which removes both vulnerable paths. Alternatively, terminate HTTP/2 at a reverse proxy that enforces its own send timeouts and speaks HTTP/1 to Bandit."
        }
      ],
      "x_proofOfConcept": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eConnection-level path:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eOpen a stream whose response exceeds the connection send window (65,535 bytes by default).\u003c/li\u003e\n\u003cli\u003eThe server sends the window\u0027s worth and queues the remainder in \u003ccode\u003epending_sends\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSend \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e for stream 0 with an increment of 1 every few seconds, or an empty \u003ccode\u003eSETTINGS\u003c/code\u003e frame. Each one re-queues the remainder with a fresh deadline, so \u003ccode\u003eexpire_pending_sends/1\u003c/code\u003e never expires it.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eStream-level path:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eSend a large \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e for stream 0 so the connection window never blocks.\u003c/li\u003e\n\u003cli\u003eAdvertise a small \u003ccode\u003eSETTINGS_INITIAL_WINDOW_SIZE\u003c/code\u003e so each stream window is tiny.\u003c/li\u003e\n\u003cli\u003eRequest a response larger than the stream window. The server sends the window\u0027s worth and blocks in the recursive \u003ccode\u003ereceive\u003c/code\u003e in \u003ccode\u003esend_data/3\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSend \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e for the stream with an increment of 1 at intervals shorter than 15 seconds. Each one re-arms a fresh \u003ccode\u003eread_timeout\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eRepeat across streams, up to \u003ccode\u003emax_concurrent_streams\u003c/code\u003e per connection, and across connections.\u003c/li\u003e\n\u003c/ol\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Connection-level path:\n\n1. Open a stream whose response exceeds the connection send window (65,535 bytes by default).\n2. The server sends the window\u0027s worth and queues the remainder in `pending_sends`.\n3. Send `WINDOW_UPDATE` for stream 0 with an increment of 1 every few seconds, or an empty `SETTINGS` frame. Each one re-queues the remainder with a fresh deadline, so `expire_pending_sends/1` never expires it.\n\nStream-level path:\n\n1. Send a large `WINDOW_UPDATE` for stream 0 so the connection window never blocks.\n2. Advertise a small `SETTINGS_INITIAL_WINDOW_SIZE` so each stream window is tiny.\n3. Request a response larger than the stream window. The server sends the window\u0027s worth and blocks in the recursive `receive` in `send_data/3`.\n4. Send `WINDOW_UPDATE` for the stream with an increment of 1 at intervals shorter than 15 seconds. Each one re-arms a fresh `read_timeout`.\n5. Repeat across streams, up to `max_concurrent_streams` per connection, and across connections."
            }
          ],
          "value": "Connection-level path:\n\n* Open a stream whose response exceeds the connection send window (65,535 bytes by default).\n* The server sends the window\u0027s worth and queues the remainder in pending_sends.\n* Send WINDOW_UPDATE for stream 0 with an increment of 1 every few seconds, or an empty SETTINGS frame. Each one re-queues the remainder with a fresh deadline, so expire_pending_sends/1 never expires it.\n\nStream-level path:\n\n* Send a large WINDOW_UPDATE for stream 0 so the connection window never blocks.\n* Advertise a small SETTINGS_INITIAL_WINDOW_SIZE so each stream window is tiny.\n* Request a response larger than the stream window. The server sends the window\u0027s worth and blocks in the recursive receive in send_data/3.\n* Send WINDOW_UPDATE for the stream with an increment of 1 at intervals shorter than 15 seconds. Each one re-arms a fresh read_timeout.\n* Repeat across streams, up to max_concurrent_streams per connection, and across connections."
        }
      ],
      "x_technicalAnalysis": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003e\u003cstrong\u003e1. Connection-level block\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/connection.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003eA send that outruns the connection send window is queued in \u003ccode\u003epending_sends\u003c/code\u003e with an \u003ccode\u003eexpires_at\u003c/code\u003e deadline computed in \u003ccode\u003efinish_data/5\u003c/code\u003e as now plus \u003ccode\u003e@pending_send_timeout\u003c/code\u003e (15 seconds). \u003ccode\u003eexpire_pending_sends/1\u003c/code\u003e drops any queued send whose deadline has passed whenever data arrives on the connection. \u003ccode\u003edo_pending_sends/2\u003c/code\u003e runs on every \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e on stream 0 and on every \u003ccode\u003eSETTINGS\u003c/code\u003e frame (including an empty one, through \u003ccode\u003ehandle_frame/3\u003c/code\u003e). When the granted window is smaller than the queued remainder, the leftover is re-queued through \u003ccode\u003efinish_data/5\u003c/code\u003e, which recomputes \u003ccode\u003eexpires_at\u003c/code\u003e from the current time instead of preserving the original deadline. Each partial advance therefore restarts the timer, and an empty \u003ccode\u003eSETTINGS\u003c/code\u003e frame restarts it without granting any window at all.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Stream-level block\u003c/strong\u003e (\u003ccode\u003elib/bandit/http2/stream.ex\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003esend_data/3\u003c/code\u003e runs in the stream process. When the stream send window is exhausted it enters a \u003ccode\u003ereceive\u003c/code\u003e that waits for \u003ccode\u003e{:bandit, {:send_window_update, delta}}\u003c/code\u003e with \u003ccode\u003eafter stream.read_timeout\u003c/code\u003e (the struct default of 15 seconds, never overridden in \u003ccode\u003einit/5\u003c/code\u003e). On each stream \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e it applies the delta and recurses into \u003ccode\u003esend_data/3\u003c/code\u003e with the remainder, which re-enters the same \u003ccode\u003ereceive\u003c/code\u003e with a fresh \u003ccode\u003eafter\u003c/code\u003e timeout. Each partial advance restarts the 15 second wait.\u003c/p\u003e\n\u003cp\u003eFor the stream-level block to be the one stuck, the stream window must be exhausted while the connection window has room. The peer controls both: a large \u003ccode\u003eWINDOW_UPDATE\u003c/code\u003e on stream 0 opens the connection window, and a small \u003ccode\u003eSETTINGS_INITIAL_WINDOW_SIZE\u003c/code\u003e shrinks every stream window. Closing only the connection-level path therefore leaves the stream-level path fully exploitable.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Effect\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe trickle also keeps the transport-level read timeout from firing, since the connection continuously receives valid frames. The blocked stream process, its Plug state, and anything it holds mid-response stay retained for as long as the peer keeps dripping, instead of being released after 15 seconds.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "**1. Connection-level block** (`lib/bandit/http2/connection.ex`)\n\nA send that outruns the connection send window is queued in `pending_sends` with an `expires_at` deadline computed in `finish_data/5` as now plus `@pending_send_timeout` (15 seconds). `expire_pending_sends/1` drops any queued send whose deadline has passed whenever data arrives on the connection. `do_pending_sends/2` runs on every `WINDOW_UPDATE` on stream 0 and on every `SETTINGS` frame (including an empty one, through `handle_frame/3`). When the granted window is smaller than the queued remainder, the leftover is re-queued through `finish_data/5`, which recomputes `expires_at` from the current time instead of preserving the original deadline. Each partial advance therefore restarts the timer, and an empty `SETTINGS` frame restarts it without granting any window at all.\n\n**2. Stream-level block** (`lib/bandit/http2/stream.ex`)\n\n`send_data/3` runs in the stream process. When the stream send window is exhausted it enters a `receive` that waits for `{:bandit, {:send_window_update, delta}}` with `after stream.read_timeout` (the struct default of 15 seconds, never overridden in `init/5`). On each stream `WINDOW_UPDATE` it applies the delta and recurses into `send_data/3` with the remainder, which re-enters the same `receive` with a fresh `after` timeout. Each partial advance restarts the 15 second wait.\n\nFor the stream-level block to be the one stuck, the stream window must be exhausted while the connection window has room. The peer controls both: a large `WINDOW_UPDATE` on stream 0 opens the connection window, and a small `SETTINGS_INITIAL_WINDOW_SIZE` shrinks every stream window. Closing only the connection-level path therefore leaves the stream-level path fully exploitable.\n\n**3. Effect**\n\nThe trickle also keeps the transport-level read timeout from firing, since the connection continuously receives valid frames. The blocked stream process, its Plug state, and anything it holds mid-response stay retained for as long as the peer keeps dripping, instead of being released after 15 seconds."
            }
          ],
          "value": "1. Connection-level block (lib/bandit/http2/connection.ex)\n\nA send that outruns the connection send window is queued in pending_sends with an expires_at deadline computed in finish_data/5 as now plus @pending_send_timeout (15 seconds). expire_pending_sends/1 drops any queued send whose deadline has passed whenever data arrives on the connection. do_pending_sends/2 runs on every WINDOW_UPDATE on stream 0 and on every SETTINGS frame (including an empty one, through handle_frame/3). When the granted window is smaller than the queued remainder, the leftover is re-queued through finish_data/5, which recomputes expires_at from the current time instead of preserving the original deadline. Each partial advance therefore restarts the timer, and an empty SETTINGS frame restarts it without granting any window at all.\n\n2. Stream-level block (lib/bandit/http2/stream.ex)\n\nsend_data/3 runs in the stream process. When the stream send window is exhausted it enters a receive that waits for {:bandit, {:send_window_update, delta}} with after stream.read_timeout (the struct default of 15 seconds, never overridden in init/5). On each stream WINDOW_UPDATE it applies the delta and recurses into send_data/3 with the remainder, which re-enters the same receive with a fresh after timeout. Each partial advance restarts the 15 second wait.\n\nFor the stream-level block to be the one stuck, the stream window must be exhausted while the connection window has room. The peer controls both: a large WINDOW_UPDATE on stream 0 opens the connection window, and a small SETTINGS_INITIAL_WINDOW_SIZE shrinks every stream window. Closing only the connection-level path therefore leaves the stream-level path fully exploitable.\n\n3. Effect\n\nThe trickle also keeps the transport-level read timeout from firing, since the connection continuously receives valid frames. The blocked stream process, its Plug state, and anything it holds mid-response stay retained for as long as the peer keeps dripping, instead of being released after 15 seconds."
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-105659",
    "datePublished": "2026-10-14T16:54:46.484Z",
    "dateReserved": "2026-10-12T10:00:01.326Z",
    "dateUpdated": "2026-10-16T20:07:01.914Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-75484 (GCVE-0-2026-75484)
Vulnerability from cvelistv5
Published
2026-08-20 21:11
Modified
2026-08-21 19:52
CWE
  • CWE-93 - Improper Neutralization of CRLF Sequences ('CRLF Injection')
Summary
Improper Neutralization of CRLF Sequences ('CRLF Injection') vulnerability in mtrudel bandit allows an unauthenticated remote attacker to smuggle CR, LF, or NUL characters into application-visible request headers via HTTP/2. Bandit.HTTP2.Stream.read_headers/1 validates pseudo-header placement and uniqueness, header-name casing, connection-specific headers, the te value, and content-length, but never checks field values. Because HPACK carries arbitrary octets, a HEADERS block whose field values contain \r, \n, or \0 decodes without error and the values land in conn.req_headers unchanged. The HTTP/1 path already rejects the same octets; HTTP/2 did not. Bandit itself is not a sink for the injected bytes: its own logging uses fixed strings or inspect, and HTTP/2 response headers are HPACK-encoded and separately rejected by Plug's put_resp_header, so response splitting is not reachable through this path. The risk is entirely in how a downstream application consumes header values, such as appending one verbatim to a plain-text log or concatenating it into an upstream request. A related gap bundled in the same fix: only :method, :scheme, and :path were checked for at most one occurrence; a duplicate :authority pseudo-header was accepted, with the first instance silently winning as conn.host while a conflicting value remained visible to the application. This issue affects bandit: from 1.4.0 before 1.12.5.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.4.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: fff06efe3be962b484bd4d3eb339372fda5a231f
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-75484",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-08-21T19:52:23.602062Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-08-21T19:52:42.954Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-x3gh-xhj4-3vq8"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Stream\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/stream.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Stream\u0027:read_headers/1"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.12.5",
              "status": "affected",
              "version": "1.4.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Stream\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/stream.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Stream\u0027:read_headers/1"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "d38cf046c9a3cae4d0f88001c2ceb4143f86366b",
              "status": "affected",
              "version": "fff06efe3be962b484bd4d3eb339372fda5a231f",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.12.5",
                  "versionStartIncluding": "1.4.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "coordinator",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eImproper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027) vulnerability in mtrudel bandit allows an unauthenticated remote attacker to smuggle CR, LF, or NUL characters into application-visible request headers via HTTP/2. \u003ccode\u003eBandit.HTTP2.Stream.read_headers/1\u003c/code\u003e validates pseudo-header placement and uniqueness, header-name casing, connection-specific headers, the \u003ccode\u003ete\u003c/code\u003e value, and \u003ccode\u003econtent-length\u003c/code\u003e, but never checks field \u003cem\u003evalues\u003c/em\u003e. Because HPACK carries arbitrary octets, a HEADERS block whose field values contain \u003ccode\u003e\\r\u003c/code\u003e, \u003ccode\u003e\\n\u003c/code\u003e, or \u003ccode\u003e\\0\u003c/code\u003e decodes without error and the values land in \u003ccode\u003econn.req_headers\u003c/code\u003e unchanged. The HTTP/1 path already rejects the same octets; HTTP/2 did not.\u003c/p\u003e\n\u003cp\u003eBandit itself is not a sink for the injected bytes: its own logging uses fixed strings or \u003ccode\u003einspect\u003c/code\u003e, and HTTP/2 response headers are HPACK-encoded and separately rejected by Plug\u0027s \u003ccode\u003eput_resp_header\u003c/code\u003e, so response splitting is not reachable through this path. The risk is entirely in how a downstream application consumes header values, such as appending one verbatim to a plain-text log or concatenating it into an upstream request. A related gap bundled in the same fix: only \u003ccode\u003e:method\u003c/code\u003e, \u003ccode\u003e:scheme\u003c/code\u003e, and \u003ccode\u003e:path\u003c/code\u003e were checked for at most one occurrence; a duplicate \u003ccode\u003e:authority\u003c/code\u003e pseudo-header was accepted, with the first instance silently winning as \u003ccode\u003econn.host\u003c/code\u003e while a conflicting value remained visible to the application.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 1.4.0 before 1.12.5.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027) vulnerability in mtrudel bandit allows an unauthenticated remote attacker to smuggle CR, LF, or NUL characters into application-visible request headers via HTTP/2. `Bandit.HTTP2.Stream.read_headers/1` validates pseudo-header placement and uniqueness, header-name casing, connection-specific headers, the `te` value, and `content-length`, but never checks field *values*. Because HPACK carries arbitrary octets, a HEADERS block whose field values contain `\\r`, `\\n`, or `\\0` decodes without error and the values land in `conn.req_headers` unchanged. The HTTP/1 path already rejects the same octets; HTTP/2 did not.\n\nBandit itself is not a sink for the injected bytes: its own logging uses fixed strings or `inspect`, and HTTP/2 response headers are HPACK-encoded and separately rejected by Plug\u0027s `put_resp_header`, so response splitting is not reachable through this path. The risk is entirely in how a downstream application consumes header values, such as appending one verbatim to a plain-text log or concatenating it into an upstream request. A related gap bundled in the same fix: only `:method`, `:scheme`, and `:path` were checked for at most one occurrence; a duplicate `:authority` pseudo-header was accepted, with the first instance silently winning as `conn.host` while a conflicting value remained visible to the application.\n\nThis issue affects bandit: from 1.4.0 before 1.12.5."
            }
          ],
          "value": "Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027) vulnerability in mtrudel bandit allows an unauthenticated remote attacker to smuggle CR, LF, or NUL characters into application-visible request headers via HTTP/2. Bandit.HTTP2.Stream.read_headers/1 validates pseudo-header placement and uniqueness, header-name casing, connection-specific headers, the te value, and content-length, but never checks field values. Because HPACK carries arbitrary octets, a HEADERS block whose field values contain \\r, \\n, or \\0 decodes without error and the values land in conn.req_headers unchanged. The HTTP/1 path already rejects the same octets; HTTP/2 did not.\n\nBandit itself is not a sink for the injected bytes: its own logging uses fixed strings or inspect, and HTTP/2 response headers are HPACK-encoded and separately rejected by Plug\u0027s put_resp_header, so response splitting is not reachable through this path. The risk is entirely in how a downstream application consumes header values, such as appending one verbatim to a plain-text log or concatenating it into an upstream request. A related gap bundled in the same fix: only :method, :scheme, and :path were checked for at most one occurrence; a duplicate :authority pseudo-header was accepted, with the first instance silently winning as conn.host while a conflicting value remained visible to the application.\n\nThis issue affects bandit: from 1.4.0 before 1.12.5."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-81",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-81 Web Server Logs Tampering"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 6.9,
            "baseSeverity": "MEDIUM",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
            "version": "4.0",
            "vulnAvailabilityImpact": "NONE",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "LOW",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-93",
              "description": "CWE-93 Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027)",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-08-20T21:11:27.523Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-x3gh-xhj4-3vq8"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-75484.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-75484"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/d38cf046c9a3cae4d0f88001c2ceb4143f86366b"
        }
      ],
      "source": {
        "discovery": "UNKNOWN"
      },
      "title": "HTTP/2 header field values containing CR, LF or NUL are passed to the application unvalidated in Bandit"
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-75484",
    "datePublished": "2026-08-20T21:11:27.523Z",
    "dateReserved": "2026-08-18T10:30:01.764Z",
    "dateUpdated": "2026-08-21T19:52:42.954Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-74836 (GCVE-0-2026-74836)
Vulnerability from cvelistv5
Published
2026-08-20 21:11
Modified
2026-08-21 19:55
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin an unbounded number of HTTP/2 stream processes indefinitely via connection-level flow control. When a stream's response body outruns the HTTP/2 connection-level send window (default 65,535 bytes, shared across all streams on the connection), Bandit.HTTP2.Connection queues the remaining bytes and a reply closure in pending_sends and the stream process blocks forever inside a synchronous call to the connection process. Nothing bounds that wait and nothing purges the queue: a client RST_STREAM for the blocked stream is delivered to its mailbox but never read while it is stuck inside the call, so cancelling frees nothing, and periodic PING frames keep the transport-level read timeout from ever firing. The equivalent block on the stream-level send window is already bounded at 15 seconds; the connection-level path had no such bound. Each stalled stream pins its process, Plug state, and any resource the Plug holds across the blocked write, such as a pooled upstream connection in a reverse-proxy Plug. The attacker chooses any endpoint whose response exceeds the connection window (common for most non-trivial payloads), grants a generous stream-level window so only the connection window limits it, and keeps the connection alive with periodic PINGs; the primitive is repeatable across streams and connections at the cost of one idle socket each. This issue affects bandit: from 0.3.4 before 1.12.5.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.3.4   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 6feadb31189d18b7dcda6c663112cd3bbaf63a46
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-74836",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-08-21T19:54:42.335290Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-08-21T19:55:01.929Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-xj8g-532w-jv94"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Connection\u0027",
            "\u0027Elixir.Bandit.HTTP2.Stream\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/connection.ex",
            "lib/bandit/http2/stream.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:finish_data/5"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:handle_frame/3"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Stream\u0027:send_data/3"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.12.5",
              "status": "affected",
              "version": "0.3.4",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Connection\u0027",
            "\u0027Elixir.Bandit.HTTP2.Stream\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/connection.ex",
            "lib/bandit/http2/stream.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:finish_data/5"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Connection\u0027:handle_frame/3"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Stream\u0027:send_data/3"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "f6914aad14bb1365dd6f306aa592cfb0819bed3e",
              "status": "affected",
              "version": "6feadb31189d18b7dcda6c663112cd3bbaf63a46",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.12.5",
                  "versionStartIncluding": "0.3.4",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "reporter",
          "value": "Giovanni Visciano / Hewlett Packard Enterprise - Athonet"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "coordinator",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eAllocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin an unbounded number of HTTP/2 stream processes indefinitely via connection-level flow control. When a stream\u0027s response body outruns the HTTP/2 connection-level send window (default 65,535 bytes, shared across all streams on the connection), \u003ccode\u003eBandit.HTTP2.Connection\u003c/code\u003e queues the remaining bytes and a reply closure in \u003ccode\u003epending_sends\u003c/code\u003e and the stream process blocks forever inside a synchronous call to the connection process. Nothing bounds that wait and nothing purges the queue: a client \u003ccode\u003eRST_STREAM\u003c/code\u003e for the blocked stream is delivered to its mailbox but never read while it is stuck inside the call, so cancelling frees nothing, and periodic PING frames keep the transport-level read timeout from ever firing. The equivalent block on the stream-level send window is already bounded at 15 seconds; the connection-level path had no such bound.\u003c/p\u003e\n\u003cp\u003eEach stalled stream pins its process, Plug state, and any resource the Plug holds across the blocked write, such as a pooled upstream connection in a reverse-proxy Plug. The attacker chooses any endpoint whose response exceeds the connection window (common for most non-trivial payloads), grants a generous stream-level window so only the connection window limits it, and keeps the connection alive with periodic PINGs; the primitive is repeatable across streams and connections at the cost of one idle socket each.\u003c/p\u003e\n\u003cp\u003eThis issue affects bandit: from 0.3.4 before 1.12.5.\u003c/p\u003e"
            },
            {
              "base64": false,
              "type": "text/markdown",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin an unbounded number of HTTP/2 stream processes indefinitely via connection-level flow control. When a stream\u0027s response body outruns the HTTP/2 connection-level send window (default 65,535 bytes, shared across all streams on the connection), `Bandit.HTTP2.Connection` queues the remaining bytes and a reply closure in `pending_sends` and the stream process blocks forever inside a synchronous call to the connection process. Nothing bounds that wait and nothing purges the queue: a client `RST_STREAM` for the blocked stream is delivered to its mailbox but never read while it is stuck inside the call, so cancelling frees nothing, and periodic PING frames keep the transport-level read timeout from ever firing. The equivalent block on the stream-level send window is already bounded at 15 seconds; the connection-level path had no such bound.\n\nEach stalled stream pins its process, Plug state, and any resource the Plug holds across the blocked write, such as a pooled upstream connection in a reverse-proxy Plug. The attacker chooses any endpoint whose response exceeds the connection window (common for most non-trivial payloads), grants a generous stream-level window so only the connection window limits it, and keeps the connection alive with periodic PINGs; the primitive is repeatable across streams and connections at the cost of one idle socket each.\n\nThis issue affects bandit: from 0.3.4 before 1.12.5."
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows an unauthenticated remote attacker to pin an unbounded number of HTTP/2 stream processes indefinitely via connection-level flow control. When a stream\u0027s response body outruns the HTTP/2 connection-level send window (default 65,535 bytes, shared across all streams on the connection), Bandit.HTTP2.Connection queues the remaining bytes and a reply closure in pending_sends and the stream process blocks forever inside a synchronous call to the connection process. Nothing bounds that wait and nothing purges the queue: a client RST_STREAM for the blocked stream is delivered to its mailbox but never read while it is stuck inside the call, so cancelling frees nothing, and periodic PING frames keep the transport-level read timeout from ever firing. The equivalent block on the stream-level send window is already bounded at 15 seconds; the connection-level path had no such bound.\n\nEach stalled stream pins its process, Plug state, and any resource the Plug holds across the blocked write, such as a pooled upstream connection in a reverse-proxy Plug. The attacker chooses any endpoint whose response exceeds the connection window (common for most non-trivial payloads), grants a generous stream-level window so only the connection window limits it, and keeps the connection alive with periodic PINGs; the primitive is repeatable across streams and connections at the cost of one idle socket each.\n\nThis issue affects bandit: from 0.3.4 before 1.12.5."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-130 Excessive Allocation"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "Automatable": "NOT_DEFINED",
            "Recovery": "NOT_DEFINED",
            "Safety": "NOT_DEFINED",
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "providerUrgency": "NOT_DEFINED",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "valueDensity": "NOT_DEFINED",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE",
            "vulnerabilityResponseEffort": "NOT_DEFINED"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-08-20T21:11:18.999Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-xj8g-532w-jv94"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-74836.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-74836"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/f6914aad14bb1365dd6f306aa592cfb0819bed3e"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "HTTP/2 connection-window starvation pins Plug processes indefinitely in Bandit"
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-74836",
    "datePublished": "2026-08-20T21:11:18.999Z",
    "dateReserved": "2026-08-20T14:30:02.109Z",
    "dateUpdated": "2026-08-21T19:55:01.929Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-65623 (GCVE-0-2026-65623)
Vulnerability from cvelistv5
Published
2026-07-24 16:32
Modified
2026-07-25 04:16
CWE
  • CWE-407 - Inefficient Algorithmic Complexity
Summary
Inefficient Algorithmic Complexity vulnerability in mtrudel bandit allows unauthenticated remote denial of service via CPU exhaustion during WebSocket fragment reassembly. The size guard 'Elixir.Bandit.WebSocket.Connection':oversize_message?/2 called from handle_frame/3 in lib/bandit/websocket/connection.ex appends each non-final continuation frame to a left-nested iolist and then re-measures the entire accumulated buffer with IO.iodata_length/1 on every frame. Because the buffer grows by one element per frame and is fully re-traversed each time, reassembly work is quadratic (O(n^2)) in the number of continuation frames. The max_fragmented_message_size limit (default 8 MB) bounds total bytes but not frame count, and each frame can carry as little as one payload byte, so an attacker can send millions of tiny continuation frames using modest bandwidth to pin a CPU core for minutes to hours. Many concurrent connections can starve the whole server of CPU, denying service to legitimate users. The WebSocket read timeout does not help, because it is an idle timeout evaluated between reads and cannot preempt the synchronous reassembly work spent inside a single callback. This issue affects bandit: from 1.11.0 before 1.12.1.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.11.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 21612c7c7b1ce43eccd36d3af3a2299d23513667
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-65623",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-07-24T17:08:51.111665Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-07-24T17:09:09.552Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-vg8x-66vg-5pxh"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "Elixir.Bandit.WebSocket.Connection"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/websocket/connection.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.WebSocket.Connection\u0027:handle_frame/3"
            },
            {
              "name": "\u0027Elixir.Bandit.WebSocket.Connection\u0027:oversize_message?/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.12.1",
              "status": "affected",
              "version": "1.11.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "Elixir.Bandit.WebSocket.Connection"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/websocket/connection.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.WebSocket.Connection\u0027:handle_frame/3"
            },
            {
              "name": "\u0027Elixir.Bandit.WebSocket.Connection\u0027:oversize_message?/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "418ef7e906192a230ddba112f7a669c87b6b0e3a",
              "status": "affected",
              "version": "21612c7c7b1ce43eccd36d3af3a2299d23513667",
              "versionType": "git"
            }
          ]
        }
      ],
      "configurations": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eThis vulnerability only affects applications that serve WebSocket endpoints through Bandit (including Phoenix applications that use Bandit as the HTTP adapter). Applications that do not upgrade any connections to WebSocket are not affected.\u003c/p\u003e"
            }
          ],
          "value": "This vulnerability only affects applications that serve WebSocket endpoints through Bandit (including Phoenix applications that use Bandit as the HTTP adapter). Applications that do not upgrade any connections to WebSocket are not affected."
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.12.1",
                  "versionStartIncluding": "1.11.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Inefficient Algorithmic Complexity vulnerability in mtrudel bandit allows unauthenticated remote denial of service via CPU exhaustion during WebSocket fragment reassembly.\u003cp\u003eThe size guard \u003ctt\u003e\u0027Elixir.Bandit.WebSocket.Connection\u0027:oversize_message?/2\u003c/tt\u003e called from \u003ctt\u003ehandle_frame/3\u003c/tt\u003e in \u003ctt\u003elib/bandit/websocket/connection.ex\u003c/tt\u003e appends each non-final continuation frame to a left-nested iolist and then re-measures the entire accumulated buffer with \u003ctt\u003eIO.iodata_length/1\u003c/tt\u003e on every frame. Because the buffer grows by one element per frame and is fully re-traversed each time, reassembly work is quadratic (O(n^2)) in the number of continuation frames.\u003c/p\u003e\u003cp\u003eThe \u003ctt\u003emax_fragmented_message_size\u003c/tt\u003e limit (default 8 MB) bounds total bytes but not frame count, and each frame can carry as little as one payload byte, so an attacker can send millions of tiny continuation frames using modest bandwidth to pin a CPU core for minutes to hours. Many concurrent connections can starve the whole server of CPU, denying service to legitimate users. The WebSocket read timeout does not help, because it is an idle timeout evaluated between reads and cannot preempt the synchronous reassembly work spent inside a single callback.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: from 1.11.0 before 1.12.1.\u003c/p\u003e"
            }
          ],
          "value": "Inefficient Algorithmic Complexity vulnerability in mtrudel bandit allows unauthenticated remote denial of service via CPU exhaustion during WebSocket fragment reassembly.\n\nThe size guard \u0027Elixir.Bandit.WebSocket.Connection\u0027:oversize_message?/2 called from handle_frame/3 in lib/bandit/websocket/connection.ex appends each non-final continuation frame to a left-nested iolist and then re-measures the entire accumulated buffer with IO.iodata_length/1 on every frame. Because the buffer grows by one element per frame and is fully re-traversed each time, reassembly work is quadratic (O(n^2)) in the number of continuation frames.\n\nThe max_fragmented_message_size limit (default 8 MB) bounds total bytes but not frame count, and each frame can carry as little as one payload byte, so an attacker can send millions of tiny continuation frames using modest bandwidth to pin a CPU core for minutes to hours. Many concurrent connections can starve the whole server of CPU, denying service to legitimate users. The WebSocket read timeout does not help, because it is an idle timeout evaluated between reads and cannot preempt the synchronous reassembly work spent inside a single callback.\n\nThis issue affects bandit: from 1.11.0 before 1.12.1."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-229",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-229 Serialized Data Parameter Blowup"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-407",
              "description": "CWE-407 Inefficient Algorithmic Complexity",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-07-25T04:16:42.726Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-vg8x-66vg-5pxh"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-65623.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-65623"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/418ef7e906192a230ddba112f7a669c87b6b0e3a"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "Quadratic CPU blow-up reassembling fragmented WebSocket messages in Bandit",
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-65623",
    "datePublished": "2026-07-24T16:32:24.923Z",
    "dateReserved": "2026-07-22T13:55:59.401Z",
    "dateUpdated": "2026-07-25T04:16:42.726Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-39806 (GCVE-0-2026-39806)
Vulnerability from cvelistv5
Published
2026-05-13 13:36
Modified
2026-05-27 15:41
CWE
  • CWE-835 - Loop with Unreachable Exit Condition ('Infinite Loop')
Summary
Loop with Unreachable Exit Condition ('Infinite Loop') vulnerability in mtrudel bandit allows unauthenticated remote denial of service via worker process exhaustion. 'Elixir.Bandit.HTTP1.Socket':do_read_chunked_data!/5 in lib/bandit/http1/socket.ex terminates only when the last-chunk line 0\r\n is followed immediately by the empty trailer line \r\n. RFC 9112 §7.1.2 permits zero or more trailer fields between them. When trailers are present, none of the match clauses fit: the catch-all arm computes a negative to_read, calls read_available!/2, receives <<>> on timeout, and tail-recurses with unchanged state. The worker process is pinned for the lifetime of the TCP connection. A handful of concurrent connections sending RFC-conformant chunked requests with trailer fields is sufficient to exhaust the Bandit worker pool and render the server unresponsive to all further traffic. No authentication, special headers, or large payload is required. Proxies such as NGINX and HAProxy legitimately forward trailer-bearing requests, so servers behind such proxies may be affected without any malicious client involvement. This issue affects bandit: from 1.6.1 before 1.11.1.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.6.1   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: e73e379ab59840e8561b5730878f16e29ab06217
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-39806",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-05-13T14:36:21.650529Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-05-13T14:36:34.475Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-rf5q-vwxw-gmrf"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP1.Socket\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http1/socket.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP1.Socket\u0027:do_read_chunked_data!/5"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.11.1",
              "status": "affected",
              "version": "1.6.1",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP1.Socket\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http1/socket.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP1.Socket\u0027:do_read_chunked_data!/5"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "ae3520dfdbfab115c638f8c7f6f6b805db34e1ab",
              "status": "affected",
              "version": "e73e379ab59840e8561b5730878f16e29ab06217",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.11.1",
                  "versionStartIncluding": "1.6.1",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Loop with Unreachable Exit Condition (\u0027Infinite Loop\u0027) vulnerability in mtrudel bandit allows unauthenticated remote denial of service via worker process exhaustion.\u003cp\u003e\u003ctt\u003e\u0027Elixir.Bandit.HTTP1.Socket\u0027:do_read_chunked_data!/5\u003c/tt\u003e in \u003ctt\u003elib/bandit/http1/socket.ex\u003c/tt\u003e terminates only when the last-chunk line \u003ctt\u003e0\\r\\n\u003c/tt\u003e is followed immediately by the empty trailer line \u003ctt\u003e\\r\\n\u003c/tt\u003e. RFC 9112 \u00a77.1.2 permits zero or more trailer fields between them. When trailers are present, none of the match clauses fit: the catch-all arm computes a negative \u003ctt\u003eto_read\u003c/tt\u003e, calls \u003ctt\u003eread_available!/2\u003c/tt\u003e, receives \u003ctt\u003e\u0026lt;\u0026lt;\u0026gt;\u0026gt;\u003c/tt\u003e on timeout, and tail-recurses with unchanged state. The worker process is pinned for the lifetime of the TCP connection.\u003c/p\u003e\u003cp\u003eA handful of concurrent connections sending RFC-conformant chunked requests with trailer fields is sufficient to exhaust the Bandit worker pool and render the server unresponsive to all further traffic. No authentication, special headers, or large payload is required. Proxies such as NGINX and HAProxy legitimately forward trailer-bearing requests, so servers behind such proxies may be affected without any malicious client involvement.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: from 1.6.1 before 1.11.1.\u003c/p\u003e"
            }
          ],
          "value": "Loop with Unreachable Exit Condition (\u0027Infinite Loop\u0027) vulnerability in mtrudel bandit allows unauthenticated remote denial of service via worker process exhaustion.\n\n\u0027Elixir.Bandit.HTTP1.Socket\u0027:do_read_chunked_data!/5 in lib/bandit/http1/socket.ex terminates only when the last-chunk line 0\\r\\n is followed immediately by the empty trailer line \\r\\n. RFC 9112 \u00a77.1.2 permits zero or more trailer fields between them. When trailers are present, none of the match clauses fit: the catch-all arm computes a negative to_read, calls read_available!/2, receives \u003c\u003c\u003e\u003e on timeout, and tail-recurses with unchanged state. The worker process is pinned for the lifetime of the TCP connection.\n\nA handful of concurrent connections sending RFC-conformant chunked requests with trailer fields is sufficient to exhaust the Bandit worker pool and render the server unresponsive to all further traffic. No authentication, special headers, or large payload is required. Proxies such as NGINX and HAProxy legitimately forward trailer-bearing requests, so servers behind such proxies may be affected without any malicious client involvement.\n\nThis issue affects bandit: from 1.6.1 before 1.11.1."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-469",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-469 HTTP DoS"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-835",
              "description": "CWE-835 Loop with Unreachable Exit Condition (\u0027Infinite Loop\u0027)",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-05-27T15:41:42.286Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-rf5q-vwxw-gmrf"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-39806.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-39806"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/ae3520dfdbfab115c638f8c7f6f6b805db34e1ab"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "HTTP/1 chunked decoder infinite loop on requests with trailer fields in bandit",
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-39806",
    "datePublished": "2026-05-13T13:36:17.806Z",
    "dateReserved": "2026-04-07T12:28:54.916Z",
    "dateUpdated": "2026-05-27T15:41:42.286Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-39803 (GCVE-0-2026-39803)
Vulnerability from cvelistv5
Published
2026-05-13 13:36
Modified
2026-05-27 15:40
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion. The chunked clause of 'Elixir.Bandit.HTTP1.Socket':read_data/2 in lib/bandit/http1/socket.ex ignores the caller-supplied :length option when reading HTTP/1 chunked request bodies. Instead of capping the accumulated body at the configured limit (e.g. Plug.Parsers' default 8 MB), do_read_chunked_data!/5 buffers every received chunk into an iolist unconditionally and materializes the entire body as a single binary. The function always returns {:ok, body, ...}, so callers cannot interpose a 413 response. Because Plug.Parsers runs before routing and authentication in the standard Phoenix endpoint, an unauthenticated attacker needs no valid route or credentials. Sending a single Transfer-Encoding: chunked POST request with an arbitrarily large body to any path causes the BEAM process to exhaust available memory and be terminated by the OS OOM killer. The content-length path in the same function correctly enforces the limit and is not affected. This issue affects bandit: from 1.4.0 before 1.11.1.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.4.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 903e209a521bc216b9f9065c01ae9a0cac2d5a10
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-39803",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-05-13T14:41:58.440838Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-05-13T14:43:25.158Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-9q9q-324x-93r2"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP1.Socket\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http1/socket.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP1.Socket\u0027:read_data/2"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP1.Socket\u0027:do_read_chunked_data!/5"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.11.1",
              "status": "affected",
              "version": "1.4.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP1.Socket\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http1/socket.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP1.Socket\u0027:read_data/2"
            },
            {
              "name": "\u0027Elixir.Bandit.HTTP1.Socket\u0027:do_read_chunked_data!/5"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "ae3520dfdbfab115c638f8c7f6f6b805db34e1ab",
              "status": "affected",
              "version": "903e209a521bc216b9f9065c01ae9a0cac2d5a10",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.11.1",
                  "versionStartIncluding": "1.4.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion.\u003cp\u003eThe chunked clause of \u003ctt\u003e\u0027Elixir.Bandit.HTTP1.Socket\u0027:read_data/2\u003c/tt\u003e in \u003ctt\u003elib/bandit/http1/socket.ex\u003c/tt\u003e ignores the caller-supplied \u003ctt\u003e:length\u003c/tt\u003e option when reading HTTP/1 chunked request bodies. Instead of capping the accumulated body at the configured limit (e.g. \u003ctt\u003ePlug.Parsers\u003c/tt\u003e\u0027 default 8 MB), \u003ctt\u003edo_read_chunked_data!/5\u003c/tt\u003e buffers every received chunk into an iolist unconditionally and materializes the entire body as a single binary. The function always returns \u003ctt\u003e{:ok, body, ...}\u003c/tt\u003e, so callers cannot interpose a 413 response.\u003c/p\u003e\u003cp\u003eBecause \u003ctt\u003ePlug.Parsers\u003c/tt\u003e runs before routing and authentication in the standard Phoenix endpoint, an unauthenticated attacker needs no valid route or credentials. Sending a single \u003ctt\u003eTransfer-Encoding: chunked\u003c/tt\u003e POST request with an arbitrarily large body to any path causes the BEAM process to exhaust available memory and be terminated by the OS OOM killer.\u003c/p\u003e\u003cp\u003eThe content-length path in the same function correctly enforces the limit and is not affected.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: from 1.4.0 before 1.11.1.\u003c/p\u003e"
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion.\n\nThe chunked clause of \u0027Elixir.Bandit.HTTP1.Socket\u0027:read_data/2 in lib/bandit/http1/socket.ex ignores the caller-supplied :length option when reading HTTP/1 chunked request bodies. Instead of capping the accumulated body at the configured limit (e.g. Plug.Parsers\u0027 default 8 MB), do_read_chunked_data!/5 buffers every received chunk into an iolist unconditionally and materializes the entire body as a single binary. The function always returns {:ok, body, ...}, so callers cannot interpose a 413 response.\n\nBecause Plug.Parsers runs before routing and authentication in the standard Phoenix endpoint, an unauthenticated attacker needs no valid route or credentials. Sending a single Transfer-Encoding: chunked POST request with an arbitrarily large body to any path causes the BEAM process to exhaust available memory and be terminated by the OS OOM killer.\n\nThe content-length path in the same function correctly enforces the limit and is not affected.\n\nThis issue affects bandit: from 1.4.0 before 1.11.1."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-130 Excessive Allocation"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-05-27T15:40:37.538Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-9q9q-324x-93r2"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-39803.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-39803"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/ae3520dfdbfab115c638f8c7f6f6b805db34e1ab"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "HTTP/1 chunked body reader ignores length cap in bandit",
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-39803",
    "datePublished": "2026-05-13T13:36:09.648Z",
    "dateReserved": "2026-04-07T12:28:54.916Z",
    "dateUpdated": "2026-05-27T15:40:37.538Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-39805 (GCVE-0-2026-39805)
Vulnerability from cvelistv5
Published
2026-05-01 20:34
Modified
2026-07-24 14:16
CWE
  • CWE-444 - Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
Summary
Inconsistent Interpretation of HTTP Requests vulnerability in mtrudel bandit allows HTTP request smuggling via duplicate Content-Length headers. 'Elixir.Bandit.Headers':get_content_length/1 in lib/bandit/headers.ex uses List.keyfind/3, which returns only the first matching header. When a request contains two Content-Length headers with different values, Bandit silently accepts it, uses the first value to read the body, and dispatches the remaining bytes as a second pipelined request on the same keep-alive connection. RFC 9112 §6.3 requires recipients to treat this as an unrecoverable framing error. When Bandit sits behind a proxy that picks the last Content-Length value and forwards the request rather than rejecting it, an unauthenticated attacker can smuggle requests past edge WAF rules, path-based ACLs, rate limiting, and audit logging. This issue affects bandit: before 1.11.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.1.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 7bc8d97cd22697c299baa8012b7f419a7606a80c
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-39805",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-05-02T01:20:49.825555Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-05-02T01:21:12.460Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-c67r-gc9j-2qf7"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.Headers\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/headers.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.Headers\u0027:get_content_length/1"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.11.0",
              "status": "affected",
              "version": "0.1.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.Headers\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/headers.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.Headers\u0027:get_content_length/1"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "f2ca636eb6df385219957e8934e9fc6efa1630d1",
              "status": "affected",
              "version": "7bc8d97cd22697c299baa8012b7f419a7606a80c",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.11.0",
                  "versionStartIncluding": "0.1.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Inconsistent Interpretation of HTTP Requests vulnerability in mtrudel bandit allows HTTP request smuggling via duplicate \u003ctt\u003eContent-Length\u003c/tt\u003e headers.\u003cp\u003e\u003ctt\u003e\u0027Elixir.Bandit.Headers\u0027:get_content_length/1\u003c/tt\u003e in \u003ctt\u003elib/bandit/headers.ex\u003c/tt\u003e uses \u003ctt\u003eList.keyfind/3\u003c/tt\u003e, which returns only the first matching header. When a request contains two \u003ctt\u003eContent-Length\u003c/tt\u003e headers with different values, Bandit silently accepts it, uses the first value to read the body, and dispatches the remaining bytes as a second pipelined request on the same keep-alive connection. RFC 9112 \u00a76.3 requires recipients to treat this as an unrecoverable framing error.\u003c/p\u003e\u003cp\u003eWhen Bandit sits behind a proxy that picks the last \u003ctt\u003eContent-Length\u003c/tt\u003e value and forwards the request rather than rejecting it, an unauthenticated attacker can smuggle requests past edge WAF rules, path-based ACLs, rate limiting, and audit logging.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: before 1.11.0.\u003c/p\u003e"
            }
          ],
          "value": "Inconsistent Interpretation of HTTP Requests vulnerability in mtrudel bandit allows HTTP request smuggling via duplicate Content-Length headers.\n\n\u0027Elixir.Bandit.Headers\u0027:get_content_length/1 in lib/bandit/headers.ex uses List.keyfind/3, which returns only the first matching header. When a request contains two Content-Length headers with different values, Bandit silently accepts it, uses the first value to read the body, and dispatches the remaining bytes as a second pipelined request on the same keep-alive connection. RFC 9112 \u00a76.3 requires recipients to treat this as an unrecoverable framing error.\n\nWhen Bandit sits behind a proxy that picks the last Content-Length value and forwards the request rather than rejecting it, an unauthenticated attacker can smuggle requests past edge WAF rules, path-based ACLs, rate limiting, and audit logging.\n\nThis issue affects bandit: before 1.11.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-33",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-33 HTTP Request Smuggling"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "PRESENT",
            "attackVector": "NETWORK",
            "baseScore": 6.3,
            "baseSeverity": "MEDIUM",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
            "version": "4.0",
            "vulnAvailabilityImpact": "NONE",
            "vulnConfidentialityImpact": "LOW",
            "vulnIntegrityImpact": "LOW"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-444",
              "description": "CWE-444 Inconsistent Interpretation of HTTP Requests (\u0027HTTP Request/Response Smuggling\u0027)",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-07-24T14:16:00.658Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-c67r-gc9j-2qf7"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-39805.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-39805"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/f2ca636eb6df385219957e8934e9fc6efa1630d1"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "CL.CL HTTP request smuggling via duplicate Content-Length in bandit",
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-39805",
    "datePublished": "2026-05-01T20:34:29.400Z",
    "dateReserved": "2026-04-07T12:28:54.916Z",
    "dateUpdated": "2026-07-24T14:16:00.658Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-39804 (GCVE-0-2026-39804)
Vulnerability from cvelistv5
Published
2026-05-01 20:34
Modified
2026-05-27 15:41
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion when WebSocket permessage-deflate compression is enabled. 'Elixir.Bandit.WebSocket.PerMessageDeflate':inflate/2 in lib/bandit/websocket/permessage_deflate.ex calls :zlib.inflate/2 with no output-size cap, then materializes the entire decompressed payload as a single binary via IO.iodata_to_binary/1. The websocket_options.max_frame_size option only bounds the on-the-wire (compressed) frame size, not the decompressed output. A high-ratio compressed frame (e.g. uniform data at ~1024:1 ratio) can stay well under any wire-size limit while forcing GiB-scale heap allocations in the connection process before any application code runs. An unauthenticated attacker who can open a WebSocket connection can send a single such frame to exhaust the BEAM node's memory and trigger an OOM kill. This vulnerability requires both Bandit's server-level websocket_options.compress and the per-upgrade compress: true option passed to WebSockAdapter.upgrade/4 to be enabled. Stock Phoenix and LiveView applications are not affected as they default to compress: false. This issue affects bandit: from 0.5.9 before 1.11.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.5.9   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: da4027cff7d2b80319e76fe7a32f84beceec490a
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-39804",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-05-02T01:19:00.687361Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-05-02T01:19:55.677Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-frh3-6pv6-rc8j"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.WebSocket.PerMessageDeflate\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/websocket/permessage_deflate.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.WebSocket.PerMessageDeflate\u0027:inflate/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.11.0",
              "status": "affected",
              "version": "0.5.9",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.WebSocket.PerMessageDeflate\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/websocket/permessage_deflate.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.WebSocket.PerMessageDeflate\u0027:inflate/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "8156921a51e684a951221da7bc30a70a022f722e",
              "status": "affected",
              "version": "da4027cff7d2b80319e76fe7a32f84beceec490a",
              "versionType": "git"
            }
          ]
        }
      ],
      "configurations": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "The vulnerability is only reachable when both of the following conditions are true:\u003cul\u003e\u003cli\u003eBandit\u0027s server-level \u003ctt\u003ewebsocket_options.compress\u003c/tt\u003e is enabled (it defaults to \u003ctt\u003etrue\u003c/tt\u003e).\u003c/li\u003e\u003cli\u003eThe per-upgrade \u003ctt\u003ecompress: true\u003c/tt\u003e option is passed to \u003ctt\u003eWebSockAdapter.upgrade/4\u003c/tt\u003e (it defaults to \u003ctt\u003efalse\u003c/tt\u003e; Phoenix\u0027s default is also \u003ctt\u003efalse\u003c/tt\u003e).\u003c/li\u003e\u003c/ul\u003eStock Phoenix and LiveView applications are not affected because \u003ctt\u003ecompress: false\u003c/tt\u003e is their default."
            }
          ],
          "value": "The vulnerability is only reachable when both of the following conditions are true:\n- Bandit\u0027s server-level websocket_options.compress is enabled (it defaults to true).\n- The per-upgrade compress: true option is passed to WebSockAdapter.upgrade/4 (it defaults to false; Phoenix\u0027s default is also false).\n\nStock Phoenix and LiveView applications are not affected because compress: false is their default."
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.11.0",
                  "versionStartIncluding": "0.5.9",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion when WebSocket permessage-deflate compression is enabled.\u003cp\u003e\u003ctt\u003e\u0027Elixir.Bandit.WebSocket.PerMessageDeflate\u0027:inflate/2\u003c/tt\u003e in \u003ctt\u003elib/bandit/websocket/permessage_deflate.ex\u003c/tt\u003e calls \u003ctt\u003e:zlib.inflate/2\u003c/tt\u003e with no output-size cap, then materializes the entire decompressed payload as a single binary via \u003ctt\u003eIO.iodata_to_binary/1\u003c/tt\u003e. The \u003ctt\u003ewebsocket_options.max_frame_size\u003c/tt\u003e option only bounds the on-the-wire (compressed) frame size, not the decompressed output. A high-ratio compressed frame (e.g. uniform data at ~1024:1 ratio) can stay well under any wire-size limit while forcing GiB-scale heap allocations in the connection process before any application code runs.\u003c/p\u003e\u003cp\u003eAn unauthenticated attacker who can open a WebSocket connection can send a single such frame to exhaust the BEAM node\u0027s memory and trigger an OOM kill.\u003c/p\u003e\u003cp\u003eThis vulnerability requires both Bandit\u0027s server-level \u003ctt\u003ewebsocket_options.compress\u003c/tt\u003e and the per-upgrade \u003ctt\u003ecompress: true\u003c/tt\u003e option passed to \u003ctt\u003eWebSockAdapter.upgrade/4\u003c/tt\u003e to be enabled. Stock Phoenix and LiveView applications are not affected as they default to \u003ctt\u003ecompress: false\u003c/tt\u003e.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: from 0.5.9 before 1.11.0.\u003c/p\u003e"
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion when WebSocket permessage-deflate compression is enabled.\n\n\u0027Elixir.Bandit.WebSocket.PerMessageDeflate\u0027:inflate/2 in lib/bandit/websocket/permessage_deflate.ex calls :zlib.inflate/2 with no output-size cap, then materializes the entire decompressed payload as a single binary via IO.iodata_to_binary/1. The websocket_options.max_frame_size option only bounds the on-the-wire (compressed) frame size, not the decompressed output. A high-ratio compressed frame (e.g. uniform data at ~1024:1 ratio) can stay well under any wire-size limit while forcing GiB-scale heap allocations in the connection process before any application code runs.\n\nAn unauthenticated attacker who can open a WebSocket connection can send a single such frame to exhaust the BEAM node\u0027s memory and trigger an OOM kill.\n\nThis vulnerability requires both Bandit\u0027s server-level websocket_options.compress and the per-upgrade compress: true option passed to WebSockAdapter.upgrade/4 to be enabled. Stock Phoenix and LiveView applications are not affected as they default to compress: false.\n\nThis issue affects bandit: from 0.5.9 before 1.11.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-130 Excessive Allocation"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "PRESENT",
            "attackVector": "NETWORK",
            "baseScore": 8.2,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-05-27T15:41:26.362Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-frh3-6pv6-rc8j"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-39804.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-39804"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/8156921a51e684a951221da7bc30a70a022f722e"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "WebSocket permessage-deflate inflate has no output-size cap in bandit",
      "workarounds": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Do not pass \u003ctt\u003ecompress: true\u003c/tt\u003e to \u003ctt\u003eWebSockAdapter.upgrade/4\u003c/tt\u003e. Omitting this option (or setting it to \u003ctt\u003efalse\u003c/tt\u003e) prevents permessage-deflate from being negotiated, so the inflate path is never reached."
            }
          ],
          "value": "Do not pass compress: true to WebSockAdapter.upgrade/4. Omitting this option (or setting it to false) prevents permessage-deflate from being negotiated, so the inflate path is never reached."
        }
      ],
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-39804",
    "datePublished": "2026-05-01T20:34:24.604Z",
    "dateReserved": "2026-04-07T12:28:54.916Z",
    "dateUpdated": "2026-05-27T15:41:26.362Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-39807 (GCVE-0-2026-39807)
Vulnerability from cvelistv5
Published
2026-05-01 20:34
Modified
2026-05-27 15:41
CWE
  • CWE-807 - Reliance on Untrusted Inputs in a Security Decision
Summary
Reliance on Untrusted Inputs in a Security Decision vulnerability in mtrudel bandit allows unauthenticated transport-state spoofing on plaintext HTTP connections. 'Elixir.Bandit.Pipeline':determine_scheme/2 in lib/bandit/pipeline.ex returns the client-supplied URI scheme verbatim, ignoring the transport's secure? flag. HTTP/1.1 absolute-form request targets (e.g. GET https://victim/path HTTP/1.1) and the HTTP/2 :scheme pseudo-header are both attacker-controlled strings that flow through this function. Over a plaintext TCP connection, a client can declare https and Bandit will set conn.scheme = :https even though no TLS was negotiated. Downstream Plug consumers that branch on conn.scheme are silently misled: Plug.SSL's already-secure branch skips its HTTP→HTTPS redirect, cookies emitted with secure: true are sent over plaintext, audit logs record requests as having arrived over HTTPS, and CSRF/SameSite gating may make incorrect decisions. This issue affects bandit: from 1.0.0 before 1.11.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 1.0.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: ff2f829326cd5dcf7335939aef9775269d881e28
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-39807",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-05-02T01:17:52.498343Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-05-02T01:18:10.776Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-375f-4r2h-f99j"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.Pipeline\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/pipeline.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.Pipeline\u0027:determine_scheme/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.11.0",
              "status": "affected",
              "version": "1.0.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.Pipeline\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/pipeline.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.Pipeline\u0027:determine_scheme/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "45feea20dea8af7ffd7245271107b695c040e667",
              "status": "affected",
              "version": "ff2f829326cd5dcf7335939aef9775269d881e28",
              "versionType": "git"
            }
          ]
        }
      ],
      "configurations": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eThe vulnerable system must be accepting plaintext (non-TLS) HTTP connections, either directly or via h2c. Deployments that exclusively use TLS are not affected.\u003c/p\u003e"
            }
          ],
          "value": "The vulnerable system must be accepting plaintext (non-TLS) HTTP connections, either directly or via h2c. Deployments that exclusively use TLS are not affected."
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.11.0",
                  "versionStartIncluding": "1.0.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Reliance on Untrusted Inputs in a Security Decision vulnerability in mtrudel bandit allows unauthenticated transport-state spoofing on plaintext HTTP connections.\u003cp\u003e\u003ctt\u003e\u0027Elixir.Bandit.Pipeline\u0027:determine_scheme/2\u003c/tt\u003e in \u003ctt\u003elib/bandit/pipeline.ex\u003c/tt\u003e returns the client-supplied URI scheme verbatim, ignoring the transport\u0027s \u003ctt\u003esecure?\u003c/tt\u003e flag. HTTP/1.1 absolute-form request targets (e.g. \u003ctt\u003eGET https://victim/path HTTP/1.1\u003c/tt\u003e) and the HTTP/2 \u003ctt\u003e:scheme\u003c/tt\u003e pseudo-header are both attacker-controlled strings that flow through this function. Over a plaintext TCP connection, a client can declare \u003ctt\u003ehttps\u003c/tt\u003e and Bandit will set \u003ctt\u003econn.scheme = :https\u003c/tt\u003e even though no TLS was negotiated.\u003c/p\u003e\u003cp\u003eDownstream Plug consumers that branch on \u003ctt\u003econn.scheme\u003c/tt\u003e are silently misled: \u003ctt\u003ePlug.SSL\u003c/tt\u003e\u0027s already-secure branch skips its HTTP\u2192HTTPS redirect, cookies emitted with \u003ctt\u003esecure: true\u003c/tt\u003e are sent over plaintext, audit logs record requests as having arrived over HTTPS, and CSRF/SameSite gating may make incorrect decisions.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: from 1.0.0 before 1.11.0.\u003c/p\u003e"
            }
          ],
          "value": "Reliance on Untrusted Inputs in a Security Decision vulnerability in mtrudel bandit allows unauthenticated transport-state spoofing on plaintext HTTP connections.\n\n\u0027Elixir.Bandit.Pipeline\u0027:determine_scheme/2 in lib/bandit/pipeline.ex returns the client-supplied URI scheme verbatim, ignoring the transport\u0027s secure? flag. HTTP/1.1 absolute-form request targets (e.g. GET https://victim/path HTTP/1.1) and the HTTP/2 :scheme pseudo-header are both attacker-controlled strings that flow through this function. Over a plaintext TCP connection, a client can declare https and Bandit will set conn.scheme = :https even though no TLS was negotiated.\n\nDownstream Plug consumers that branch on conn.scheme are silently misled: Plug.SSL\u0027s already-secure branch skips its HTTP\u2192HTTPS redirect, cookies emitted with secure: true are sent over plaintext, audit logs record requests as having arrived over HTTPS, and CSRF/SameSite gating may make incorrect decisions.\n\nThis issue affects bandit: from 1.0.0 before 1.11.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-220",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-220 Client-Server Protocol Manipulation"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "PRESENT",
            "attackVector": "NETWORK",
            "baseScore": 6.3,
            "baseSeverity": "MEDIUM",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
            "version": "4.0",
            "vulnAvailabilityImpact": "NONE",
            "vulnConfidentialityImpact": "LOW",
            "vulnIntegrityImpact": "LOW"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-807",
              "description": "CWE-807 Reliance on Untrusted Inputs in a Security Decision",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-05-27T15:41:35.917Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-375f-4r2h-f99j"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-39807.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-39807"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/45feea20dea8af7ffd7245271107b695c040e667"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "Client-supplied URI scheme trusted without transport verification in bandit",
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-39807",
    "datePublished": "2026-05-01T20:34:22.832Z",
    "dateReserved": "2026-04-07T12:28:54.916Z",
    "dateUpdated": "2026-05-27T15:41:35.917Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-42786 (GCVE-0-2026-42786)
Vulnerability from cvelistv5
Published
2026-05-01 20:34
Modified
2026-05-27 15:41
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion. The fragment reassembly path in 'Elixir.Bandit.WebSocket.Connection':handle_frame/3 in lib/bandit/websocket/connection.ex appends every incoming Continuation{fin: false} frame's payload to a per-connection iolist with no cumulative size cap. The existing max_frame_size option only bounds individual frames; a peer that streams an unbounded number of continuation frames without ever setting fin=1 grows BEAM heap linearly until the OS or a supervisor kills the process. Because the accumulation happens before WebSock.handle_in/2 is called, the application has no opportunity to interpose a size check. Phoenix Channels and LiveView both run over WebSock on Bandit, so a stock Phoenix application exposes this surface as soon as it accepts socket connections. This issue affects bandit: from 0.5.0 before 1.11.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.5.0   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: 8909391f486d42138c5308410bc5ea49a65f4d46
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-42786",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-05-02T01:15:58.376139Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-05-02T01:16:39.704Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-pf94-94m9-536p"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.WebSocket.Connection\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/websocket/connection.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.WebSocket.Connection\u0027:handle_frame/3"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.11.0",
              "status": "affected",
              "version": "0.5.0",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.WebSocket.Connection\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/websocket/connection.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.WebSocket.Connection\u0027:handle_frame/3"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "21612c7c7b1ce43eccd36d3af3a2299d23513667",
              "status": "affected",
              "version": "8909391f486d42138c5308410bc5ea49a65f4d46",
              "versionType": "git"
            }
          ]
        }
      ],
      "configurations": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "\u003cp\u003eThe application must accept WebSocket connections. Applications that expose no WebSocket endpoints are not affected.\u003c/p\u003e"
            }
          ],
          "value": "The application must accept WebSocket connections. Applications that expose no WebSocket endpoints are not affected."
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.11.0",
                  "versionStartIncluding": "0.5.0",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion.\u003cp\u003eThe fragment reassembly path in \u003ctt\u003e\u0027Elixir.Bandit.WebSocket.Connection\u0027:handle_frame/3\u003c/tt\u003e in \u003ctt\u003elib/bandit/websocket/connection.ex\u003c/tt\u003e appends every incoming \u003ctt\u003eContinuation{fin: false}\u003c/tt\u003e frame\u0027s payload to a per-connection iolist with no cumulative size cap. The existing \u003ctt\u003emax_frame_size\u003c/tt\u003e option only bounds individual frames; a peer that streams an unbounded number of continuation frames without ever setting \u003ctt\u003efin=1\u003c/tt\u003e grows BEAM heap linearly until the OS or a supervisor kills the process.\u003c/p\u003e\u003cp\u003eBecause the accumulation happens before \u003ctt\u003eWebSock.handle_in/2\u003c/tt\u003e is called, the application has no opportunity to interpose a size check. Phoenix Channels and LiveView both run over \u003ctt\u003eWebSock\u003c/tt\u003e on Bandit, so a stock Phoenix application exposes this surface as soon as it accepts socket connections.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: from 0.5.0 before 1.11.0.\u003c/p\u003e"
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated remote denial of service via memory exhaustion.\n\nThe fragment reassembly path in \u0027Elixir.Bandit.WebSocket.Connection\u0027:handle_frame/3 in lib/bandit/websocket/connection.ex appends every incoming Continuation{fin: false} frame\u0027s payload to a per-connection iolist with no cumulative size cap. The existing max_frame_size option only bounds individual frames; a peer that streams an unbounded number of continuation frames without ever setting fin=1 grows BEAM heap linearly until the OS or a supervisor kills the process.\n\nBecause the accumulation happens before WebSock.handle_in/2 is called, the application has no opportunity to interpose a size check. Phoenix Channels and LiveView both run over WebSock on Bandit, so a stock Phoenix application exposes this surface as soon as it accepts socket connections.\n\nThis issue affects bandit: from 0.5.0 before 1.11.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-130 Excessive Allocation"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "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",
            "version": "4.0",
            "vulnAvailabilityImpact": "HIGH",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-05-27T15:41:06.211Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-pf94-94m9-536p"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-42786.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-42786"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/21612c7c7b1ce43eccd36d3af3a2299d23513667"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "WebSocket fragmented message reassembly unbounded in bandit",
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-42786",
    "datePublished": "2026-05-01T20:34:17.014Z",
    "dateReserved": "2026-04-29T18:06:33.251Z",
    "dateUpdated": "2026-05-27T15:41:06.211Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}

CVE-2026-42788 (GCVE-0-2026-42788)
Vulnerability from cvelistv5
Published
2026-05-01 20:34
Modified
2026-05-27 15:40
CWE
  • CWE-770 - Allocation of Resources Without Limits or Throttling
Summary
Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated memory exhaustion via oversized HTTP/2 frames. 'Elixir.Bandit.HTTP2.Frame':deserialize/2 in lib/bandit/http2/frame.ex checks the SETTINGS_MAX_FRAME_SIZE limit only after pattern-matching payload::binary-size(length), which requires the entire frame body to be present in memory before either the accept or reject clause can fire. A peer that announces a frame length up to the 24-bit maximum (~16 MiB) causes the server to buffer that entire body before the size guard is evaluated, regardless of the max_frame_size negotiated during the HTTP/2 handshake (default 16 KiB per RFC 9113). An unauthenticated attacker holding many concurrent connections can force the server to buffer far more memory than the negotiated frame size limit should permit, leading to memory pressure and potential denial of service. This issue affects bandit: from 0.3.6 before 1.11.0.
Impacted products
Vendor Product Version
mtrudel bandit Version: 0.3.6   ≤
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
   mtrudel bandit Version: f00dd69a5b2a4863be585907acd853c4ffd41399
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-42788",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "yes"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-05-02T01:14:16.975049Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-05-02T01:14:50.042Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-q6v9-r226-v65f"
          }
        ],
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "collectionURL": "https://repo.hex.pm",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Frame\u0027"
          ],
          "packageName": "bandit",
          "packageURL": "pkg:hex/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/frame.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Frame\u0027:deserialize/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1.11.0",
              "status": "affected",
              "version": "0.3.6",
              "versionType": "semver"
            }
          ]
        },
        {
          "collectionURL": "https://github.com",
          "cpes": [
            "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
          ],
          "defaultStatus": "unaffected",
          "modules": [
            "\u0027Elixir.Bandit.HTTP2.Frame\u0027"
          ],
          "packageName": "mtrudel/bandit",
          "packageURL": "pkg:github/mtrudel/bandit",
          "product": "bandit",
          "programFiles": [
            "lib/bandit/http2/frame.ex"
          ],
          "programRoutines": [
            {
              "name": "\u0027Elixir.Bandit.HTTP2.Frame\u0027:deserialize/2"
            }
          ],
          "repo": "https://github.com/mtrudel/bandit",
          "vendor": "mtrudel",
          "versions": [
            {
              "lessThan": "1e8e55966da9129016b73d32f0e1df4630e3b463",
              "status": "affected",
              "version": "f00dd69a5b2a4863be585907acd853c4ffd41399",
              "versionType": "git"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "1.11.0",
                  "versionStartIncluding": "0.3.6",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ],
          "operator": "AND"
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Peter Ullrich"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Mat Trudel"
        },
        {
          "lang": "en",
          "type": "analyst",
          "value": "Jonatan M\u00e4nnchen / EEF"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "supportingMedia": [
            {
              "base64": false,
              "type": "text/html",
              "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated memory exhaustion via oversized HTTP/2 frames.\u003cp\u003e\u003ctt\u003e\u0027Elixir.Bandit.HTTP2.Frame\u0027:deserialize/2\u003c/tt\u003e in \u003ctt\u003elib/bandit/http2/frame.ex\u003c/tt\u003e checks the \u003ctt\u003eSETTINGS_MAX_FRAME_SIZE\u003c/tt\u003e limit only after pattern-matching \u003ctt\u003epayload::binary-size(length)\u003c/tt\u003e, which requires the entire frame body to be present in memory before either the accept or reject clause can fire. A peer that announces a frame length up to the 24-bit maximum (~16 MiB) causes the server to buffer that entire body before the size guard is evaluated, regardless of the \u003ctt\u003emax_frame_size\u003c/tt\u003e negotiated during the HTTP/2 handshake (default 16 KiB per RFC 9113).\u003c/p\u003e\u003cp\u003eAn unauthenticated attacker holding many concurrent connections can force the server to buffer far more memory than the negotiated frame size limit should permit, leading to memory pressure and potential denial of service.\u003c/p\u003e\u003cp\u003eThis issue affects bandit: from 0.3.6 before 1.11.0.\u003c/p\u003e"
            }
          ],
          "value": "Allocation of Resources Without Limits or Throttling vulnerability in mtrudel bandit allows unauthenticated memory exhaustion via oversized HTTP/2 frames.\n\n\u0027Elixir.Bandit.HTTP2.Frame\u0027:deserialize/2 in lib/bandit/http2/frame.ex checks the SETTINGS_MAX_FRAME_SIZE limit only after pattern-matching payload::binary-size(length), which requires the entire frame body to be present in memory before either the accept or reject clause can fire. A peer that announces a frame length up to the 24-bit maximum (~16 MiB) causes the server to buffer that entire body before the size guard is evaluated, regardless of the max_frame_size negotiated during the HTTP/2 handshake (default 16 KiB per RFC 9113).\n\nAn unauthenticated attacker holding many concurrent connections can force the server to buffer far more memory than the negotiated frame size limit should permit, leading to memory pressure and potential denial of service.\n\nThis issue affects bandit: from 0.3.6 before 1.11.0."
        }
      ],
      "impacts": [
        {
          "capecId": "CAPEC-130",
          "descriptions": [
            {
              "lang": "en",
              "value": "CAPEC-130 Excessive Allocation"
            }
          ]
        }
      ],
      "metrics": [
        {
          "cvssV4_0": {
            "attackComplexity": "LOW",
            "attackRequirements": "NONE",
            "attackVector": "NETWORK",
            "baseScore": 6.9,
            "baseSeverity": "MEDIUM",
            "privilegesRequired": "NONE",
            "subAvailabilityImpact": "NONE",
            "subConfidentialityImpact": "NONE",
            "subIntegrityImpact": "NONE",
            "userInteraction": "NONE",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
            "version": "4.0",
            "vulnAvailabilityImpact": "LOW",
            "vulnConfidentialityImpact": "NONE",
            "vulnIntegrityImpact": "NONE"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "GENERAL"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-770",
              "description": "CWE-770 Allocation of Resources Without Limits or Throttling",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-05-27T15:40:29.557Z",
        "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "shortName": "EEF"
      },
      "references": [
        {
          "tags": [
            "vendor-advisory",
            "related"
          ],
          "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-q6v9-r226-v65f"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://cna.erlef.org/cves/CVE-2026-42788.html"
        },
        {
          "tags": [
            "related"
          ],
          "url": "https://osv.dev/vulnerability/EEF-CVE-2026-42788"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/mtrudel/bandit/commit/1e8e55966da9129016b73d32f0e1df4630e3b463"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "HTTP/2 frame size limit checked after body is buffered in bandit",
      "x_generator": {
        "engine": "cvelib 1.8.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
    "assignerShortName": "EEF",
    "cveId": "CVE-2026-42788",
    "datePublished": "2026-05-01T20:34:11.911Z",
    "dateReserved": "2026-04-29T18:06:33.251Z",
    "dateUpdated": "2026-05-27T15:40:29.557Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}