Skip to content

Fix DNS forward/cache path for relay-driven poisoning - #21853

Open
Pushpenderrathore wants to merge 1 commit into
rapid7:masterfrom
Pushpenderrathore:fix/dns-forward-cache-relay-poisoning
Open

Fix DNS forward/cache path for relay-driven poisoning#21853
Pushpenderrathore wants to merge 1 commit into
rapid7:masterfrom
Pushpenderrathore:fix/dns-forward-cache-relay-poisoning

Conversation

@Pushpenderrathore

@Pushpenderrathore Pushpenderrathore commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Description

Three fixes to the core Rex::Proto::DNS forward/cache path that surface once the DNS server is used as a selective poisoner in front of a real upstream resolver (the Kerberos relay coercion workflow in #21693). Identified during @jheysel-r7's ESC8 relay testing.

Each is small and independent:

  1. Cache#cache_record crashed on non-cacheable forwarded records. Real upstream responses carry records whose names do not match MATCH_HOSTNAME; caching them raised and killed the dispatch thread. Skip the record instead of raising.

  2. Shallow dup shared the question array. Dnsruby::Message#dup is shallow, so req.question and forward.question referenced the same Array. Deleting a cache-served question from the forwarded packet also emptied the original request's question list, so the echoed response lost its questions. forward now gets its own copy.

  3. Mutating a decoded request's @answer produced a malformed packet. Building the reply by instance_variable_set(:@answer, ...) on the decoded request and re-encoding appended the answer bytes after the packet end, so clients decoded zero answers (surfaces as "Bad DNS packet" on Windows). The response is now built as a fresh Dnsruby::Message.

Also handles udp_sock.recvfrom returning an explicit source port so the reply is addressed to the right host/port across socket variants.

Verification

Both non-obvious bugs (2 and 3) were confirmed empirically against the pinned dnsruby before and after the fix, and are covered by new regression specs.

$ bundle exec rspec spec/lib/rex/proto/dns/server_spec.rb
11 examples, 0 failures

The existing coverage from #21784 (empty-forward response) still passes.

Related

Three issues surfaced while running the DNS server under the Kerberos relay
coercion workflow (identified during jheysel's ESC8 testing):

- Rex::Proto::DNS::Cache#cache_record raised on any forwarded record whose
  name did not match MATCH_HOSTNAME, killing the dispatch thread. Skip the
  non-cacheable record instead.

- Server#default_dispatch_request duplicated the request with Dnsruby::Message#dup,
  which is shallow, so req.question and forward.question shared one Array.
  Deleting a cache-served question from the forwarded packet also emptied the
  original request's question list. Give forward its own copy.

- The response was built by mutating the decoded request's @answer via
  instance_variable_set and re-encoding it, which appended the answer bytes
  past the packet end so clients decoded zero answers ("bad DNS packet" on
  Windows). Build a fresh Dnsruby::Message for the response instead.

Also handle the udp recvfrom returning an explicit source port so the reply
goes back to the right host/port across socket variants.

Adds regression specs for the shallow-dup question handling and the
answer-encoding fix.
@Pushpenderrathore
Pushpenderrathore force-pushed the fix/dns-forward-cache-relay-poisoning branch from 40fbf90 to b0d920f Compare August 31, 2026 19:33
@jheysel-r7 jheysel-r7 self-assigned this Aug 31, 2026
@jheysel-r7
jheysel-r7 requested a balanced review from Copilot August 31, 2026 20:36
@jheysel-r7 jheysel-r7 added rn-fix release notes fix library labels Aug 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes DNS forwarding, caching, response encoding, and UDP reply addressing for relay-driven poisoning.

Changes:

  • Skips invalid-hostname cache records.
  • Safely separates forwarded questions and rebuilds DNS responses.
  • Supports alternate UDP recvfrom return shapes.

Impact Analysis:

  • Blast radius: Medium; affects default DNS forwarding and UDP listeners.
  • Data and contract effects: Changes DNS response construction and cache behavior; RD is not currently preserved.
  • Rollback and test focus: Reversible; validate DNS flags, cache rejection, and both UDP address formats.
File summaries
File Description
lib/rex/proto/dns/cache.rb Skips non-cacheable hostnames.
lib/rex/proto/dns/server.rb Fixes forwarding, response encoding, and UDP source handling.
spec/lib/rex/proto/dns/server_spec.rb Adds forwarding and encoding regressions.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 59 to +60
unless record.name.to_s.match(MATCH_HOSTNAME)
raise "Invalid record for cache entry (invalid hostname) - #{record.inspect}"
return # skip non-cacheable record: " - #{record.inspect}"
Comment on lines +226 to +230
buf, addr, source_port = self.udp_sock.recvfrom(65535)
if source_port
host, port = addr, source_port
else
host, port = addr[3], addr[1]
Comment on lines +188 to +191
resp = Dnsruby::Message.new
resp.header.id = req.header.id
resp.header.qr = true
resp.header.ra = req.header.rd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

library rn-fix release notes fix

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

3 participants