diff --git a/lib/msf/core/exploit/dns.rb b/lib/msf/core/exploit/dns.rb index 00cba925f9..5d7457de16 100644 --- a/lib/msf/core/exploit/dns.rb +++ b/lib/msf/core/exploit/dns.rb @@ -15,5 +15,6 @@ module Exploit::Remote::DNS end end +require 'msf/core/exploit/dns/common' require 'msf/core/exploit/dns/client' require 'msf/core/exploit/dns/server' diff --git a/lib/msf/core/exploit/dns/client.rb b/lib/msf/core/exploit/dns/client.rb index c9ba8b1bc0..61b2cbfce0 100644 --- a/lib/msf/core/exploit/dns/client.rb +++ b/lib/msf/core/exploit/dns/client.rb @@ -13,6 +13,7 @@ module Msf module Exploit::Remote::DNS module Client + include Common include Exploit::Remote::Udp include Exploit::Remote::Tcp diff --git a/lib/msf/core/exploit/dns/common.rb b/lib/msf/core/exploit/dns/common.rb new file mode 100644 index 0000000000..810172bdf2 --- /dev/null +++ b/lib/msf/core/exploit/dns/common.rb @@ -0,0 +1,22 @@ +# -*- coding: binary -*- +require 'msf/core' +require 'rex/proto/dns' + + +module Msf + +### +# +# This module exposes methods for querying a remote DNS service +# +### +module Exploit::Remote::DNS +module Common + + MATCH_HOSTNAME = Rex::Proto::DNS::Constants::MATCH_HOSTNAME + + Packet = Rex::Proto::DNS::Packet + +end +end +end diff --git a/lib/msf/core/exploit/dns/server.rb b/lib/msf/core/exploit/dns/server.rb index 299acd1005..01f8265ac9 100644 --- a/lib/msf/core/exploit/dns/server.rb +++ b/lib/msf/core/exploit/dns/server.rb @@ -12,10 +12,9 @@ module Msf ### module Exploit::Remote::DNS module Server + include Common include Exploit::Remote::SocketServer - MATCH_HOSTNAME = Rex::Proto::DNS::Constants::MATCH_HOSTNAME - # # Initializes an exploit module that serves DNS requests # @@ -156,7 +155,10 @@ module Server # @param destroy [TrueClass,FalseClass] Dereference the server object def stop_service(destroy = false) self.service.stop unless self.service.nil? - self.service = nil if destroy + if destroy + @dns_resolver = nil + self.service = nil + end end # diff --git a/lib/rex/proto/dns.rb b/lib/rex/proto/dns.rb index 793c626caa..96c5419c5e 100644 --- a/lib/rex/proto/dns.rb +++ b/lib/rex/proto/dns.rb @@ -12,5 +12,6 @@ end end end +require 'rex/proto/dns/packet' require 'rex/proto/dns/resolver' require 'rex/proto/dns/server' diff --git a/lib/rex/proto/dns/packet.rb b/lib/rex/proto/dns/packet.rb new file mode 100644 index 0000000000..a81357d00b --- /dev/null +++ b/lib/rex/proto/dns/packet.rb @@ -0,0 +1,136 @@ +require 'net/dns' +require 'resolv' + +module Rex +module Proto +module DNS + +module Packet + + # + # Reconstructs a packet with both standard DNS libraries + # Ensures that headers match the payload + # + # @param packet [String, Net::DNS::Packet] Data to be validated + # + # @return [Net::DNS::Packet] + def self.validate(packet) + Net::DNS::Packet.parse( + Resolv::DNS::Message.decode( + packet.respond_to?(:data) ? packet.data : packet + ).encode + ) + end + + # + # Reads a packet into the Net::DNS::Packet format + # + # @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data + # + # @return [Net::DNS::Packet] + def self.encode_net(packet) + return packet if packet.respond_to?(:data) + Net::DNS::Packet.parse( + packet.respond_to?(:decode) ? packet.encode : packet + ) + end + + # Reads a packet into the Resolv::DNS::Message format + # + # @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data + # + # @return [Resolv::DNS::Message] + def self.encode_res(packet) + return packet if packet.respond_to?(:decode) + Resolv::DNS::Message.decode( + packet.respond_to?(:data) ? packet.data : packet + ) + end + + # Reads a packet into the raw String format + # + # @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data + # + # @return [Resolv::DNS::Message] + def self.encode_raw(packet) + return packet unless packet.respond_to?(:decode) or packet.respond_to?(:data) + packet.respond_to?(:data) ? packet.data : packet.encode + end + + module Raw + + # + # Convert data to big endian unsigned short + # + # @param data [Fixnum, Float, Array] Input for conversion + # + # @return [String] Raw output + def self.to_dw(data) + [data].flatten.pack('S>*') + end + + # + # Convert data from big endian unsigned short + # + # @param data [String] Input for conversion + # + # @return [Array] Integer array output + def self.from_dw(data) + data.unpack('S>*') + end + + # + # Convert data to big endian unsigned int + # + # @param data [Fixnum, Float, Array] Input for conversion + # + # @return [String] Raw output + def self.to_dd(data) + [data].flatten.pack('I>*') + end + + # + # Convert data from big endian unsigned int + # + # @param data [String] Input for conversion + # + # @return [Array] Integer array output + def self.from_dd(data) + data.unpack('I>*') + end + + # + # Convert data to big endian unsigned long + # + # @param data [Fixnum, Float, Array] Input for conversion + # + # @return [String] Raw output + def self.to_dl(data) + [data].flatten.pack('L>*') + end + + # + # Convert data from big endian unsigned long + # + # @param data [String] Input for conversion + # + # @return [Array] Integer array output + def self.from_dl(data) + data.unpack('L>*') + end + + # + # Returns request ID from raw packet skipping parsing + # + # @param data [String] Request data + # + # @return [Fixnum] Request ID + def self.request_id(data) + self.from_dw(data[0..1])[0] + end + end +end + +end +end +end diff --git a/lib/rex/proto/dns/server.rb b/lib/rex/proto/dns/server.rb index 6a0592130e..afef3f7ec2 100644 --- a/lib/rex/proto/dns/server.rb +++ b/lib/rex/proto/dns/server.rb @@ -213,18 +213,16 @@ class Server # # @param flush_cache [TrueClass,FalseClass] Flush eDNS cache on stop def stop(flush_cache = false) - if self.udp_sock - self.listener_thread.kill + ensure_close = [self.udp_sock, self.tcp_sock].compact + begin + self.listener_thread.kill if self.listener_thread.respond_to?(:kill) self.listener_thread = nil - @udp_sock = nil + ensure + while csock = ensure_close.shift + csock.stop if csock.respond_to?(:stop) + csock.close unless csock.closed? + end end - - if self.tcp_sock - self.tcp_sock.stop if self.tcp_sock.respond_to?(:stop) - self.tcp_sock.close if self.tcp_sock.respond_to?(:close) - @tcp_sock = nil - end - self.cache.stop(flush_cache) end diff --git a/modules/auxiliary/server/dns/native_server.rb b/modules/auxiliary/server/dns/native_server.rb index 140aafbbb0..523255ba27 100644 --- a/modules/auxiliary/server/dns/native_server.rb +++ b/modules/auxiliary/server/dns/native_server.rb @@ -35,7 +35,6 @@ class Metasploit3 < Msf::Auxiliary def run begin start_service - primer service.wait ensure stop_service(true) @@ -46,7 +45,7 @@ class Metasploit3 < Msf::Auxiliary # Creates Proc to handle incoming requests # def on_dispatch_request(cli,data) - req = Net::DNS::Packet.parse(data) + req = Packet.encode_net(data) peer = "#{cli.peerhost}:#{cli.peerport}" asked = req.question.map(&:qName).join(', ') vprint_status("Received request for #{asked} from #{peer}") @@ -66,7 +65,7 @@ class Metasploit3 < Msf::Auxiliary end unless service.cache.nil? # Forward remaining requests, cache responses if forward.question.count > 0 and service.fwd_res - forwarded = service.fwd_res.send(service.validate_packet(forward)) + forwarded = service.fwd_res.send(Packet.validate(forward)) forwarded.answer.each do |ans| vprint_status("Caching response #{ans.name}:#{ans.address} #{ans.type}") service.cache.cache_record(ans) @@ -76,14 +75,14 @@ class Metasploit3 < Msf::Auxiliary req = forwarded end req.header.qr = 1 # Set response bit - service.send_response(cli, service.validate_packet(req).data) + service.send_response(cli, Packet.validate(req).data) end # # Creates Proc to handle outbound responses # def on_send_response(cli,data) - res = Net::DNS::Packet.parse(data) + res = Packet.encode_net(data) peer = "#{cli.peerhost}:#{cli.peerport}" asked = res.question.map(&:qName).join(', ') vprint_status("Sending response for #{asked} to #{peer}")