Skip to content

Fix two crashes in the default DNS forward/cache path - #21784

Open
Pushpenderrathore wants to merge 1 commit into
rapid7:masterfrom
Pushpenderrathore:fix/dns-server-default-dispatch-request
Open

Fix two crashes in the default DNS forward/cache path#21784
Pushpenderrathore wants to merge 1 commit into
rapid7:masterfrom
Pushpenderrathore:fix/dns-server-default-dispatch-request

Conversation

@Pushpenderrathore

Copy link
Copy Markdown
Contributor

Description

Rex::Proto::DNS::Server#default_dispatch_request crashes the moment it has to finalize an empty response. That happens on any query that misses the cache and gets forwarded to the resolver with no answer coming back, which in practice includes any out-of-scope query being forwarded through unchanged, such as the PTR reverse lookup a real client sends before its actual query. The exception is uncaught, so it kills the listener thread and stops the whole server, silently, on the very first such query.

Two mistakes in the same two lines, both against Dnsruby::Message, the type Packet.encode_drb documents req as being:

if req.answer.size < 1
  req.header.rCode = Dnsruby::RCode::NOERROR
end
req.header.qr = true
send_response(cli, req.data)
  • req.header.rCode= does not exist on Dnsruby::Header. The real setter is rcode= (lowercase). The class does expose an rCode-cased reader alias elsewhere in its API, which is presumably where the casing came from.
  • req.data does not exist on Dnsruby::Message. The real serializer is #encode. Packet.encode_raw, a few lines above in this same file, already gets this right:
    (packet.respond_to?(:data) ? packet.data : packet.encode).force_encoding('binary')
    default_dispatch_request just used the wrong branch of the same data/encode distinction this file already knows how to make.

No spec covered this path, so both shipped and stayed live.

Live reproduction

I hit this while building an IPv6 DNS-takeover module. Once the crash was fixed I could see it happening: with only the first fix applied, the second line raised immediately on the next real query.

[-] Auxiliary failed: NoMethodError undefined method `rCode=' for an instance of Dnsruby::Header
[-]   .../lib/rex/proto/dns/server.rb:188:in `default_dispatch_request'
[-]   .../lib/msf/core/exploit/remote/dns/name_poisoner.rb:54:in `on_dispatch_request'
[*] Server stopped.
[-] Auxiliary failed: NoMethodError undefined method `data' for an instance of Dnsruby::Message
[-]   .../lib/rex/proto/dns/server.rb:191:in `default_dispatch_request'
[-]   .../lib/msf/core/exploit/remote/dns/name_poisoner.rb:54:in `on_dispatch_request'
[*] Server stopped.

With both fixed, the same real client's query is handled and forwarded correctly, and the server survives.

Testing

Added spec/lib/rex/proto/dns/server_spec.rb, covering the path that crashed: a forwarded query whose response carries no answers. Confirmed the new spec fails against the pre-fix code with the exact errors above, and passes with the fix.

$ bundle exec rspec spec/lib/rex/proto/dns/
53 examples, 0 failures

Breaking Changes

None. Both lines were unreachable without raising; nothing depended on the old behavior.

Rex::Proto::DNS::Server#default_dispatch_request crashed with a
NoMethodError the moment it had to finalize an empty response, which
happens on any query that misses the cache and comes back from the
resolver with no answers - an out-of-scope query being forwarded, for
instance, or any query with a genuinely empty result. The exception was
uncaught, so it killed the listener thread and stopped the whole server.

Two mistakes, both on code Packet.encode_drb documents as returning a
Dnsruby::Message:

- req.header.rCode= does not exist on Dnsruby::Header; the real setter is
  rcode= (lowercase). The class does define an rCode-cased method
  elsewhere as a getter alias, which is presumably what led to the wrong
  casing here.
- req.data does not exist on Dnsruby::Message either; the real
  serializer is #encode. Packet.encode_raw, a few lines above in the
  same file, already handles this correctly by checking respond_to?(:data)
  for a legacy Net::DNS::Packet and falling back to #encode otherwise -
  default_dispatch_request just used the wrong branch of that same
  distinction.

No existing spec covered this path, so both bugs shipped and stayed live.
Reproduced against a real client. Fixed and added coverage for the empty
answer case that crashed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant