CVE-2026-62994
CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin
CoreDNS is a DNS server written in Go. From 1.9.4 until 1.14.5, a network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports; plugin/kubernetes/object/endpoint.go creates Port: -1, plugin/k8s_external/msg_to_dns.go skips that service, plugin/k8s_external/transfer.go sends an empty []dns.RR batch, and plugin/transfer/transfer.go indexes records[0] without checking the batch is non-empty. This issue is fixed in version 1.14.5.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
3.7
CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin
CoreDNS is a DNS server written in Go. From 1.9.4 until 1.14.5, a network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports; plugin/kubernetes/object/endpoint.go creates Port: -1, plugin/k8s_external/msg_to_dns.go skips that service, plugin/k8s_external/transfer.go sends an empty []dns.RR batch, and plugin/transfer/transfer.go indexes records[0] without checking the batch is non-empty. This issue is fixed in version 1.14.5.
- Record
CVE-2026-62994- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-248, CWE-755
- Updated
- 2026-11-02
EUVDEUVD-2026-45018
3.7
CoreDNS is a DNS server written in Go. From 1.9.4 until 1.14.5, a network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports; plugin/kubernetes/object/endpoint.go creates Port: -1, plugin/k8s_external/msg_to_dns.go skips that service, plugin/k8s_external/transfer.go sends an empty []dns.RR batch, and plugin/transfer/transfer.go indexes records[0] without checking the batch is non-empty. This issue is fixed in version 1.14.5.
- Record
EUVD-2026-45018- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-74w3-63xv-x9mv
3.7
CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin
Summary
A network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports. The built-in k8s_external transferer can emit an empty []dns.RR batch, and the shared transfer plugin indexes records[0] without checking that the batch is non-empty. In normal CoreDNS mode the panic is recovered and returned as SERVFAIL, but repeated triggering can disrupt requests and amplify panic logging; if the debug plugin disables recovery, the same path can crash the CoreDNS process.
Details
plugin/transfer/transfer.go assumes that every Transferer emits only non-empty []dns.RR batches. During AXFR/IXFR response generation it indexes records[0] without checking the slice length:
plugin/transfer/transfer.go:129-131
for records := range pchan {
if x, ok := records[0].(*dns.SOA); ok && soa == nil {
soa = x
}
The built-in k8s_external transferer violates that contract for headless services whose endpoints have no declared ports. plugin/k8s_external/transfer.go sends the SRV result slice unconditionally:
plugin/k8s_external/transfer.go:104-106
recs, _ := e.srv(ctx, svcs, s)
ch <- recs
plugin/k8s_external/msg_to_dns.go intentionally skips services whose port is -1:
plugin/k8s_external/msg_to_dns.go:107-112
for _, s := range services {
// Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint
// does not have any declared ports.
if s.Port == -1 {
continue
}
The Kubernetes EndpointSlice conversion creates that sentinel when an EndpointSlice has no ports:
plugin/kubernetes/object/endpoint.go:62-66
if len(ends.Ports) == 0 {
// Add sentinel if there are no ports.
e.Subsets[0].Ports = []EndpointPort{{Port: -1}}
This creates a built-in source-to-sink chain:
- Kubernetes EndpointSlice has ready addresses and no ports.
- CoreDNS
kubernetesconverts it to an endpoint withPort: -1. k8s_externalwithheadlessincludes that service in transfer data.k8s_external.Transfersends an empty SRV[]dns.RRbatch.transfer.ServeDNSindexesrecords[0]and panics.
Suggested remediation:
- In
plugin/transfer/transfer.go, skip or reject emptyrecordsbatches before indexingrecords[0]. - In
plugin/k8s_external/transfer.go, only sendrecswhenlen(recs) > 0in the headless endpoint and port/protocol paths. - Add regression tests covering
Port: -1headless services and empty transfer batches.
Example consumer-side guard:
for records := range pchan {
if len(records) == 0 {
continue
}
if x, ok := records[0].(*dns.SOA); ok && soa == nil {
soa = x
}
...
}
PoC
Safe local unit-level reproduction was performed. It does not contact external infrastructure and does not require a live Kubernetes cluster.
Reproducer A: the transfer sink panics on an empty batch
A temporary package test was added under plugin/transfer:
package transfer
import (
"context"
"fmt"
"testing"
"github.com/coredns/coredns/plugin/pkg/dnstest"
"github.com/coredns/coredns/plugin/test"
"github.com/miekg/dns"
)
type auditEmptyBatchTransferer struct{}
func (auditEmptyBatchTransferer) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) {
if zone != "example.org." {
return nil, ErrNotAuthoritative
}
ch := make(chan []dns.RR, 3)
ch <- []dns.RR{test.SOA(fmt.Sprintf("%s 100 IN SOA ns.dns.%s hostmaster.%s %d 7200 1800 86400 100", zone, zone, zone, 1))}
ch <- []dns.RR{}
close(ch)
return ch, nil
}
func TestAuditEmptyTransferBatchPanics(t *testing.T) {
tr := &Transfer{
Transferers: []Transferer{auditEmptyBatchTransferer{}},
xfrs: …
- Record
GHSA-74w3-63xv-x9mv- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-248, CWE-755
- Updated
- 2026-11-02
NVD
3.7
CoreDNS is a DNS server written in Go. From 1.9.4 until 1.14.5, a network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports; plugin/kubernetes/object/endpoint.go creates Port: -1, plugin/k8s_external/msg_to_dns.go skips that service, plugin/k8s_external/transfer.go sends an empty []dns.RR batch, and plugin/transfer/transfer.go indexes records[0] without checking the batch is non-empty. This issue is fixed in version 1.14.5.
- Record
CVE-2026-62994- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-248, CWE-755
- Updated
- 2026-11-02
OSV
3.7
CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin
CoreDNS is a DNS server written in Go. From 1.9.4 until 1.14.5, a network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports; plugin/kubernetes/object/endpoint.go creates Port: -1, plugin/k8s_external/msg_to_dns.go skips that service, plugin/k8s_external/transfer.go sends an empty []dns.RR batch, and plugin/transfer/transfer.go indexes records[0] without checking the batch is non-empty. This issue is fixed in version 1.14.5.
- Record
CVE-2026-62994- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-248, CWE-755
- Updated
- 2026-11-02
OSVGHSA-74w3-63xv-x9mv
3.7
CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin
Summary
A network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports. The built-in k8s_external transferer can emit an empty []dns.RR batch, and the shared transfer plugin indexes records[0] without checking that the batch is non-empty. In normal CoreDNS mode the panic is recovered and returned as SERVFAIL, but repeated triggering can disrupt requests and amplify panic logging; if the debug plugin disables recovery, the same path can crash the CoreDNS process.
Details
plugin/transfer/transfer.go assumes that every Transferer emits only non-empty []dns.RR batches. During AXFR/IXFR response generation it indexes records[0] without checking the slice length:
plugin/transfer/transfer.go:129-131
for records := range pchan {
if x, ok := records[0].(*dns.SOA); ok && soa == nil {
soa = x
}
The built-in k8s_external transferer violates that contract for headless services whose endpoints have no declared ports. plugin/k8s_external/transfer.go sends the SRV result slice unconditionally:
plugin/k8s_external/transfer.go:104-106
recs, _ := e.srv(ctx, svcs, s)
ch <- recs
plugin/k8s_external/msg_to_dns.go intentionally skips services whose port is -1:
plugin/k8s_external/msg_to_dns.go:107-112
for _, s := range services {
// Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint
// does not have any declared ports.
if s.Port == -1 {
continue
}
The Kubernetes EndpointSlice conversion creates that sentinel when an EndpointSlice has no ports:
plugin/kubernetes/object/endpoint.go:62-66
if len(ends.Ports) == 0 {
// Add sentinel if there are no ports.
e.Subsets[0].Ports = []EndpointPort{{Port: -1}}
This creates a built-in source-to-sink chain:
- Kubernetes EndpointSlice has ready addresses and no ports.
- CoreDNS
kubernetesconverts it to an endpoint withPort: -1. k8s_externalwithheadlessincludes that service in transfer data.k8s_external.Transfersends an empty SRV[]dns.RRbatch.transfer.ServeDNSindexesrecords[0]and panics.
Suggested remediation:
- In
plugin/transfer/transfer.go, skip or reject emptyrecordsbatches before indexingrecords[0]. - In
plugin/k8s_external/transfer.go, only sendrecswhenlen(recs) > 0in the headless endpoint and port/protocol paths. - Add regression tests covering
Port: -1headless services and empty transfer batches.
Example consumer-side guard:
for records := range pchan {
if len(records) == 0 {
continue
}
if x, ok := records[0].(*dns.SOA); ok && soa == nil {
soa = x
}
...
}
PoC
Safe local unit-level reproduction was performed. It does not contact external infrastructure and does not require a live Kubernetes cluster.
Reproducer A: the transfer sink panics on an empty batch
A temporary package test was added under plugin/transfer:
package transfer
import (
"context"
"fmt"
"testing"
"github.com/coredns/coredns/plugin/pkg/dnstest"
"github.com/coredns/coredns/plugin/test"
"github.com/miekg/dns"
)
type auditEmptyBatchTransferer struct{}
func (auditEmptyBatchTransferer) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) {
if zone != "example.org." {
return nil, ErrNotAuthoritative
}
ch := make(chan []dns.RR, 3)
ch <- []dns.RR{test.SOA(fmt.Sprintf("%s 100 IN SOA ns.dns.%s hostmaster.%s %d 7200 1800 86400 100", zone, zone, zone, 1))}
ch <- []dns.RR{}
close(ch)
return ch, nil
}
func TestAuditEmptyTransferBatchPanics(t *testing.T) {
tr := &Transfer{
Transferers: []Transferer{auditEmptyBatchTransferer{}},
xfrs: …
- Record
GHSA-74w3-63xv-x9mv- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-248, CWE-755
- Updated
- 2026-11-02
OSVAZL-92895 No score
CVE-2026-62994 affecting package coredns for versions less than 1.11.4-20
CoreDNS is a DNS server written in Go. From 1.9.4 until 1.14.5, a network DNS client allowed to request AXFR for a CoreDNS zone can trigger a panic when CoreDNS is configured with k8s_external headless-service zone transfers and Kubernetes contains a headless service endpoint with no declared ports; plugin/kubernetes/object/endpoint.go creates Port: -1, plugin/k8s_external/msg_to_dns.go skips that service, plugin/k8s_external/transfer.go sends an empty []dns.RR batch, and plugin/transfer/transfer.go indexes records[0] without checking the batch is non-empty. This issue is fixed in version 1.14.5.
- Record
AZL-92895- Updated
- 2026-11-05
OSVGO-2026-6805 No score
CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin in github.com/coredns/coredns
CoreDNS k8s_external headless AXFR can emit an empty transfer batch that panics the transfer plugin in github.com/coredns/coredns
- Record
GO-2026-6805- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/coredns/corednsGo |
1.9.4 to 1.14.4 | 1.14.5 |
corednsAzure Linux:3 |
before 1.11.4-20 | 1.11.4-20 |
github.com/coredns/corednsGo |
1.9.4 up to 1.14.5 | 1.14.5 |
History
| Recorded | Changed | Sources |
|---|---|---|
| facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-62994 is a low vulnerability (CVSS 3.7) in Coredns, published July 16, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.46%, the 40th percentile. Fixed in github.com/coredns/coredns 1.14.5.
References
github.com/coredns/coredns/security/advisories/GHSA-74w3-63xv-x9mv
github.com/coredns/coredns/pull/8207
github.com/coredns/coredns/commit/ab318db7b4a3a19273852ed627f54888198c8efb
github.com/coredns/coredns/releases/tag/v1.14.5
nvd.nist.gov/vuln/detail/CVE-2026-62994
github.com/advisories/GHSA-74w3-63xv-x9mv
github.com/CVEProject/cvelistV5/tree/main/cves/2026/62xxx/CVE-2026-62994.json
github.com/coredns/coredns
Left out here: the rest of 2 source descriptions. The JSON record has them.