CVE-2026-110314 (GCVE-0-2026-110314)

Vulnerability from cvelistv5 – Published: 2026-10-26 13:31 – Updated: 2026-11-02 21:00
VLAI
Title
WebSocket frames keep reaching the WebSock after a server-initiated close in Bandit
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.
SSVC
Exploitation: poc Automatable: no Technical Impact: partial
CISA Coordinator · CISA-ADP (v2.0.3)
Decision recorded 2026-11-02 20:59 UTC
CWE
  • CWE-602 - Client-Side Enforcement of Server-Side Security
Assigner
EEF CNA under the mitre root
CNA scorecard A 97/100 over 351 records in the last 180 days details
Impacted products
Vendor Product Version CPE status
mtrudel bandit Affected: 0.5.1 , < 1.14.0 (semver)
    cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*
Create a notification for this product.
mtrudel bandit Affected: bb407f2277dcb65a58dd8cc07913a0b58a80e266 , < 220b9dfd27f9a21305be2afc75cbe585ddc24e7a (git)
    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-110314",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "no"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-11-02T20:59:44.175690Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-11-02T21:00:10.938Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "references": [
          {
            "tags": [
              "exploit"
            ],
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-2v5v-8f9v-xh85"
          }
        ],
        "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.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-11-02T21:00:10.938Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2",
  "vulnerability-lookup:meta": {
    "epss": {
      "cve": "CVE-2026-110314",
      "date": "2026-11-08",
      "epss": "0.00384",
      "percentile": "0.32692"
    },
    "nvd": {
      "cve": {
        "affected": [
          {
            "affectedData": [
              {
                "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"
                  }
                ]
              }
            ],
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db"
          }
        ],
        "cveTags": [],
        "descriptions": [
          {
            "lang": "en",
            "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."
          }
        ],
        "id": "CVE-2026-110314",
        "lastModified": "2026-11-02T21:17:23.077",
        "metrics": {
          "cvssMetricV40": [
            {
              "cvssData": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "availabilityRequirement": "NOT_DEFINED",
                "baseScore": 6.3,
                "baseSeverity": "MEDIUM",
                "confidentialityRequirement": "NOT_DEFINED",
                "exploitMaturity": "NOT_DEFINED",
                "integrityRequirement": "NOT_DEFINED",
                "modifiedAttackComplexity": "NOT_DEFINED",
                "modifiedAttackRequirements": "NOT_DEFINED",
                "modifiedAttackVector": "NOT_DEFINED",
                "modifiedPrivilegesRequired": "NOT_DEFINED",
                "modifiedSubAvailabilityImpact": "NOT_DEFINED",
                "modifiedSubConfidentialityImpact": "NOT_DEFINED",
                "modifiedSubIntegrityImpact": "NOT_DEFINED",
                "modifiedUserInteraction": "NOT_DEFINED",
                "modifiedVulnAvailabilityImpact": "NOT_DEFINED",
                "modifiedVulnConfidentialityImpact": "NOT_DEFINED",
                "modifiedVulnIntegrityImpact": "NOT_DEFINED",
                "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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
                "version": "4.0",
                "vulnAvailabilityImpact": "NONE",
                "vulnConfidentialityImpact": "LOW",
                "vulnIntegrityImpact": "LOW",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
              "type": "Secondary"
            }
          ],
          "ssvcV203": [
            {
              "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "ssvcData": {
                "id": "CVE-2026-110314",
                "options": [
                  {
                    "exploitation": "poc"
                  },
                  {
                    "automatable": "no"
                  },
                  {
                    "technicalImpact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-11-02T20:59:44.175690Z",
                "version": "2.0.3"
              }
            }
          ]
        },
        "published": "2026-10-26T14:16:25.470",
        "references": [
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://cna.erlef.org/cves/CVE-2026-110314.html"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/mtrudel/bandit/commit/bb407f2277dcb65a58dd8cc07913a0b58a80e266"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-2v5v-8f9v-xh85"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://osv.dev/vulnerability/EEF-CVE-2026-110314"
          },
          {
            "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
            "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-2v5v-8f9v-xh85"
          }
        ],
        "sourceIdentifier": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "vulnStatus": "Awaiting Analysis",
        "weaknesses": [
          {
            "description": [
              {
                "lang": "en",
                "value": "CWE-602"
              }
            ],
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "type": "Secondary"
          }
        ]
      }
    },
    "vulnrichment": {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-110314",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-11-02T20:59:44.175690Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-11-02T21:00:01.381Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/mtrudel/bandit/security/advisories/GHSA-2v5v-8f9v-xh85"
              }
            ],
            "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.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-11-02T21:00:10.938Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }
  }
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
How durations are calculated

Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.

Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.

Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.