{"id":"CVE-2026-62994","aliases":["AZL-92895","EUVD-2026-45018","GHSA-74w3-63xv-x9mv","GO-2026-6805"],"status":"published","title":"CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin","description":"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.","severity":{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"low","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00459,"percentile":0.40254,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-248","CWE-755"],"published":"2026-07-16T19:39:05Z","modified":"2026-11-05T05:40:51Z","fixes":[{"ecosystem":"Go","package":"github.com/coredns/coredns","fixed_in":["1.14.5"],"sources":["ghsa","osv"]},{"ecosystem":"Azure Linux:3","package":"coredns","fixed_in":["1.11.4-20"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"Go","package":"github.com/coredns/coredns","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.9.4"},{"last_affected":"1.14.4"},{"fixed":"1.14.5"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"coredns","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.11.4-20"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/coredns/coredns","ranges":[{"type":"SEMVER","events":[{"introduced":"1.9.4"},{"fixed":"1.14.5"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/coredns/coredns","ranges":[{"type":"SEMVER","events":[{"introduced":"1.9.4"},{"fixed":"1.14.5"}]}]}],"products":[{"vendor":"coredns","product":"coredns"},{"vendor":"coredns.io","product":"coredns"}],"references":[{"url":"https://github.com/coredns/coredns/security/advisories/GHSA-74w3-63xv-x9mv","tags":["x_refsource_CONFIRM","exploit","Exploit","Third Party Advisory","advisory","web"]},{"url":"https://github.com/coredns/coredns/pull/8207","tags":["x_refsource_MISC","Issue Tracking","Patch","fix","web"]},{"url":"https://github.com/coredns/coredns/commit/ab318db7b4a3a19273852ed627f54888198c8efb","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/coredns/coredns/releases/tag/v1.14.5","tags":["x_refsource_MISC","Release Notes","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-62994","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-74w3-63xv-x9mv"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/62xxx/CVE-2026-62994.json","tags":["advisory"]},{"url":"https://github.com/coredns/coredns","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-62994","title":"CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin","description":"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.","status":"published","published":"2026-07-16T19:39:05.582Z","modified":"2026-07-17T11:12:51.792Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"low","assessor":"cna"}],"cwes":["CWE-248","CWE-755"],"updated_at":"2026-11-02T14:20:14.665567Z"}],"euvd":[{"id":"EUVD-2026-45018","description":"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.","status":"published","published":"2026-07-16T19:39:05Z","modified":"2026-07-17T11:12:51Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"low"}],"updated_at":"2026-11-02T18:19:50.846094Z"}],"ghsa":[{"id":"GHSA-74w3-63xv-x9mv","title":"CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin","description":"### Summary\n\nA 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.\n\n### Details\n\n`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:\n\n- `plugin/transfer/transfer.go:129-131`\n\n```go\nfor records := range pchan {\n    if x, ok := records[0].(*dns.SOA); ok \u0026\u0026 soa == nil {\n        soa = x\n    }\n```\n\nThe 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:\n\n- `plugin/k8s_external/transfer.go:104-106`\n\n```go\nrecs, _ := e.srv(ctx, svcs, s)\nch \u003c- recs\n```\n\n`plugin/k8s_external/msg_to_dns.go` intentionally skips services whose port is `-1`:\n\n- `plugin/k8s_external/msg_to_dns.go:107-112`\n\n```go\nfor _, s := range services {\n    // Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint\n    // does not have any declared ports.\n    if s.Port == -1 {\n        continue\n    }\n```\n\nThe Kubernetes EndpointSlice conversion creates that sentinel when an EndpointSlice has no ports:\n\n- `plugin/kubernetes/object/endpoint.go:62-66`\n\n```go\nif len(ends.Ports) == 0 {\n    // Add sentinel if there are no ports.\n    e.Subsets[0].Ports = []EndpointPort{{Port: -1}}\n```\n\nThis creates a built-in source-to-sink chain:\n\n1. Kubernetes EndpointSlice has ready addresses and no ports.\n2. CoreDNS `kubernetes` converts it to an endpoint with `Port: -1`.\n3. `k8s_external` with `headless` includes that service in transfer data.\n4. `k8s_external.Transfer` sends an empty SRV `[]dns.RR` batch.\n5. `transfer.ServeDNS` indexes `records[0]` and panics.\n\nSuggested remediation:\n\n1. In `plugin/transfer/transfer.go`, skip or reject empty `records` batches before indexing `records[0]`.\n2. In `plugin/k8s_external/transfer.go`, only send `recs` when `len(recs) \u003e 0` in the headless endpoint and port/protocol paths.\n3. Add regression tests covering `Port: -1` headless services and empty transfer batches.\n\nExample consumer-side guard:\n\n```go\nfor records := range pchan {\n    if len(records) == 0 {\n        continue\n    }\n    if x, ok := records[0].(*dns.SOA); ok \u0026\u0026 soa == nil {\n        soa = x\n    }\n    ...\n}\n```\n\n### PoC\n\nSafe local unit-level reproduction was performed. It does not contact external infrastructure and does not require a live Kubernetes cluster.\n\n#### Reproducer A: the `transfer` sink panics on an empty batch\n\nA temporary package test was added under `plugin/transfer`:\n\n```go\npackage transfer\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"testing\"\n\n    \"github.com/coredns/coredns/plugin/pkg/dnstest\"\n    \"github.com/coredns/coredns/plugin/test\"\n\n    \"github.com/miekg/dns\"\n)\n\ntype auditEmptyBatchTransferer struct{}\n\nfunc (auditEmptyBatchTransferer) Transfer(zone string, serial uint32) (\u003c-chan []dns.RR, error) {\n    if zone != \"example.org.\" {\n        return nil, ErrNotAuthoritative\n    }\n    ch := make(chan []dns.RR, 3)\n    ch \u003c- []dns.RR{test.SOA(fmt.Sprintf(\"%s 100 IN SOA ns.dns.%s hostmaster.%s %d 7200 1800 86400 100\", zone, zone, zone, 1))}\n    ch \u003c- []dns.RR{}\n    close(ch)\n    return ch, nil\n}\n\nfunc TestAuditEmptyTransferBatchPanics(t *testing.T) {\n    tr := \u0026Transfer{\n        Transferers: []Transferer{auditEmptyBatchTransferer{}},\n        xfrs:        []*xfr{{Zones: []string{\"example.org.\"}, to: []string{\"*\"}}},\n    }\n\n    req := new(dns.Msg)\n    req.SetQuestion(\"example.org.\", dns.TypeAXFR)\n    rec := dnstest.NewRecorder(\u0026test.ResponseWriter{TCP: true})\n\n    defer func() {\n        if rec := recover(); rec == nil {\n            t.Fatal(\"expected empty transfer batch to panic\")\n        }\n    }()\n\n    _, _ = tr.ServeDNS(context.Background(), rec, req)\n}\n```\n\nVerification command and result:\n\n```text\n$ go test ./plugin/transfer -run TestAuditEmptyTransferBatchPanics -count=1 -v\n=== RUN   TestAuditEmptyTransferBatchPanics\n--- PASS: TestAuditEmptyTransferBatchPanics (0.00s)\nPASS\nok  \tgithub.com/coredns/coredns/plugin/transfer\t0.005s\n```\n\n#### Reproducer B: the built-in `k8s_external` transferer emits the empty batch\n\nA temporary package test was added under `plugin/k8s_external`:\n\n```go\npackage external\n\nimport (\n    \"testing\"\n    \"time\"\n\n    \"github.com/coredns/coredns/plugin/etcd/msg\"\n    \"github.com/coredns/coredns/plugin/kubernetes\"\n    \"github.com/coredns/coredns/request\"\n\n    \"github.com/miekg/dns\"\n)\n\nfunc TestAuditHeadlessNoPortTransferEmitsEmptyBatch(t *testing.T) {\n    e := New()\n    e.Zones = []string{\"example.com.\"}\n    e.headless = true\n    e.externalAddrFunc = func(request.Request, bool) []dns.RR { return nil }\n    e.externalSerialFunc = func(string) uint32 { return 1 }\n    e.externalServicesFunc = func(string, bool) ([]msg.Service, map[string][]msg.Service) {\n        return nil, map[string][]msg.Service{\n            \"/coredns/com/example/testns/headless/\" + kubernetes.Endpoint: {{Host: \"192.0.2.10\", Port: -1, TTL: 5, Key: \"/coredns/com/example/testns/headless/endpoint-a\"}},\n        }\n    }\n\n    ch, err := e.Transfer(\"example.com.\", 0)\n    if err != nil {\n        t.Fatalf(\"Transfer returned error: %v\", err)\n    }\n\n    deadline := time.After(time.Second)\n    for {\n        select {\n        case batch, ok := \u003c-ch:\n            if !ok {\n                t.Fatal(\"transfer closed before emitting an empty batch\")\n            }\n            if len(batch) == 0 {\n                return\n            }\n        case \u003c-deadline:\n            t.Fatal(\"timed out waiting for an empty batch\")\n        }\n    }\n}\n```\n\nVerification command and result:\n\n```text\n$ go test ./plugin/k8s_external -run TestAuditHeadlessNoPortTransferEmitsEmptyBatch -count=1 -v\n=== RUN   TestAuditHeadlessNoPortTransferEmitsEmptyBatch\n--- PASS: TestAuditHeadlessNoPortTransferEmitsEmptyBatch (0.00s)\nPASS\nok  \tgithub.com/coredns/coredns/plugin/k8s_external\t0.012s\n```\n\nAdditional focused verification:\n\n```text\n$ go test ./plugin/transfer ./plugin/k8s_external ./plugin/kubernetes -run 'TestAudit|^$' -count=1 -v\nok  \tgithub.com/coredns/coredns/plugin/transfer\t0.006s\nok  \tgithub.com/coredns/coredns/plugin/k8s_external\t0.013s\nok  \tgithub.com/coredns/coredns/plugin/kubernetes\t0.116s\n```\n\nAffected package suites:\n\n```text\n$ go test ./plugin/transfer ./plugin/k8s_external ./plugin/kubernetes -count=1\nok  \tgithub.com/coredns/coredns/plugin/transfer\t0.010s\nok  \tgithub.com/coredns/coredns/plugin/k8s_external\t0.011s\nok  \tgithub.com/coredns/coredns/plugin/kubernetes\t0.194s\n```\n\n### Impact\n\nA network client allowed to request AXFR for the affected zone can trigger a panic in CoreDNS when all prerequisites are present.\n\nRequired configuration/state:\n\n```corefile\nexample.com:53 {\n    kubernetes cluster.local\n    k8s_external example.com {\n        headless\n    }\n    transfer example.com {\n        to *\n    }\n}\n```\n\nAdditional prerequisite:\n\n- 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}`.\n\nIn 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.","status":"published","published":"2026-10-16T19:26:42Z","modified":"2026-10-16T19:26:43Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"low"}],"cwes":["CWE-248","CWE-755"],"updated_at":"2026-11-02T14:41:32.483482Z"}],"nvd":[{"id":"CVE-2026-62994","description":"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.","status":"published","published":"2026-07-16T20:16:47.657Z","modified":"2026-07-22T20:14:36.067Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"low","assessor":"secondary"}],"cwes":["CWE-248","CWE-755"],"updated_at":"2026-11-02T14:12:30.250437Z"}],"osv":[{"id":"CVE-2026-62994","title":"CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin","description":"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.","status":"published","published":"2026-07-16T19:39:05.582Z","modified":"2026-10-28T02:31:00.780851664Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"low"}],"cwes":["CWE-248","CWE-755"],"updated_at":"2026-11-02T15:45:13.306117Z"},{"id":"GHSA-74w3-63xv-x9mv","title":"CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin","description":"### Summary\n\nA 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.\n\n### Details\n\n`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:\n\n- `plugin/transfer/transfer.go:129-131`\n\n```go\nfor records := range pchan {\n    if x, ok := records[0].(*dns.SOA); ok \u0026\u0026 soa == nil {\n        soa = x\n    }\n```\n\nThe 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:\n\n- `plugin/k8s_external/transfer.go:104-106`\n\n```go\nrecs, _ := e.srv(ctx, svcs, s)\nch \u003c- recs\n```\n\n`plugin/k8s_external/msg_to_dns.go` intentionally skips services whose port is `-1`:\n\n- `plugin/k8s_external/msg_to_dns.go:107-112`\n\n```go\nfor _, s := range services {\n    // Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint\n    // does not have any declared ports.\n    if s.Port == -1 {\n        continue\n    }\n```\n\nThe Kubernetes EndpointSlice conversion creates that sentinel when an EndpointSlice has no ports:\n\n- `plugin/kubernetes/object/endpoint.go:62-66`\n\n```go\nif len(ends.Ports) == 0 {\n    // Add sentinel if there are no ports.\n    e.Subsets[0].Ports = []EndpointPort{{Port: -1}}\n```\n\nThis creates a built-in source-to-sink chain:\n\n1. Kubernetes EndpointSlice has ready addresses and no ports.\n2. CoreDNS `kubernetes` converts it to an endpoint with `Port: -1`.\n3. `k8s_external` with `headless` includes that service in transfer data.\n4. `k8s_external.Transfer` sends an empty SRV `[]dns.RR` batch.\n5. `transfer.ServeDNS` indexes `records[0]` and panics.\n\nSuggested remediation:\n\n1. In `plugin/transfer/transfer.go`, skip or reject empty `records` batches before indexing `records[0]`.\n2. In `plugin/k8s_external/transfer.go`, only send `recs` when `len(recs) \u003e 0` in the headless endpoint and port/protocol paths.\n3. Add regression tests covering `Port: -1` headless services and empty transfer batches.\n\nExample consumer-side guard:\n\n```go\nfor records := range pchan {\n    if len(records) == 0 {\n        continue\n    }\n    if x, ok := records[0].(*dns.SOA); ok \u0026\u0026 soa == nil {\n        soa = x\n    }\n    ...\n}\n```\n\n### PoC\n\nSafe local unit-level reproduction was performed. It does not contact external infrastructure and does not require a live Kubernetes cluster.\n\n#### Reproducer A: the `transfer` sink panics on an empty batch\n\nA temporary package test was added under `plugin/transfer`:\n\n```go\npackage transfer\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"testing\"\n\n    \"github.com/coredns/coredns/plugin/pkg/dnstest\"\n    \"github.com/coredns/coredns/plugin/test\"\n\n    \"github.com/miekg/dns\"\n)\n\ntype auditEmptyBatchTransferer struct{}\n\nfunc (auditEmptyBatchTransferer) Transfer(zone string, serial uint32) (\u003c-chan []dns.RR, error) {\n    if zone != \"example.org.\" {\n        return nil, ErrNotAuthoritative\n    }\n    ch := make(chan []dns.RR, 3)\n    ch \u003c- []dns.RR{test.SOA(fmt.Sprintf(\"%s 100 IN SOA ns.dns.%s hostmaster.%s %d 7200 1800 86400 100\", zone, zone, zone, 1))}\n    ch \u003c- []dns.RR{}\n    close(ch)\n    return ch, nil\n}\n\nfunc TestAuditEmptyTransferBatchPanics(t *testing.T) {\n    tr := \u0026Transfer{\n        Transferers: []Transferer{auditEmptyBatchTransferer{}},\n        xfrs:        []*xfr{{Zones: []string{\"example.org.\"}, to: []string{\"*\"}}},\n    }\n\n    req := new(dns.Msg)\n    req.SetQuestion(\"example.org.\", dns.TypeAXFR)\n    rec := dnstest.NewRecorder(\u0026test.ResponseWriter{TCP: true})\n\n    defer func() {\n        if rec := recover(); rec == nil {\n            t.Fatal(\"expected empty transfer batch to panic\")\n        }\n    }()\n\n    _, _ = tr.ServeDNS(context.Background(), rec, req)\n}\n```\n\nVerification command and result:\n\n```text\n$ go test ./plugin/transfer -run TestAuditEmptyTransferBatchPanics -count=1 -v\n=== RUN   TestAuditEmptyTransferBatchPanics\n--- PASS: TestAuditEmptyTransferBatchPanics (0.00s)\nPASS\nok  \tgithub.com/coredns/coredns/plugin/transfer\t0.005s\n```\n\n#### Reproducer B: the built-in `k8s_external` transferer emits the empty batch\n\nA temporary package test was added under `plugin/k8s_external`:\n\n```go\npackage external\n\nimport (\n    \"testing\"\n    \"time\"\n\n    \"github.com/coredns/coredns/plugin/etcd/msg\"\n    \"github.com/coredns/coredns/plugin/kubernetes\"\n    \"github.com/coredns/coredns/request\"\n\n    \"github.com/miekg/dns\"\n)\n\nfunc TestAuditHeadlessNoPortTransferEmitsEmptyBatch(t *testing.T) {\n    e := New()\n    e.Zones = []string{\"example.com.\"}\n    e.headless = true\n    e.externalAddrFunc = func(request.Request, bool) []dns.RR { return nil }\n    e.externalSerialFunc = func(string) uint32 { return 1 }\n    e.externalServicesFunc = func(string, bool) ([]msg.Service, map[string][]msg.Service) {\n        return nil, map[string][]msg.Service{\n            \"/coredns/com/example/testns/headless/\" + kubernetes.Endpoint: {{Host: \"192.0.2.10\", Port: -1, TTL: 5, Key: \"/coredns/com/example/testns/headless/endpoint-a\"}},\n        }\n    }\n\n    ch, err := e.Transfer(\"example.com.\", 0)\n    if err != nil {\n        t.Fatalf(\"Transfer returned error: %v\", err)\n    }\n\n    deadline := time.After(time.Second)\n    for {\n        select {\n        case batch, ok := \u003c-ch:\n            if !ok {\n                t.Fatal(\"transfer closed before emitting an empty batch\")\n            }\n            if len(batch) == 0 {\n                return\n            }\n        case \u003c-deadline:\n            t.Fatal(\"timed out waiting for an empty batch\")\n        }\n    }\n}\n```\n\nVerification command and result:\n\n```text\n$ go test ./plugin/k8s_external -run TestAuditHeadlessNoPortTransferEmitsEmptyBatch -count=1 -v\n=== RUN   TestAuditHeadlessNoPortTransferEmitsEmptyBatch\n--- PASS: TestAuditHeadlessNoPortTransferEmitsEmptyBatch (0.00s)\nPASS\nok  \tgithub.com/coredns/coredns/plugin/k8s_external\t0.012s\n```\n\nAdditional focused verification:\n\n```text\n$ go test ./plugin/transfer ./plugin/k8s_external ./plugin/kubernetes -run 'TestAudit|^$' -count=1 -v\nok  \tgithub.com/coredns/coredns/plugin/transfer\t0.006s\nok  \tgithub.com/coredns/coredns/plugin/k8s_external\t0.013s\nok  \tgithub.com/coredns/coredns/plugin/kubernetes\t0.116s\n```\n\nAffected package suites:\n\n```text\n$ go test ./plugin/transfer ./plugin/k8s_external ./plugin/kubernetes -count=1\nok  \tgithub.com/coredns/coredns/plugin/transfer\t0.010s\nok  \tgithub.com/coredns/coredns/plugin/k8s_external\t0.011s\nok  \tgithub.com/coredns/coredns/plugin/kubernetes\t0.194s\n```\n\n### Impact\n\nA network client allowed to request AXFR for the affected zone can trigger a panic in CoreDNS when all prerequisites are present.\n\nRequired configuration/state:\n\n```corefile\nexample.com:53 {\n    kubernetes cluster.local\n    k8s_external example.com {\n        headless\n    }\n    transfer example.com {\n        to *\n    }\n}\n```\n\nAdditional prerequisite:\n\n- 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}`.\n\nIn 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.","status":"published","published":"2026-10-16T19:26:42Z","modified":"2026-10-19T21:40:45.304482699Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"low"}],"cwes":["CWE-248","CWE-755"],"updated_at":"2026-11-02T15:46:40.657155Z"},{"id":"AZL-92895","title":"CVE-2026-62994 affecting package coredns for versions less than 1.11.4-20","description":"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.","status":"published","published":"2026-07-16T20:16:47Z","modified":"2026-11-05T05:40:51Z","updated_at":"2026-11-05T16:41:25.880689Z"},{"id":"GO-2026-6805","title":"CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin in github.com/coredns/coredns","description":"CoreDNS `k8s_external` headless AXFR can emit an empty transfer batch that panics the `transfer` plugin in github.com/coredns/coredns","status":"published","published":"2026-10-19T21:08:35Z","modified":"2026-10-19T21:31:38.407421025Z","updated_at":"2026-11-02T15:47:03.11707Z"}]}}
