# CoreDNS DoH/DoQ/gRPC bypass UPDATE rejection enforced on UDP/TCP

CoreDNS DoH/DoQ/gRPC listeners skipped the opcode policy applied on UDP/TCP, so unauthenticated RFC 2136 UPDATEs were forwarded upstream as if from CoreDNS.

- Payout: Research
- Severity: high
- Program: CoreDNS
- Found by: thevilledev
- Disclosed: 2026-09-17
- CVE: CVE-2026-86003
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-9gm5-9rfh-m6vx
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-9gm5-9rfh-m6vx
- Tags: area:devices domain:infra domain:network impact:integrity kind:research platform:ghsa sev:high tech:go technique:chained technique:source-review vuln:auth-bypass vuln:misconfiguration

**Target:** CoreDNS DoH/DoH3/DoQ/DNS-over-gRPC listeners with forward/proxy plugin
**Root cause:** DoH, DoQ and gRPC listeners called dns.Msg.Unpack without the DefaultMsgAcceptFunc opcode policy used by the UDP/TCP server, so UPDATE (opcode 5) reached plugin dispatch and forward passed the original message upstream.
**Steps:**
1. Build pre-fix CoreDNS at d5e54040ffab9a5c12c6de27b66f59f62b385195 with `go build -tags=grpcnotrace -o coredns .`
2. Run with a Corefile exposing DoH on 127.0.0.1:8053 and `forward . 127.0.0.1:15354`
3. Start a synthetic UDP/TCP upstream on 15354 that records UPDATE messages
4. POST an unsigned RFC 2136 UPDATE (opcode 5, zone example.com., record foo.example.com. 300 IN A 192.0.2.123) to `/dns-query` with `Content-Type: application/dns-message`
5. Observe upstream receives the full UPDATE from CoreDNS's address; fixed build returns HTTP 400 and never forwards
**How found:** Compared transport handlers: UDP/TCP/DoT applied dns.DefaultMsgAcceptFunc (QUERY/NOTIFY only) before dispatch, while DoH/DoQ/gRPC called Unpack directly and routed on the Zone question without checking opcode.
**Exploitation:** Attacker with network access to a DoH/DoQ/gRPC listener sends an unsigned UPDATE; CoreDNS forwards it unchanged to an update-capable upstream that trusts CoreDNS's source address or connection, letting the attacker add/replace/delete records in the writable zone.
**Insight:** When a server has multiple transports, diff the request-acceptance path per listener: alternate transports often skip the opcode/policy gate the primary server enforces, turning the proxy into a confused deputy for privileged upstreams.

