The front door checks your badge but not if you wrote your name in crayon
gRPC-Go versions before 1.79.3 accepted HTTP/2 requests where the :path pseudo-header omitted the mandatory leading slash — Service/Method instead of /Service/Method. The server's routing layer found the right handler regardless, but the authorization engine (google.golang.org/grpc/authz or any custom interceptor reading info.FullMethod) evaluated the raw, un-normalized path string. Deny rules written against canonical paths (starting with /) never matched the slash-less variant. If the policy had a fallback allow rule — the common "deny these sensitive methods, allow everything else" pattern — the request sailed through. Affected: all google.golang.org/grpc versions prior to v1.79.3. Fixed in v1.79.3 (PR #8981).
The vendor-assigned CVSS 9.1 CRITICAL is technically defensible for the worst case: an internet-facing gRPC server using the official authz interceptor with deny+allow policies. In practice, it overstates the risk for most enterprise deployments. The vast majority of gRPC-Go services sit behind L7 proxies (Envoy, NGINX, AWS ALB, GCP CLB) that enforce RFC 9113 path compliance and would reject or normalize the malformed :path before it reaches the Go process. The vulnerability also requires a specific authorization configuration — path-based interceptors with deny rules *and* a fallback allow — that represents a subset of all gRPC-Go deployments. Many teams rely on service mesh authorization, mTLS, or external API gateways rather than gRPC-Go's built-in RBAC. These friction points compound to bring real-world severity from CRITICAL down to HIGH.
5 steps from start to impact.
Identify a gRPC-Go endpoint
grpcurl or Shodan dorks for gRPC reflection endpoints surface candidates. The attacker needs to reach the gRPC server's TCP port with HTTP/2.- Network reachability to the gRPC endpoint (direct internet exposure or internal network position)
- Most gRPC services are internal microservices not exposed to the internet
- Typically sit behind L7 load balancers, API gateways, or service mesh sidecars
Confirm path-based authorization is active
/AdminService/DeleteUser) and observes a PERMISSION_DENIED or UNAUTHENTICATED response. This confirms path-based RBAC is enforced. Without this confirmation, the bypass has no effect — a server with no authz interceptor simply handles every request.- Server must use
google.golang.org/grpc/authzor a custom interceptor readinginfo.FullMethod - Authorization policy must use deny rules with a fallback allow
- Many deployments use external authorization (service mesh, API gateway, mTLS identity) rather than gRPC-Go's built-in authz
- The official
grpc/authzpackage sees moderate but not universal adoption
Craft a slash-less :path HTTP/2 frame
h2 library, curl --http2, or any raw HTTP/2 frame writer, the attacker constructs a HEADERS frame where :path omits the leading slash: AdminService/DeleteUser instead of /AdminService/DeleteUser. The public PoC by JohannesLks is a ~50-line Python script. No credentials, cookies, or tokens are needed.- Ability to send raw HTTP/2 frames (not just standard gRPC client calls, which always include the leading slash)
- Essentially none — tooling is freely available and trivial to use
:path for RFC 9113 compliance (missing leading slash); gRPC-Go verbose logging may capture non-canonical pathBypass deny rules via path mismatch
AdminService/DeleteUser against its policy. The deny rule targeting /AdminService/DeleteUser does not match because the leading / is missing. The policy falls through to the default allow rule, and the request is authorized.- Policy must have explicit deny rules (with canonical paths) AND a fallback allow rule
- A deny-all-by-default policy is NOT bypassed
- The deny+allow pattern is common but not universal
- Strict deny-by-default policies are immune
Execute unauthorized operations
- Protected methods must not have secondary authorization checks (per-field validation, row-level security, handler-level auth)
- Well-designed services may have defense-in-depth with handler-level auth checks
go get google.golang.org/[email protected] && go mod tidy in every affected module, rebuild, and redeploy. The patch rejects slash-less :path values at the transport layer before any interceptor runs. One-line dependency bump with no breaking API changes. Per noisgate mitigation SLA for HIGH, deploy within 30 days.info.FullMethod starts with / and returns codes.InvalidArgument if not. This is the vendor-recommended interim mitigation. Deploy within 30 days per the noisgate mitigation SLA for HIGH.:path does not start with /. Envoy's normalize_path and NGINX's grpc_pass with strict path checking accomplish this. Effective only when ALL paths to the gRPC server transit the proxy. Deploy within 30 days per the noisgate mitigation SLA for HIGH.- Network-level firewalling (L3/L4): Blocking ports or IPs does not help if the attacker has legitimate network access. The exploit travels over a normal HTTP/2 connection on the standard port.
- mTLS between services: mTLS authenticates caller identity but does not validate
:pathheader content. A compromised service with a valid mTLS certificate can still send slash-less paths. - Rate limiting: The exploit requires a single request. Rate limiting has no meaningful effect on a one-shot authorization bypass.
- Standard gRPC client library hardening: All well-behaved gRPC clients automatically include the leading slash. The exploit requires a raw HTTP/2 frame writer, so hardening standard clients does nothing against an attacker who bypasses them.
The supporting signals.
| In-the-wild exploitation | No evidence. Not listed in CISA KEV. No campaigns attributed. No GreyNoise or Shadowserver tags observed as of 2026-10-08. |
|---|---|
| Proof of concept | Public PoC available. JohannesLks/CVE-2026-33186 — Python script using h2 library, ~50 lines, sends baseline/attack/control requests. Published shortly after disclosure. 1 star, 0 forks. No Metasploit module or Nuclei template identified. |
| EPSS | 1.625% (75th percentile). Low probability of exploitation in next 30 days relative to all CVEs, but notably above median. |
| CISA KEV | Not listed as of 2026-10-08. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (9.1). Network-attackable, low complexity, no privileges, no interaction. High C+I, no availability impact. |
| Affected versions | google.golang.org/grpc < v1.79.3. All prior versions using grpc/authz or custom FullMethod-based interceptors with deny+allow policies. |
| Fixed version | v1.79.3. Fix rejects any :path without a leading slash at the transport layer with codes.Unimplemented. Patch: PR #8981. |
| Downstream impact | Confirmed affected dependencies: Traefik (GHSA-46wh-3698-f2cx), Kubernetes cluster-autoscaler v1.35.0, Envoy Gateway v1.6.6, DataDog Agent (#48151), Istio Service Bridge. |
| Disclosure timeline | GitHub Advisory (GHSA-p77j-4mvh-x3m3): 2026-03-17. NVD: 2026-03-20. Go Vuln DB: GO-2026-4762. |
| Reporter | MariuszMaik (@MariuszMaik on GitHub). |
Sources.
Why this verdict
- Configuration prerequisite narrows the population. The bypass requires three conditions: vulnerable gRPC-Go version + path-based authz interceptor (
grpc/authzor customFullMethod-based) + deny rules with a fallback allow. A deny-by-default policy is immune. Estimated ~15–25% of gRPC-Go servers use built-in path-based RBAC with this policy shape. - L7 proxy layer provides natural mitigation. Envoy (Istio sidecars), NGINX 1.31.4+, AWS ALB, and GCP CLB enforce RFC 9113 compliance on HTTP/2
:pathheaders. Any gRPC service behind these proxies is protected without patching. In enterprise microservice architectures, direct client-to-server gRPC without an intermediary proxy is the exception. - Internal network positioning required for most targets. gRPC services are overwhelmingly internal microservices. Direct internet exposure of raw gRPC endpoints is rare in enterprise environments. Exploitation against internal services implies a prior compromise stage.
- Role multiplier: gRPC-Go is a library, not a high-value component by definition. It runs *inside* high-value infrastructure (Kubernetes, Istio, Traefik, DataDog), but those projects implement their own authorization logic rather than relying on
grpc/authz. Kubernetes uses its own RBAC and admission controllers. Istio enforces authorization at the sidecar level. Traefik has its own middleware chain. The downstream advisories are about the dependency being present, not the auth bypass being exercisable. For custom microservices that *do* usegrpc/authz— financial APIs, healthcare backends, admin services — the blast radius is service-scoped (data exfiltration, unauthorized operations) but does not cascade to domain takeover, fleet compromise, or supply-chain pivot. - Role multiplier floor: HIGH. When a custom Go microservice deliberately uses
grpc/authzto protect sensitive administrative or data-access methods (financial transactions, patient records, admin APIs), the bypass grants full unauthenticated access. This represents a plausible high-value-role outcome (mass data egress) for a non-trivial fraction of the installed base. The floor is HIGH.
Why not higher?
The three-condition prerequisite chain (vulnerable version + authz interceptor + deny+allow policy) compounds with L7 proxy mitigation and internal-network positioning to substantially narrow the reachable population. Confirmed high-value downstream projects (Kubernetes, Istio, Traefik) do not exercise the vulnerable code path for their own authorization. No in-the-wild exploitation after 6+ months. EPSS at the 75th percentile — elevated but not alarming.
Why not lower?
The vulnerability requires zero credentials and zero user interaction. When conditions align, every deny rule in the policy is defeated — complete authorization bypass. A public PoC exists and exploitation is trivial (~50 lines of Python). Custom microservices that chose grpc/authz for access control to sensitive methods face full bypass, and those deployments represent a non-negligible share of the gRPC-Go ecosystem. The floor holds at HIGH.
Crowdsourced verification payload.
Run on any host where your Go modules are built or checked out. It inspects go.mod / go.sum to determine whether the installed google.golang.org/grpc version is vulnerable. No special privileges required. Usage: bash check_cve_2026_33186.sh /path/to/your/go/module
#!/usr/bin/env bash
# check_cve_2026_33186.sh — Detect CVE-2026-33186 (gRPC-Go auth bypass)
# Usage: bash check_cve_2026_33186.sh /path/to/go/module
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
MOD_DIR="${1:-.}"
GOMOD="${MOD_DIR}/go.mod"
FIXED_VERSION="1.79.3"
if [[ ! -f "$GOMOD" ]]; then
echo "UNKNOWN — no go.mod found at ${GOMOD}"
exit 2
fi
# Extract the google.golang.org/grpc version from go.mod
GRPC_LINE=$(grep -E '^\s+google\.golang\.org/grpc\s+v' "$GOMOD" | head -1 || true)
if [[ -z "$GRPC_LINE" ]]; then
GRPC_LINE=$(grep -E '^module\s+google\.golang\.org/grpc' "$GOMOD" || true)
if [[ -n "$GRPC_LINE" ]]; then
echo "UNKNOWN — this IS the grpc-go module itself; check its version tag"
exit 2
fi
if [[ -f "${MOD_DIR}/go.sum" ]]; then
GRPC_LINE=$(grep -E 'google\.golang\.org/grpc\s+v' "${MOD_DIR}/go.sum" | tail -1 || true)
fi
if [[ -z "$GRPC_LINE" ]]; then
echo "UNKNOWN — google.golang.org/grpc not found in go.mod or go.sum"
exit 2
fi
fi
VERSION=$(echo "$GRPC_LINE" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 | sed 's/^v//')
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN — could not parse grpc-go version from: $GRPC_LINE"
exit 2
fi
MIN_SAFE=$(printf '%s\n%s' "$VERSION" "$FIXED_VERSION" | sort -V | head -1)
if [[ "$MIN_SAFE" == "$FIXED_VERSION" ]] || [[ "$VERSION" == "$FIXED_VERSION" ]]; then
echo "PATCHED — google.golang.org/grpc v${VERSION} >= v${FIXED_VERSION}"
exit 0
else
echo "VULNERABLE — google.golang.org/grpc v${VERSION} < v${FIXED_VERSION} (CVE-2026-33186)"
echo " Fix: go get google.golang.org/grpc@v${FIXED_VERSION} && go mod tidy"
exit 1
fi