A crash bug in a niche image decoder that vendors love to label HIGH because the input is network-reachable
CVE-2026-46604 is a panic in the TIFF decoder of golang.org/x/image — a supplementary Go imaging package, not the standard library. A crafted TIFF with an out-of-bounds strip offset trips an unchecked slice index inside tiff.Decode, and the goroutine panics with runtime error: slice bounds out of range. The bug affects every release of golang.org/x/image prior to 0.43.0; the fix landed in CL 788421 and ships as GO-2026-5066. Reporter credit: *sorte*.
Vendor severity here is the Go security team's standard CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — which is mechanically correct (the panic is unauthenticated, network-triggerable in some apps, and is *availability* impact) but practitionally misleading. There is no memory disclosure, no code execution, no integrity impact. The damage ceiling is whatever the panicking process was doing — and in Go, a panic on a per-request goroutine inside an http.Handler is recovered by net/http's default serverHandler. Real-world blast radius is much narrower than 7.5 implies.
4 steps from start to impact.
Identify a service that decodes untrusted TIFFs via golang.org/x/image
II*\0 / MM\0*) into tiff.Decode. Typical targets: avatar/upload pipelines, OCR/scan-ingestion microservices, thumbnailing workers, document-conversion APIs. Tools: ffuf, gobuster, and content-type fuzzers like tiffuzz.- Target accepts user-uploaded images or fetches remote images
- TIFF format is in the accepted/auto-detected set
- Server uses
golang.org/x/image/tiff< 0.43.0
- Most Go web apps only accept JPEG/PNG/WebP and explicitly reject TIFF
- Many pipelines pre-validate via libvips or ImageMagick (different code path entirely)
- Cloud image CDNs (Cloudflare Images, imgix, Cloudinary) handle decode outside the customer's Go process
govulncheck is the highest-fidelity check because it verifies the *vulnerable symbol* tiff.Decode is actually reached in your binary.Craft a TIFF with an out-of-bounds StripOffsets tag
tiffcp, exiftool, or a 20-line Go script), the attacker forges a TIFF whose StripOffsets (tag 273) points past the file size, or whose StripByteCounts (tag 279) exceeds the remaining strip buffer. Public PoC pattern is trivial — set StripOffsets = 0xFFFFFFFF.- Knowledge of TIFF tag layout — well documented in the TIFF 6.0 spec
- None — this is a one-liner. Expect public PoCs within days of disclosure.
Submit the TIFF to the decoding endpoint
tiff.Decode is called and panics.- Reachable HTTP endpoint that consumes user-supplied bytes
- No size/type pre-validation that blocks TIFF
- Upload size limits, content-type allowlists, and pre-decode
image.DecodeConfigchecks (which also panic, so this doesn't always help) all narrow exposure - API gateways (Kong, Envoy) frequently reject unknown MIME types
runtime error: slice bounds out of range followed by a Go stack trace through tiff.Decode — very high-signal IOC.Realize the impact: goroutine panic, process crash, or denial-of-service
net/http's default serve loop, the panic is recovered and only that one request fails — effectively a logged error, not an outage. If the call sits in a background worker, a go func() without recover(), or a non-HTTP service (gRPC streaming, queue consumer, CLI batch job), the panic terminates the entire process. Repeated submissions become a sustained DoS against the worker pool.- Decode call runs without
defer recover() - Worker is not isolated per-message (e.g., shared goroutine pool)
net/httprecovers panics by default — the most common Go web deployment is largely insulated- Kubernetes restarts crashed pods within seconds
- No persistence, no privilege escalation, no data exposure
BackOff events, and panic stack traces in stdout aggregators (Loki, Datadog Logs).The supporting signals.
| In-the-wild exploitation | None observed. No KEV listing, no GreyNoise tags, no incident-response chatter at disclosure +4 days. |
|---|---|
| Proof-of-concept | Trivial to write from the Go CL 788421 test fixture; expect public PoCs in osv-scanner regression suites and security blogs within days. |
| EPSS | 0.00169 (~0.17%) — bottom-quartile percentile, consistent with availability-only Go library bugs. |
| KEV status | Not listed by CISA as of 2026-06-30. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — availability-only; no confidentiality or integrity impact. Vendor score 7.5 (HIGH). |
| Affected versions | All golang.org/x/image releases before 0.43.0 (module path: golang.org/x/image/tiff). |
| Fixed version | 0.43.0 — see GO-2026-5066 and CL 788421. |
| Exposure population | Limited. golang.org/x/image is a supplementary package; most Go HTTP services use image/jpeg + image/png from stdlib or shell out to libvips/ImageMagick. govulncheck data suggests <5% of indexed Go binaries import the tiff subpackage. |
| Disclosure date | 2026-06-26 (reserved 2026-05-15). |
| Reporter | *sorte* via the Go security team. |
noisgate verdict.
Downgraded from vendor HIGH (7.5) to MEDIUM (5.3) because the single decisive factor is availability-only impact with no code execution path — the CVSS A:H reflects a goroutine panic, not RCE or data exposure. The blast radius is bounded by the panic-recovery behavior of net/http and the narrow population of Go services that actually parse TIFFs.
Why this verdict
- Availability-only impact: vector explicitly says C:N/I:N/A:H. There is no RCE, no information leak, no privilege escalation — the worst case is a panic.
net/httppanic recovery: the default Go HTTP server recovers per-request panics, so the most common deployment shape (an HTTP handler that decodes uploads) degrades to a logged 500 rather than a process crash.- Narrow library footprint:
golang.org/x/image/tiffis opt-in. Stdlib JPEG/PNG/GIF/WebP decoders are unaffected. Most image-handling Go services never import this package. - Role multiplier (workstation/dev sandbox): non-applicable — this is a library, not a deployment role. No fleet-scale identity, hypervisor, CI/CD, or kernel-mode amplifier exists.
- Role multiplier (typical app server): panic is contained per-request by
net/httprecovery; outcome is a single failed request. Floor stays MEDIUM at worst. - Role multiplier (background worker / gRPC streaming server / queue consumer): chain succeeds — these contexts often lack
recover()and a sustained malformed-TIFF stream can crash-loop the worker. Blast radius is host-level service outage, not fleet compromise. Floor remains MEDIUM; does not lift to HIGH because remediation is a singlego getand there is no privilege escalation. - EPSS 0.17% and no KEV listing confirm the realistic-exploit picture: low priority by external telemetry.
Why not higher?
HIGH (7.0+) would require either a real path to RCE, information disclosure, or a deployment role with fleet-scale blast radius. None apply: the bug is a slice-bounds panic, the panic is recoverable, and the affected library is not canonically deployed in identity, hypervisor, CI/CD, backup, or kernel-mode roles. A process crash is real, but it is recoverable in seconds and does not pivot to other assets.
Why not lower?
LOW (≤3.9) would require either no realistic reachability or trivial built-in mitigation. The bug is reachable from unauthenticated network requests on services that accept TIFFs, and not every Go process is wrapped in net/http panic recovery — gRPC servers, queue workers, and CLI batch jobs can be crashed outright. Sustained DoS against a critical ingest pipeline is a real operational risk, so MEDIUM is the honest floor.
What to do — in priority order.
- Run
govulncheckacross your Go fleet to find binaries that reachtiff.Decode—govulncheck ./...does symbol-level reachability analysis — it tells you which of your services actually import and call the vulnerable function, not just which ones import the module. Prioritize the reachable set. Per noisgate MEDIUM SLA there is no mitigation deadline; complete this inventory within the 365-day remediation window, but front-load it in the first 30 days while triage attention is still on this CVE. - Add a TIFF MIME-type denylist at the upload gateway — If your application has no business reason to accept TIFFs (most don't), reject
image/tiff,image/tif, and content-sniff for theII*\0/MM\0*magic bytes at the WAF/API gateway. This is a one-line change in Envoy/Kong/Cloudflare and neutralizes the bug regardless of whether the underlying library is patched. Apply within 30 days as a defensive hygiene step. - Wrap decode calls in
defer recover()for non-HTTP contexts — gRPC streaming handlers, queue consumers (NATS, Kafka, SQS workers), and CLI batch jobs do not getnet/http's automatic panic recovery. Audit everytiff.Decodecall site in a non-HTTP context and add adefer func() { if r := recover(); r != nil { ... } }()block. Apply within 30 days as defense-in-depth. - Pin and bump
golang.org/x/imageto ≥ 0.43.0 — The fix is in version 0.43.0. Usego get golang.org/x/[email protected] && go mod tidy, rebuild, and ship. Stage through your normal release pipeline within the noisgate remediation SLA of 365 days for MEDIUM — but most shops will roll this with the next routine dependency bump.
- WAF signatures on TIFF content — generic WAFs do not parse TIFF tag structures and cannot distinguish a malformed
StripOffsetsfrom a legitimate one. - Pre-decode
image.DecodeConfigchecks —DecodeConfigfor TIFF in the affected versions traverses the same IFD parsing path and can panic on similar inputs; it is not a safe guard. - File-size limits — the malicious TIFF can be under 1 KB. Size caps don't help.
- Sandboxing the Go process — does not prevent the DoS; the process still crashes inside the sandbox.
Crowdsourced verification payload.
Run on a CI worker or developer workstation with the Go toolchain installed and your project checked out. Invoke from the repo root: bash check-cve-2026-46604.sh ./.... No elevated privileges required. The script uses govulncheck for symbol-level reachability and falls back to go list -m for dependency-graph detection.
#!/usr/bin/env bash
# check-cve-2026-46604.sh — detect vulnerable golang.org/x/image TIFF decoder
# Usage: bash check-cve-2026-46604.sh [go-package-spec, default ./...]
set -u
TARGET="${1:-./...}"
FIXED_VERSION="v0.43.0"
MODULE="golang.org/x/image"
if ! command -v go >/dev/null 2>&1; then
echo "UNKNOWN: go toolchain not found" >&2
exit 2
fi
if ! [ -f go.mod ]; then
echo "UNKNOWN: no go.mod in current directory" >&2
exit 2
fi
# 1. Check resolved dependency version
VERSION=$(go list -m -f '{{.Version}}' "$MODULE" 2>/dev/null || true)
if [ -z "$VERSION" ]; then
echo "PATCHED: module $MODULE is not in the dependency graph"
exit 0
fi
# 2. Numeric compare against fixed version
LOWEST=$(printf '%s\n%s\n' "$VERSION" "$FIXED_VERSION" | sort -V | head -n1)
if [ "$LOWEST" = "$FIXED_VERSION" ] && [ "$VERSION" != "$FIXED_VERSION" ]; then
STATE="PATCHED" # version is >= fixed
elif [ "$VERSION" = "$FIXED_VERSION" ]; then
STATE="PATCHED"
else
STATE="VULNERABLE"
fi
# 3. Symbol-level reachability via govulncheck (if installed)
if command -v govulncheck >/dev/null 2>&1; then
if govulncheck "$TARGET" 2>/dev/null | grep -q "GO-2026-5066\|CVE-2026-46604"; then
echo "VULNERABLE: $MODULE@$VERSION — tiff.Decode is reachable (govulncheck)"
exit 1
fi
fi
if [ "$STATE" = "VULNERABLE" ]; then
echo "VULNERABLE: $MODULE@$VERSION < $FIXED_VERSION (reachability not confirmed; install govulncheck for symbol check)"
exit 1
fi
echo "PATCHED: $MODULE@$VERSION >= $FIXED_VERSION"
exit 0
If you remember one thing.
govulncheck ./... across your Go services to find binaries that actually call tiff.Decode, and check whether any of those run as non-HTTP workers (queue consumers, gRPC streamers, batch jobs) where panics are not auto-recovered. Per noisgate mitigation SLA for MEDIUM, there is no mitigation SLA — go straight to the noisgate remediation SLA of 365 days to ship golang.org/x/[email protected] through normal release trains. The two free wins to take this week regardless: (1) deny image/tiff at the upload gateway for any app that has no business reason to accept it, and (2) confirm any non-HTTP TIFF-decoding worker has defer recover(). Do NOT page anyone over this CVE — vendor HIGH is overstated.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.