Like a toll booth operator re-reading the same license plate from scratch for every car in line
CVE-2026-27145 is an algorithmic-complexity denial-of-service in Go's standard library crypto/x509 package. The (*x509.Certificate).VerifyHostname function called matchHostnames inside a loop over every DNS Subject Alternative Name (SAN) entry, redundantly executing strings.Split(host, ".") on the same hostname each iteration. A certificate with *N* SAN entries and a hostname with *L* labels produces O(N × L) string-split operations — quadratic cost. Critically, this parsing fires before certificate chain validation, so even an untrusted, self-signed certificate triggers the CPU burn. All Go binaries built with the stdlib crypto/x509 on Go < 1.25.11 or Go 1.26.0–1.26.3 are affected — that's every TLS client written in Go.
The upstream vendor scored this CVSS 6.5 MEDIUM (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:H). Red Hat disagreed and scored it 7.5 HIGH with a different vector (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H), arguing — correctly — that attack complexity is low and there is no integrity impact, only availability. The truth sits between these two: the attack is trivially crafted, but the attacker must control the TLS server endpoint the victim connects to, which narrows real-world exposure. The original vendor vector's I:L (integrity) component is incorrect; this is a pure DoS. On balance, 6.5 MEDIUM captures the practical risk for most enterprises.
4 steps from start to impact.
Attacker stands up malicious TLS endpoint
crypto/tls or OpenSSL can generate such a certificate in seconds.- Attacker controls a reachable TLS server or can MITM a connection
- Victim Go process must initiate outbound TLS to attacker-controlled or attacker-intercepted endpoint
- Enterprise egress firewalls / proxy configurations limit which hosts internal Go services can reach
Victim Go client connects to attacker endpoint
crypto/tls or net/http — initiates a TLS handshake with the malicious server. This happens naturally when Go services call external webhooks, fetch URLs from user input, pull container images, contact API backends, or resolve OIDC discovery endpoints. The TLS handshake begins and the server presents the oversized-SAN certificate.- Victim application makes outbound TLS connections to an endpoint the attacker controls or influences
- Application is built with Go < 1.25.11 or Go 1.26.0–1.26.3
- Many Go services only connect to pre-configured, trusted internal endpoints
- Container registries and API endpoints are often pinned or allow-listed
- MITM requires network position the attacker typically doesn't have
VerifyHostname triggers quadratic CPU burn
x509.Certificate.VerifyHostname iterates over every DNS SAN entry and redundantly splits the candidate hostname on each pass. With 50,000 SANs and a hostname with 10 labels, the function performs ~500,000 string-split operations instead of ~10. A single goroutine can pin a CPU core for seconds to minutes depending on SAN count. Multiple concurrent connections amplify this to full CPU exhaustion.- Certificate reaches the VerifyHostname path (default for all TLS clients)
- Go's runtime scheduler may limit the blast to the goroutine pool; other goroutines can still progress unless all cores are saturated
- Single-connection impact may be noticeable but not fatal — sustained attack requires repeated connections
container_cpu_usage_seconds_total in Prometheus) will show sustained 100% CPU; profiling will point to crypto/x509.(*Certificate).VerifyHostnameService denial achieved
- Attacker can sustain multiple concurrent connections
- Rate limiting on inbound connections to the attacker's server (from the victim's perspective, outbound rate limiting is rare but possible)
- Go services behind load balancers may distribute the load across replicas, requiring the attacker to target each independently
api.internal.corp, deny everything else. This breaks step 2 of the attack path — the victim never connects to the attacker. No mitigation SLA applies for MEDIUM, but this is good hygiene to implement alongside patching.crypto/x509.(*Certificate).VerifyHostname (visible via Go pprof or eBPF-based profiling) is a strong indicator of this attack. This doesn't prevent the DoS but limits its duration by enabling rapid response.- WAF / ingress filtering — this is a *client-side* vulnerability triggered by a *server's* certificate. WAFs inspect inbound requests, not outbound TLS handshake payloads. Completely irrelevant.
- Certificate pinning — pinning validates the certificate *identity* but does not skip hostname verification. The quadratic parsing still fires before the pin check.
- Upgrading only
golang.org/x/crypto— this CVE is in the Go *stdlib*crypto/x509, not in thex/cryptomodule. Bumpingx/cryptoto v0.54.0 (seen in some PRs) fixes a *different* CVE. You must bump the Go *toolchain* itself.
The supporting signals.
| In-the-wild exploitation | None observed. Not listed in CISA KEV. No GreyNoise or Shadowserver tags. No vendor advisories reference active campaigns. |
|---|---|
| Proof of concept | No functional PoC available. HORKimhab/CVE-2026-27145 on GitHub is an empty template repo with no exploit code. pocindex.io returns no results. Crafting a triggering certificate is trivial with openssl or Go's crypto/x509 — a researcher could write one in under an hour — but no weaponized tooling exists publicly. |
| EPSS | 0.00591 (0.59%) — 46th percentile. Below the threshold where EPSS-driven prioritization would flag this for urgent action. |
| KEV status | Not listed. No CISA KEV entry as of 2026-09-27. |
| CVSS vector (cve.org) | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:H → 6.5 MEDIUM. The I:L component appears incorrect; Red Hat's vector (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H → 7.5) better reflects the pure-availability impact. |
| Affected versions | Go stdlib crypto/x509: all versions before Go 1.25.11 and Go 1.26.0 through 1.26.3. Every Go binary compiled with an affected toolchain is vulnerable. |
| Fixed versions | Go 1.25.11 and Go 1.26.4. Distro backports: Red Hat has issued multiple RHSAs (RHSA-2026 series). SUSE and Ubuntu are tracking. |
| Scanning / exposure | No Shodan/Censys/FOFA dork applies — this is a client-side library vulnerability, not an exposed service fingerprint. Detection requires binary composition analysis (SCA/SBOM) to identify Go toolchain version. |
| Disclosure timeline | Reported 2026-05-27 by Jakub Ciolek (ciolek.dev). Published 2026-06-02. Go advisory GO-2026-5037. Fix merged in CL 783621. |
| Reporting researcher | Jakub Ciolek ([email protected], ciolek.dev) |
Sources.
Why this verdict
- DoS-only impact caps severity. There is no path from this vulnerability to code execution, data access, or privilege escalation. The worst outcome is CPU exhaustion and service unavailability — serious but bounded.
- Attacker must control the far side of a TLS connection. The victim Go process must *initiate* a TLS handshake to an attacker-controlled or attacker-intercepted endpoint. This is not a "spray the internet" attack; it requires the attacker to be positioned as the server the victim connects to, or to achieve MITM.
- No exploitation activity and trivial-but-absent PoC. EPSS is 0.59% (46th percentile), no KEV listing, no GreyNoise tags, and no public weaponized tooling. The vulnerability is easy to trigger in theory but nobody is bothering — DoS bugs in client-side TLS parsing are low-value targets for real adversaries.
- Role multiplier: Go is the primary language for Kubernetes, Docker, HashiCorp tools, and cloud-native infrastructure. In high-value roles (kube-apiserver, kubelet, Vault, CI/CD runners), a DoS could disrupt cluster operations or secret retrieval. However, the blast radius is *availability* of those components, not domain takeover, fleet compromise, or supply-chain pivot. The outcome does not meet the HIGH floor threshold because the worst-case is temporary service disruption, not the catastrophic outcomes enumerated in the floor rule. Kubernetes control-plane components typically run as replicated pods behind load balancers, further limiting single-process DoS impact.
- Red Hat's 7.5 is the ceiling, not the midpoint. Red Hat correctly fixed the CVSS vector (AC:L, I:N) but their score assumes every Go binary is equally reachable, which overstates the attacker's ability to force outbound connections to arbitrary endpoints in hardened environments.
Why not higher?
This vulnerability cannot reach HIGH because the impact is strictly availability — no confidentiality or integrity breach is possible. The attacker cannot leverage this for lateral movement, data exfiltration, or privilege escalation. Even in high-value deployment roles like Kubernetes control plane, the outcome is temporary CPU exhaustion of individual Go processes, not domain-level or fleet-level compromise. The floor rule requires outcomes like domain takeover, mass data egress, or supply-chain pivot, none of which are achievable here.
Why not lower?
This cannot drop to LOW because the attack surface is genuinely broad — every Go TLS client is affected — and the attack requires no authentication, no special privileges, and works against untrusted certificates (pre-chain-validation). The crafting of a triggering certificate is trivial. Go's dominance in infrastructure software means the total population of affected binaries is enormous, even if each individual binary's exposure to attacker-controlled endpoints varies. A sustained attack against a Go-based API gateway or ingress controller could cause real outages.
Crowdsourced verification payload.
Run this script on each host or in CI to check the Go toolchain version used to compile any Go binary. Pass the path to the binary as the first argument. Requires go version (ships with Go) or can parse the binary directly. No special privileges needed. Example: bash check_cve_2026_27145.sh /usr/local/bin/kubectl
#!/usr/bin/env bash
# check_cve_2026_27145.sh — Detect Go binaries vulnerable to CVE-2026-27145
# Usage: bash check_cve_2026_27145.sh /path/to/go-binary
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
BINARY="${1:-}"
if [[ -z "$BINARY" ]]; then
echo "Usage: $0 /path/to/go-binary"
exit 2
fi
if [[ ! -f "$BINARY" ]]; then
echo "UNKNOWN — file not found: $BINARY"
exit 2
fi
# Extract Go version from binary
GO_VERSION=$(go version "$BINARY" 2>/dev/null | grep -oP 'go[0-9]+\.[0-9]+\.?[0-9]*' | head -1)
if [[ -z "$GO_VERSION" ]]; then
echo "UNKNOWN — could not determine Go version for $BINARY"
exit 2
fi
echo "Detected Go version: $GO_VERSION"
# Parse major.minor.patch
VERSION_NUM=$(echo "$GO_VERSION" | sed 's/^go//')
MAJOR=$(echo "$VERSION_NUM" | cut -d. -f1)
MINOR=$(echo "$VERSION_NUM" | cut -d. -f2)
PATCH=$(echo "$VERSION_NUM" | cut -d. -f3)
PATCH=${PATCH:-0}
# Fixed in: go1.25.11+ or go1.26.4+
# Affected: all go < 1.25.11, and go 1.26.0 through 1.26.3
if [[ "$MAJOR" -lt 1 ]]; then
echo "VULNERABLE — $GO_VERSION is affected by CVE-2026-27145"
exit 1
fi
if [[ "$MAJOR" -eq 1 ]]; then
if [[ "$MINOR" -lt 25 ]]; then
echo "VULNERABLE — $GO_VERSION < go1.25.11"
exit 1
elif [[ "$MINOR" -eq 25 ]]; then
if [[ "$PATCH" -lt 11 ]]; then
echo "VULNERABLE — $GO_VERSION < go1.25.11"
exit 1
else
echo "PATCHED — $GO_VERSION >= go1.25.11"
exit 0
fi
elif [[ "$MINOR" -eq 26 ]]; then
if [[ "$PATCH" -lt 4 ]]; then
echo "VULNERABLE — $GO_VERSION < go1.26.4"
exit 1
else
echo "PATCHED — $GO_VERSION >= go1.26.4"
exit 0
fi
else
# go1.27+ is not affected
echo "PATCHED — $GO_VERSION is newer than affected range"
exit 0
fi
fi
echo "PATCHED — $GO_VERSION is newer than affected range"
exit 0