Overview
The CVE-2026-32936 vulnerability demonstrates a classic unrestricted resource consumption (URC) scenario where a system does not impose strict bounds on processing for incoming requests. CoreDNS prior to 1.14.3 accepted oversized dns= values on its DNS-over-HTTPS (DoH) GET path and performed URL query parsing, base64 decoding, and DNS message unpacking before any size checks could reject the request. An attacker could repeatedly send large, malformed, or crafted DoH GET requests to trigger heavy CPU usage, large transient allocations, and intensified garbage-collection pressure, leading to denial of service. The exposure arises because the GET path lacked equivalent input-size validation compared to the POST path, which is bounded by http.MaxBytesReader. This CVE was addressed by the CoreDNS release fixing DoH GET path handling in version 1.14.3. In real deployments using Go and the Gin framework, URC can occur when endpoints accept large or unbounded inputs from query parameters or headers and perform expensive processing without pre-validation. This guide explains the specific vulnerability, how it could be exploited in a Go (Gin) context, and how to fix it in practical code.
Affected Versions
CoreDNS < 1.14.3
Code Fix Example
Go (Gin) API Security Remediation
VULNERABLE:
package main
import (
"encoding/base64"
"net/http"
"github.com/gin-gonic/gin"
"github.com/miekg/dns"
)
func main() {
r := gin.Default()
r.GET("/dns-query", func(c *gin.Context) {
// Unbounded: no limit on dns parameter length or payload size
dnsParam := c.Query("dns")
payload, err := base64.StdEncoding.DecodeString(dnsParam)
if err != nil {
c.String(http.StatusBadRequest, "invalid dns parameter")
return
}
var msg dns.Msg
if err := msg.Unpack(payload); err != nil {
c.String(http.StatusBadRequest, "bad dns payload")
return
}
c.String(http.StatusOK, "ok")
})
r.Run()
}
FIXED:
package main
import (
"encoding/base64"
"net/http"
"github.com/gin-gonic/gin"
"github.com/miekg/dns"
)
func main() {
r := gin.Default()
// Define a sensible maximum for the dns query parameter (e.g., 4096 chars)
const maxDNSParamLen = 4096
r.GET("/dns-query", func(c *gin.Context) {
dnsParam := c.Query("dns")
if len(dnsParam) == 0 || len(dnsParam) > maxDNSParamLen {
c.String(http.StatusBadRequest, "dns parameter size invalid or too large")
return
}
payload, err := base64.StdEncoding.DecodeString(dnsParam)
if err != nil {
c.String(http.StatusBadRequest, "invalid dns parameter")
return
}
var msg dns.Msg
if err := msg.Unpack(payload); err != nil {
c.String(http.StatusBadRequest, "bad dns payload")
return
}
c.String(http.StatusOK, "ok")
})
// Optional: encourage POST DoH with bounded body for larger payloads
// r.POST("/dns-query", func(c *gin.Context) {
// // Use http.MaxBytesReader to bound body size, then read and process.
// })
r.Run()
}