A bouncer who lets everyone in but faints when someone doesn't show ID
CVE-2026-84445 is an index-out-of-bounds panic in gRPC-Go's xDS server routing interceptor. When a server is created via xds.NewGRPCServer(), every inbound RPC passes through a routing interceptor that looks up the :authority pseudo-header to select a virtual host. If a crafted HTTP/2 request arrives with *neither* the :authority header nor the Host header, the interceptor tries to read the first element of an empty slice — Go panics, and because the per-RPC goroutine has no recover(), the entire server process terminates. All versions of google.golang.org/grpc before 1.82.2, and the 1.83.0–1.83.1 range, are affected. The fix landed in 1.82.2 and 1.83.2. Crucially, only servers instantiated with xds.NewGRPCServer() are vulnerable; the standard grpc.NewServer() path — which represents the vast majority of grpc-go deployments — is completely unaffected.
Third-party CVSS 4.0 assessments peg this at 8.7 (HIGH), and the GitHub Security Advisory from grpc/grpc-go labels it High. That rating accurately reflects the raw mechanics: network-reachable, no authentication required (in non-mTLS configs), zero user interaction, full availability impact from a single packet. But it overweights the theoretical population. The xDS server API is a proxyless service-mesh feature used by teams running Istio proxyless gRPC, Google Traffic Director, or custom xDS control planes — a small fraction of the enormous grpc-go install base. The majority of those xDS deployments live inside Kubernetes clusters behind load balancers with mTLS enabled, which blocks unauthenticated exploitation entirely. The impact ceiling is DoS — no data exfiltration, no code execution, no lateral movement. Kubernetes auto-restarts crashed pods, limiting sustained outage. A MEDIUM assessment better reflects the real-world exploitability and blast radius.
4 steps from start to impact.
Locate an xDS-enabled gRPC endpoint
xds.NewGRPCServer() rather than standard grpc.NewServer(). This can be inferred from service mesh configurations, bootstrap files referencing an xDS management server, or by observing xDS-specific metadata in gRPC reflection responses. Scanning tools like grpcurl can enumerate exposed gRPC services.- Target runs gRPC-Go server instantiated via xds.NewGRPCServer()
- Attacker has network reachability to the gRPC port (typically internal)
- The vast majority of grpc-go deployments use grpc.NewServer(), not xds.NewGRPCServer()
- xDS servers typically run inside Kubernetes clusters, not exposed to the internet
- Differentiating xDS from standard gRPC externally is non-trivial
Complete HTTP/2 transport handshake
- TCP reachability to the gRPC port
- For mTLS/ALTS: possession of valid transport credentials
- Production xDS deployments increasingly default to mTLS via Istio/Traffic Director
- ALTS (Google's Application Layer Transport Security) is common in GCP deployments
- Network policies and Kubernetes NetworkPolicy objects may restrict pod-to-pod connectivity
Send malformed RPC without :authority or Host
:authority pseudo-header and the Host header. Standard HTTP/2 clients and libraries typically auto-populate :authority, so the attacker needs a raw HTTP/2 framing tool like h2c, hyper (Rust), or a custom Go http2 client to craft the malformed frame. No specialized exploit toolkit is required — a ~20-line script suffices.- Established HTTP/2 connection to the target
- Ability to send raw HTTP/2 frames (bypass client-side :authority auto-population)
- Standard gRPC client libraries auto-populate :authority, so exploitation requires non-standard tooling
- Compliant HTTP/2 intermediaries (L7 load balancers, Envoy sidecars) may reject or rewrite requests missing :authority before they reach the server
Server process panics and terminates
internal/xds/server/routing.go calls RouteAndProcess, which indexes authorities[0] on an empty slice. Go's runtime raises an index-out-of-bounds panic. Because the interceptor runs in the per-RPC goroutine without a recover() wrapper, the panic propagates to the server's main goroutine and terminates the entire OS process — not just the offending connection. All active RPCs on all connections are killed simultaneously.- Malformed RPC reaches the xDS routing interceptor
- Kubernetes liveness probes will detect the crash and restart the pod, typically within seconds
- CrashLoopBackOff exponential backoff limits sustained restart frequency after repeated crashes
- Service mesh health checks will remove the crashed instance from load balancing pools
kubectl get events), container runtime logs, and monitoring systems (Prometheus kube_pod_container_status_restarts_total). Crash stack traces appear in stderr/container logs with the runtime error: index out of range signature.The supporting signals.
| In-the-Wild Exploitation | No known exploitation. Not listed in CISA KEV. No reports of active campaigns targeting this CVE as of 2026-09-20. |
|---|---|
| Proof-of-Concept | No public PoC repository identified, but the attack is trivially reproducible with any raw HTTP/2 framing tool (~20 lines of Go or Python). The fix commit itself serves as a de facto PoC guide. Reported by @matiasinsaurralde. |
| EPSS | 0.00685 (0.685%) — 49th percentile. Below the median exploitation probability, consistent with a niche-scoped DoS bug. |
| KEV Status | Not listed. No CISA KEV entry. No BOD 22-01 deadline applies. |
| CVSS | CVSS 4.0: 8.7 (third-party assessment) — 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. Equivalent CVSS 3.1 base: ~7.5. Reflects unauthenticated network DoS with no C/I impact. |
| Affected Versions | google.golang.org/grpc < 1.82.2 and >= 1.83.0, < 1.83.2. Only servers using xds.NewGRPCServer() are reachable. |
| Fixed Versions | v1.82.2 and v1.83.2. Dev branch fix: v1.85.0-dev.0.20260825072537-93e31b48545e. Go vuln ID: GO-2026-6443. |
| Exposure Data | gRPC services are predominantly internal. Shodan/Censys show limited public gRPC exposure; xDS-mode servers are a further subset. KrakenD confirmed unaffected as it does not use xDS. Grafana, Agentcy OIDC Gateway issued dependency updates as precautionary measures. |
| Disclosure Date | 2026-09-14 (GHSA published). Go vuln database entry: 2026-09-15. |
| Reporter | @matiasinsaurralde via GitHub Security Advisory GHSA-2v4p-qf9q-27wj. |
Why this verdict
- xDS-only scope eliminates most of the install base: Only servers created with
xds.NewGRPCServer()are affected. Standardgrpc.NewServer()— the path used by the overwhelming majority of grpc-go consumers — is completely immune. This narrows the vulnerable population to an estimated 5–15% of grpc-go deployments (those using Istio proxyless, Traffic Director, or custom xDS control planes). - DoS ceiling caps severity: The impact is strictly availability. No memory corruption, no code execution, no data leakage. A single malformed request kills the process, but it cannot be chained into lateral movement, privilege escalation, or data exfiltration. This caps the theoretical maximum below HIGH for most deployment contexts.
- Internal deployment norm limits reachability: xDS-enabled gRPC servers are almost exclusively deployed inside Kubernetes clusters, behind L7 load balancers and service meshes. Direct internet exposure is atypical. An external attacker must first achieve internal network access (post-initial-access), which is a compounding prerequisite that depresses real-world exploitability.
- mTLS friction blocks the unauthenticated path: Production service mesh deployments using Istio, Traffic Director, or ALTS default to mutual TLS. In mTLS configurations, the attacker must possess valid client certificates to complete the transport handshake — transforming this from an unauthenticated to an authenticated-insider bug. Only insecure/plaintext or one-way TLS deployments are unauthenticated targets.
- Kubernetes auto-restart limits sustained impact: Crashed pods restart automatically via kubelet. While repeated exploitation can force CrashLoopBackOff, the exponential backoff and service mesh health-check ejection limit the blast radius of sustained DoS. Most clusters recover within seconds of a single crash.
- Role multiplier: gRPC-Go xDS servers can occupy high-value roles: service mesh data planes handling internal routing, API gateway tiers, or control-plane adjacencies. In such roles, a crash disrupts request routing for dependent services — blast radius is *service tier* to *cluster segment*. However, the impact ceiling is availability degradation, NOT domain takeover, fleet compromise, data exfiltration, or supply-chain pivot. No plausible high-value-role outcome reaches the HIGH-floor threshold because the bug produces only a process crash with no follow-on compromise capability. The component (grpc-go xDS server mode) is not canonically a DC, IdP, CA, backup system, or network edge appliance — it is an application-layer transport library used in microservices.
Why not higher?
The bug is DoS-only with no path to code execution, data theft, or lateral movement. The affected population is narrowed to the xDS subset of grpc-go users (~5–15% of deployments), and within that subset, mTLS — the production default in service mesh environments — blocks unauthenticated exploitation. Kubernetes auto-restart further caps the real-world outage window. No high-value-role scenario produces domain/fleet/supply-chain-scale compromise; the worst case is temporary service disruption in one tier.
Why not lower?
Within the affected xDS population, exploitation is trivially easy: a single unauthenticated HTTP/2 frame in non-mTLS configs kills the entire server process — no special tooling, no race condition, no user interaction. No vendor workaround exists short of upgrading. The EPSS of 0.685% (49th percentile) is non-negligible, and the Go vuln database has cataloged it (GO-2026-6443), ensuring wide scanner coverage. The attack is deterministic and repeatable, making it viable for targeted disruption even if not for broad exploitation.
What to do — in priority order.
- Upgrade grpc-go to v1.82.2 or v1.83.2 — The definitive fix. Both patch versions reject requests missing
:authorityandHostat the HTTP/2 transport layer before they reach the xDS routing interceptor. Per the noisgate remediation SLA for MEDIUM, deploy within 365 days. If you actively usexds.NewGRPCServer()in production, prioritize this within weeks — the upgrade is a drop-in dependency bump with no API changes. - Enable mTLS or ALTS on all xDS-enabled gRPC servers — mTLS requires valid client certificates before the transport handshake completes, blocking unauthenticated exploitation entirely. Istio and Traffic Director support mTLS as a mesh-wide policy (
PeerAuthenticationin STRICT mode). This should be your default posture regardless of this CVE. No mitigation SLA applies for MEDIUM severity — go straight to the remediation window. - Deploy an L7 proxy or Envoy sidecar in front of xDS servers — A compliant HTTP/2 intermediary (Envoy, nginx, HAProxy) can validate or inject the
:authorityheader before traffic reaches the gRPC server. Envoy'shttp2_protocol_optionsand HTTP connection manager will typically reject or normalize requests missing:authority, acting as a pre-filter. If you're using Istio with sidecars alongside proxyless gRPC, ensure the sidecar is the ingress point. - Configure Kubernetes liveness probes and PodDisruptionBudgets — Ensure liveness probes detect crashed gRPC servers and trigger rapid restarts. Set
PodDisruptionBudgetwithminAvailableto maintain service capacity during crash-restart cycles. This limits sustained DoS impact from repeated exploitation but does NOT prevent individual crashes. - Monitor for crash-loop signatures — Alert on
kube_pod_container_status_restarts_totalspikes and grep container logs forruntime error: index out of range [0] with length 0inrouting.go. This provides post-exploitation detection and helps distinguish CVE-2026-84445 crashes from other Go panics.
- Rate limiting on the gRPC port — a single request triggers the crash, so rate limits at any practical threshold cannot prevent the initial kill. They only slow repeated exploitation after the first crash.
- Application-layer WAF rules — gRPC uses HTTP/2 binary framing with protobuf payloads. Most WAFs inspect HTTP/1.1 text or JSON bodies and cannot reliably parse HTTP/2 pseudo-headers at the frame level to detect missing
:authority. - Go recover() wrappers in application handlers — the panic occurs in the xDS routing interceptor *before* the application handler is invoked, so application-level recovery middleware never sees it.
- Upgrading to grpc-go 1.83.0 or 1.83.1 — these versions are still in the vulnerable range. You need 1.83.2 or higher.
Crowdsourced verification payload.
Run this script on any machine with access to the Go project source tree (developer workstation, CI runner, or build server). It requires no special privileges — only read access to go.mod and *.go source files. Invoke as: bash check_cve_2026_84445.sh /path/to/your/go/project
#!/usr/bin/env bash
# CVE-2026-84445 Verification Script
# Checks if a Go project uses a vulnerable version of google.golang.org/grpc
# AND whether xds.NewGRPCServer() is in use (the only affected code path).
#
# Usage: bash check_cve_2026_84445.sh /path/to/go/project
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit: 1 = vulnerable, 0 = patched, 2 = unknown
set -euo pipefail
PROJECT_DIR="${1:-.}"
GOMOD="$PROJECT_DIR/go.mod"
if [[ ! -f "$GOMOD" ]]; then
echo "UNKNOWN - No go.mod found at $GOMOD"
exit 2
fi
# Extract grpc-go version from go.mod (direct or indirect)
GRPC_LINE=$(grep -E 'google\.golang\.org/grpc\s+v' "$GOMOD" | head -1 || true)
if [[ -z "$GRPC_LINE" ]]; then
echo "UNKNOWN - google.golang.org/grpc not found in go.mod"
exit 2
fi
GRPC_VERSION=$(echo "$GRPC_LINE" | awk '{print $2}' | sed 's/^v//')
echo "[*] Found google.golang.org/grpc version: v$GRPC_VERSION"
# Check for xds.NewGRPCServer usage in Go source files
XDS_USED="no"
if grep -rq 'xds\.NewGRPCServer\|"google\.golang\.org/grpc/xds"' "$PROJECT_DIR" --include='*.go' 2>/dev/null; then
XDS_USED="yes"
echo "[!] xds.NewGRPCServer() or xds import detected in source code"
else
echo "[i] No xds.NewGRPCServer() usage found (standard grpc.NewServer is NOT affected)"
fi
# Portable version comparison: returns 0 if $1 >= $2
version_gte() {
printf '%s\n%s\n' "$2" "$1" | sort -V -C
}
# Determine vulnerability status
# Vulnerable ranges: < 1.82.2 OR (>= 1.83.0 AND < 1.83.2)
VULN="no"
if version_gte "$GRPC_VERSION" "1.83.0" && ! version_gte "$GRPC_VERSION" "1.83.2"; then
VULN="yes"
RANGE="[1.83.0, 1.83.2)"
elif ! version_gte "$GRPC_VERSION" "1.82.2"; then
VULN="yes"
RANGE="(< 1.82.2)"
fi
if [[ "$VULN" == "yes" ]]; then
if [[ "$XDS_USED" == "yes" ]]; then
echo "VULNERABLE - grpc-go v$GRPC_VERSION in range $RANGE with xds.NewGRPCServer() usage"
echo " -> Upgrade to v1.82.2+ or v1.83.2+ immediately."
exit 1
else
echo "VULNERABLE - grpc-go v$GRPC_VERSION in range $RANGE (dependency is vulnerable)"
echo " -> xds.NewGRPCServer() not detected in source; effective risk is LOW unless xDS is used at runtime."
echo " -> Upgrade to v1.82.2+ or v1.83.2+ as part of routine maintenance."
exit 1
fi
else
echo "PATCHED - grpc-go v$GRPC_VERSION is not in the vulnerable range"
exit 0
fiWhat defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.