From 2347c8df999737f138e5ccff2d7bfcd5dad670a8 Mon Sep 17 00:00:00 2001 From: RageLtMan Date: Mon, 29 Feb 2016 02:52:41 -0500 Subject: [PATCH] Create basic packet manipulation modules Create Rex::Proto::DNS::Packet and Packet::Raw to allow common parsing, validation, and raw data operations across both Rex and Msf namespaces. The modules contain class methods and do not need to be mixed in to use their functionality Packet.method is enough, and reduces GC strain since new objects are not constantly being instantiated, and these modules contain no internal state. Clean up UDP socket leak from Rex::Proto::DNS::Server under certain conditions. Create Msf::Exploit::DNS::Common mixin to provide descendants with access to Packet and the hostname Regex. ----- Testing: Tested running the RC provided in the pull request Manual testing in IRB/Pry while porting PoC for CVE-2015-7547 --- lib/msf/core/exploit/dns.rb | 1 + lib/msf/core/exploit/dns/client.rb | 1 + lib/msf/core/exploit/dns/common.rb | 22 +++ lib/msf/core/exploit/dns/server.rb | 8 +- lib/rex/proto/dns.rb | 1 + lib/rex/proto/dns/packet.rb | 136 ++++++++++++++++++ lib/rex/proto/dns/server.rb | 18 ++- modules/auxiliary/server/dns/native_server.rb | 9 +- 8 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 lib/msf/core/exploit/dns/common.rb create mode 100644 lib/rex/proto/dns/packet.rb 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}")