CVE-2026-62994

lowpriority · track · 0EPSS 0.005 · 40 %ileDEEUUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.46% (40th) EPSS
CVSS 3.7 · low
Is it mine?
5 products
Check your exposure
Who says so?
5 sources
cnnvd · enisa_euvd · osv · cert_bund

EPSS · scored 2026-10-27

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.

01

cvss

Technical severity, 0–10. One row per scoring source.

3.7
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.13.7lowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:Lsecondary · ENISA EU
v3.13.7lowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:Lsecondary · security-advisories@github.comexploitability 2.2 · impact 1.4
v3.13.7lowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:Lsecondary · OSV
v3.13.4lowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L/E:Usecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (5)

Products and version ranges known to be affected.

05

advisories (3)

Vendor and ecosystem advisories describing this.

  • WID-SEC-W-2026-2271CoreDNS: Mehrere Schwachstellen ermöglichen Denial of ServiceDeutsch2026-07-09csaf · cert_bundDE

    Ein entfernter, anonymer Angreifer kann mehrere Schwachstellen in CoreDNS ausnutzen, um einen Denial of Service Angriff durchzuführen.

  • GHSA-74w3-63xv-x9mvCoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin2026-10-16ghsa · osvUS

    ### 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` ```go 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` ```go 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` ```go 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` ```go 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: 1. Kubernetes EndpointSlice has ready addresses and no ports. 2. CoreDNS `kubernetes` converts it to an endpoint with `Port: -1`. 3. `k8s_external` with `headless` includes that service in transfer data. 4. `k8s_external.Transfer` sends an empty SRV `[]dns.RR` batch. 5. `transfer.ServeDNS` indexes `records[0]` and panics. Suggested remediation: 1. In `plugin/transfer/transfer.go`, skip or reject empty `records` batches before indexing `records[0]`. 2. In `plugin/k8s_external/transfer.go`, only send `recs` when `len(recs) > 0` in the headless endpoint and port/protocol paths. 3. Add regression tests covering `Port: -1` headless services and empty transfer batches. Example consumer-side guard: ```go 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`: ```go 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: []*xfr{{Zones: []string{"example.org."}, to: []string{"*"}}}, } req := new(dns.Msg) req.SetQuestion("example.org.", dns.TypeAXFR) rec := dnstest.NewRecorder(&test.ResponseWriter{TCP: true}) defer func() { if rec := recover(); rec == nil { t.Fatal("expected empty transfer batch to panic") } }() _, _ = tr.ServeDNS(context.Background(), rec, req) } ``` Verification command and result: ```text $ go test ./plugin/transfer -run TestAuditEmptyTransferBatchPanics -count=1 -v === RUN TestAuditEmptyTransferBatchPanics --- PASS: TestAuditEmptyTransferBatchPanics (0.00s) PASS ok github.com/coredns/coredns/plugin/transfer 0.005s ``` #### Reproducer B: the built-in `k8s_external` transferer emits the empty batch A temporary package test was added under `plugin/k8s_external`: ```go package external import ( "testing" "time" "github.com/coredns/coredns/plugin/etcd/msg" "github.com/coredns/coredns/plugin/kubernetes" "github.com/coredns/coredns/request" "github.com/miekg/dns" ) func TestAuditHeadlessNoPortTransferEmitsEmptyBatch(t *testing.T) { e := New() e.Zones = []string{"example.com."} e.headless = true e.externalAddrFunc = func(request.Request, bool) []dns.RR { return nil } e.externalSerialFunc = func(string) uint32 { return 1 } e.externalServicesFunc = func(string, bool) ([]msg.Service, map[string][]msg.Service) { return nil, map[string][]msg.Service{ "/coredns/com/example/testns/headless/" + kubernetes.Endpoint: {{Host: "192.0.2.10", Port: -1, TTL: 5, Key: "/coredns/com/example/testns/headless/endpoint-a"}}, } } ch, err := e.Transfer("example.com.", 0) if err != nil { t.Fatalf("Transfer returned error: %v", err) } deadline := time.After(time.Second) for { select { case batch, ok := <-ch: if !ok { t.Fatal("transfer closed before emitting an empty batch") } if len(batch) == 0 { return } case <-deadline: t.Fatal("timed out waiting for an empty batch") } } } ``` Verification command and result: ```text $ go test ./plugin/k8s_external -run TestAuditHeadlessNoPortTransferEmitsEmptyBatch -count=1 -v === RUN TestAuditHeadlessNoPortTransferEmitsEmptyBatch --- PASS: TestAuditHeadlessNoPortTransferEmitsEmptyBatch (0.00s) PASS ok github.com/coredns/coredns/plugin/k8s_external 0.012s ``` Additional focused verification: ```text $ go test ./plugin/transfer ./plugin/k8s_external ./plugin/kubernetes -run 'TestAudit|^$' -count=1 -v ok github.com/coredns/coredns/plugin/transfer 0.006s ok github.com/coredns/coredns/plugin/k8s_external 0.013s ok github.com/coredns/coredns/plugin/kubernetes 0.116s ``` Affected package suites: ```text $ go test ./plugin/transfer ./plugin/k8s_external ./plugin/kubernetes -count=1 ok github.com/coredns/coredns/plugin/transfer 0.010s ok github.com/coredns/coredns/plugin/k8s_external 0.011s ok github.com/coredns/coredns/plugin/kubernetes 0.194s ``` ### Impact A network client allowed to request AXFR for the affected zone can trigger a panic in CoreDNS when all prerequisites are present. Required configuration/state: ```corefile example.com:53 { kubernetes cluster.local k8s_external example.com { headless } transfer example.com { to * } } ``` Additional prerequisite: - The Kubernetes API state contains a headless-service endpoint represented by an EndpointSlice with ready addresses and no ports, producing CoreDNS `msg.Service{Port: -1}`. In normal CoreDNS mode, `core/dnsserver/server.go:267-279` recovers the panic and returns `SERVFAIL`, so this is a request-level availability issue and panic/log amplification issue. If the `debug` plugin is enabled, the server disables the recovery path and this can crash the CoreDNS process.

  • GO-2026-6805CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin in github.com/coredns/coredns2026-10-19vendor · osvUS

    CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin in github.com/coredns/coredns

06

also known as (4)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-07-16modified 2026-07-22
08

references (13)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan