This is a tollbooth bug where every extra SAN lane makes the same hostname pay again
In Go's crypto/x509, (*x509.Certificate).VerifyHostname used to re-split the same input hostname for every DNS SAN entry it checked. That turns certificate validation into avoidable CPU work when a certificate carries a large DNS SAN list, and the cost lands *before* chain building, so even an otherwise untrusted certificate can trigger it. Supported fixed releases are go1.25.11 and go1.26.4; on supported branches, treat anything earlier than those as affected. Older unsupported branches likely inherited the same code path, but Go's current release stream only documents fixes on 1.25 and 1.26.
There is no vendor CVSS baseline here, so this is ASSESSED AT LOW rather than upgraded or downgraded. The technical trigger is real, but the practical attack surface is narrow: this is mostly a *client-side* outbound TLS problem, the attacker usually needs control of the server certificate on a destination the victim already contacts, and the impact is CPU burn / latency / connection degradation rather than code execution, auth bypass, or data theft.
4 steps from start to impact.
Control a TLS endpoint the Go client will talk to
- Victim uses Go
crypto/x509hostname verification, directly or throughx509.Verify/ TLS client code - Attacker can cause the victim to initiate a TLS connection to an attacker-controlled or attacker-influenced endpoint
- This is not a generic internet-facing server bug; it is mostly an outbound client validation path
- Many enterprise workloads only talk to fixed allowlisted destinations, so random internet attackers cannot usually reach this code path
govulncheck, and inventory of Go client binaries.Serve a certificate bloated with DNS SAN entries
- Attacker can present an arbitrary certificate chain to the victim
- Certificate can carry a large enough SAN set to matter operationally
- Certificate size and handshake practicality cap how absurd this can get
- Some upstream components or test harnesses may reject malformed or unusually large certificates before they become useful
Trigger repeated hostname splitting before trust is even decided
x509.Verify performs hostname validation before chain building, the attacker gets the CPU hit even if the certificate would later fail trust validation.- Victim is on a vulnerable Go toolchain path
- Hostname verification is actually invoked with
VerifyOptions.DNSNameor normal TLS client hostname checks
- Strictly valid DNS hostnames are length-limited, so amplification is bounded in the common case
- Some applications sanitize or clamp hostnames before passing them into Go
crypto/x509 during failed handshakes. External IDS/WAF products generally will not surface this as a distinct signature.Degrade the client, not the trust boundary
- Victim performs enough outbound handshakes to make the extra CPU meaningful
- Attacker can repeat the interaction or fan it across many clients
- Single-handshake impact is finite and often just looks like slowness
- Blast radius is usually limited to the client process or service making the outbound call
The supporting signals.
| In-the-wild status | No evidence of active exploitation found in the sources reviewed, and not listed in CISA KEV. |
|---|---|
| Proof-of-concept availability | No standalone public exploit repo was identified; the public technical detail is the Go security issue itself, which is enough for a competent operator to reproduce with standard cert-generation tooling. |
| EPSS | No published FIRST EPSS score was observed at time of review; this is common for very fresh disclosures. |
| KEV status | Not in KEV as of 2026-06-03; no KEV add date or due date exists. |
| Affected component | Go standard library crypto/x509, specifically (*x509.Certificate).VerifyHostname and flows that call x509.Verify with VerifyOptions.DNSName. |
| Affected versions | On supported Go branches, treat < go1.25.11 and < go1.26.4 as affected. Older unsupported branches likely contain the same behavior, but Go's current security release notes only document fixed supported lines. |
| Fixed versions | Fixed in go1.25.11 and go1.26.4. |
| CVSS / vector reality | There is no vendor CVSS or vector. Practically this behaves like a constrained network-triggered availability issue where attacker position and reachability dominate risk more than raw code-path severity. |
| Scanning / exposure reality | Shodan/Censys/FOFA are the wrong lens here: this is a linked standard-library bug, not a bannered appliance. You need SBOM/SCA, govulncheck, or go version -m against deployed binaries. |
| Disclosure / credit | Publicly disclosed 2026-06-02. The Go issue credits Jakub Ciolek for reporting. |
noisgate verdict.
The decisive factor is attacker position: this usually requires control of a TLS endpoint the victim's Go client already contacts, which sharply limits reachable population compared with a normal pre-auth server-side bug. The impact is also strictly availability-oriented CPU overhead, with no trust bypass, code execution, or credential exposure in the observed behavior.
Why this verdict
- Attacker position pushes this down: this is mostly an *outbound client-side* TLS verification bug, so the attacker generally must control the destination service or otherwise steer the victim to an attacker-controlled TLS endpoint.
- Impact is availability-only: the bug burns CPU during hostname validation; it does not let the attacker forge trust, bypass hostname checks, read data, or run code.
- Reachable population is narrower than Go's install base: Go is widespread, but only the subset doing vulnerable hostname validation against attacker-influenced endpoints is realistically exposed.
- A prior-compromise or influence stage is often implied: internal service discovery poisoning, SSRF, redirect abuse, malicious webhooks, or tricking clients into new outbound destinations are common prerequisites in enterprise reality.
- Modern controls can break the chain early: egress allowlisting, outbound proxy policy, strict destination pinning, and connection timeouts all reduce the exploit path before the stdlib bug matters.
- Detection is poor but exploit economics are mediocre: scanners will miss it, but attackers also get only a bounded CPU tax rather than a durable foothold.
Why not higher?
This is not an unauthenticated internet-facing server RCE, auth bypass, or direct confidentiality event. The attacker usually needs the victim to connect outward to a hostile TLS endpoint, and even then the practical result is performance degradation rather than a clean security boundary break.
Why not lower?
It is still a real security fix, not just a cosmetic optimization: untrusted certificates can trigger the extra work before chain validation finishes. In environments with many Go clients making attacker-influenced outbound TLS connections, repeated handshakes can turn this into measurable operational pain.
What to do — in priority order.
- Clamp outbound destinations — Enforce egress allowlists or proxy-based destination control so Go clients cannot be pointed at arbitrary TLS endpoints. For a LOW verdict there is no SLA clock here, but put this into normal control hardening and apply it during the next routine policy refresh.
- Bound handshake cost — Set conservative dial, TLS handshake, and request timeouts on Go clients and reverse proxies so certificate-validation stalls cannot pile up into worker exhaustion. For LOW, roll this into standard engineering backlog hygiene rather than an emergency change.
- Inventory linked Go binaries — Use
go version -mor SBOM tooling to identify binaries built withgo1.25.10/earlier on the 1.25 line andgo1.26.3/earlier on the 1.26 line. This is the fastest way to separate truly exposed assets from noise. - Reject absurd host inputs in app code — If your application accepts user-supplied hostnames before making outbound TLS connections, enforce sane hostname length and label-count checks before those values ever reach Go's verifier. That shrinks the worst-case path even before the toolchain update lands.
- Rotating trusted roots or tightening CA stores does not solve the trigger path, because the CPU cost happens before trust chain success matters.
- Ingress WAF rules usually do not help, because this is commonly an outbound client validation path rather than an inbound web request bug.
- Relying on vulnerability scanners alone does not work; this is a linked library behavior problem, so unauthenticated network scanning typically cannot see it.
Crowdsourced verification payload.
Run this on the target host or in CI against the actual deployed Go binaries, not just the build runner. Invoke it as ./check-cve-2026-27145.sh /opt/app/bin/service or with multiple binaries; if you pass no arguments it falls back to the locally installed go toolchain. No root is required, but you need read access to the binaries and go in PATH if you want the fallback check.
#!/usr/bin/env bash
# check-cve-2026-27145.sh
# Determine likely exposure to CVE-2026-27145 in Go binaries/toolchains.
# Exit codes:
# 0 = PATCHED
# 1 = VULNERABLE
# 2 = UNKNOWN / usage / cannot determine
set -u
status=0
saw_unknown=0
extract_go_version() {
local target="$1"
local out ver
out=$(go version -m "$target" 2>/dev/null | head -n1) || return 1
ver=$(printf '%s\n' "$out" | awk '{print $2}')
[ -n "$ver" ] || return 1
printf '%s\n' "$ver"
}
extract_local_toolchain() {
local out ver
out=$(go version 2>/dev/null) || return 1
ver=$(printf '%s\n' "$out" | awk '{print $3}')
[ -n "$ver" ] || return 1
printf '%s\n' "$ver"
}
classify_version() {
local ver="$1"
local clean major minor patch rest
clean="${ver#go}"
clean="${clean%%[- ]*}"
case "$clean" in
devel*|tip*)
echo "UNKNOWN"
return 0
;;
esac
IFS='.' read -r major minor patch rest <<EOF
$clean
EOF
if ! [[ "$major" =~ ^[0-9]+$ && "$minor" =~ ^[0-9]+$ ]]; then
echo "UNKNOWN"
return 0
fi
if ! [[ "${patch:-}" =~ ^[0-9]+$ ]]; then
patch=0
fi
# Supported fixed releases documented by Go:
# 1.25.11+
# 1.26.4+
# Assume newer branches carry the fix.
if (( major > 1 )); then
echo "PATCHED"
return 0
fi
if (( major < 1 )); then
echo "UNKNOWN"
return 0
fi
if (( minor >= 27 )); then
echo "PATCHED"
return 0
fi
if (( minor == 26 )); then
if (( patch >= 4 )); then
echo "PATCHED"
else
echo "VULNERABLE"
fi
return 0
fi
if (( minor == 25 )); then
if (( patch >= 11 )); then
echo "PATCHED"
else
echo "VULNERABLE"
fi
return 0
fi
# Older/EOL lines are not covered by the current supported-branch advisory.
# They may still contain the bug, but we avoid guessing.
echo "UNKNOWN"
}
report_one() {
local label="$1"
local ver="$2"
local verdict
verdict=$(classify_version "$ver")
printf '%s: %s (%s)\n' "$label" "$verdict" "$ver"
case "$verdict" in
VULNERABLE)
status=1
;;
UNKNOWN)
saw_unknown=1
;;
esac
}
if [ "$#" -gt 0 ]; then
if ! command -v go >/dev/null 2>&1; then
echo "UNKNOWN: 'go' command not found; cannot inspect binaries with go version -m"
exit 2
fi
for bin in "$@"; do
if [ ! -r "$bin" ]; then
echo "$bin: UNKNOWN (file not readable)"
saw_unknown=1
continue
fi
ver=$(extract_go_version "$bin")
if [ -z "${ver:-}" ]; then
echo "$bin: UNKNOWN (not a Go binary with embedded build info, or stripped beyond inspection)"
saw_unknown=1
continue
fi
report_one "$bin" "$ver"
done
else
ver=$(extract_local_toolchain)
if [ -z "${ver:-}" ]; then
echo "UNKNOWN: local Go toolchain not found"
exit 2
fi
report_one "local-go-toolchain" "$ver"
fi
if [ "$status" -eq 1 ]; then
exit 1
fi
if [ "$saw_unknown" -eq 1 ]; then
exit 2
fi
exit 0
If you remember one thing.
go1.25.11 or go1.26.4 in the next normal toolchain refresh, and document any outbound-facing exceptions that accept user-controlled hostnames. For a LOW verdict there is noisgate mitigation SLA and noisgate remediation SLA is no SLA (treat as backlog hygiene); this belongs in ordinary patch hygiene, not the front of the fire line.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.