Path: blob/master/modules/auxiliary/spoof/dns/compare_results.rb
19567 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'net/dns'6require 'resolv'78class MetasploitModule < Msf::Auxiliary910def initialize(info = {})11super(12update_info(13info,14'Name' => 'DNS Lookup Result Comparison',15'Description' => %q{16This module can be used to determine differences17in the cache entries between two DNS servers. This is18primarily useful for detecting cache poisoning attacks,19but can also be used to detect geo-location load balancing.20},21'Author' => [ 'hdm' ],22'License' => MSF_LICENSE,23'References' => [24],25'DisclosureDate' => '2008-07-21',26'Notes' => {27'Stability' => [CRASH_SAFE],28'SideEffects' => [],29'Reliability' => []30}31)32)3334register_options(35[36OptAddress.new('BASEDNS', [ true, 'The DNS cache server to use as a baseline', '4.2.2.3' ]),37OptAddress.new('TARGDNS', [ true, 'The DNS cache server to test', nil ]),38OptString.new('NAMES', [ true, 'The list of host names that should be tested (comma separated)', 'www.google.com,www.yahoo.com,www.msn.com']),39OptBool.new('CHECK_AUTHORITY', [ false, 'Set this to true to verify authority records', false ]),40OptBool.new('CHECK_ADDITIONAL', [ false, 'Set this to true to verify additional records', false ]),4142]43)44end4546def run47base_addr = datastore['BASEDNS']48targ_addr = datastore['TARGDNS']49check_ar = datastore['CHECK_ADDITIONAL']50check_aa = datastore['CHECK_AUTHORITY']51names = datastore['NAMES'].split(',').map(&:strip)52recurse = true53results = {}5455print_status("Comparing results between #{base_addr} and #{targ_addr}...")5657base_sock = Rex::Socket.create_udp(58'PeerHost' => base_addr,59'PeerPort' => 5360)6162targ_sock = Rex::Socket.create_udp(63'PeerHost' => targ_addr,64'PeerPort' => 5365)6667names.each do |entry|68entry.strip!69next if entry.empty?7071req = Resolv::DNS::Message.new72req.add_question(entry, Resolv::DNS::Resource::IN::A)73req.rd = recurse ? 1 : 07475buf = req.encode76print_status("Querying servers for #{entry}...")77base_sock.put(buf)78targ_sock.put(buf)7980base_res, = base_sock.recvfrom(65535, 3.0)81targ_res, = targ_sock.recvfrom(65535, 3.0)8283if !(base_res && targ_res && !base_res.empty? && !targ_res.empty?)84print_error(' Error: The baseline server did not respond to our request.') if !(base_res && !base_res.empty?)85print_error(' Error: The target server did not respond to our request.') if !(targ_res && !targ_res.empty?)86next87end8889base_res = Resolv::DNS::Message.decode(base_res)90targ_res = Resolv::DNS::Message.decode(targ_res)9192[base_res, targ_res].each do |res|93hkey = (res == base_res) ? :base : :targ9495rrset = res.answer96rrset += res.authority if check_aa97rrset += res.additional if check_ar9899rrset.each do |ref|100name, _, data = ref101102name.to_s103anst = data.class.to_s.gsub(/^.*Resolv::DNS::Resource::IN::/, '')104case data105when Resolv::DNS::Resource::IN::NS106data = data.name.to_s107when Resolv::DNS::Resource::IN::MX108data = data.exchange.to_s109when Resolv::DNS::Resource::IN::A110data = data.address.to_s111when Resolv::DNS::Resource::IN::TXT112data = data.strings.join113when Resolv::DNS::Resource::IN::CNAME114data = data.name.to_s115else116data = anst117end118119results[entry] ||= {}120results[entry][hkey] ||= {}121results[entry][hkey][anst] ||= []122results[entry][hkey][anst] << data123end124end125end126127[ base_sock, targ_sock ].each(&:close)128129print_status("Analyzing results for #{results.keys.length} entries...")130131results.each_key do |entry|132n_add = []133n_sub = []134135# Look for additional entries in the target NS136if (results[entry][:targ])137results[entry][:targ].each_key do |rtype|138next unless !(results[entry][:base]) || !(results[entry][:base][rtype])139140results[entry][:targ][rtype].sort.each do |ref|141n_sub << (" + #{entry} #{rtype} #{ref}")142end143end144end145146if (results[entry][:base])147results[entry][:base].each_key do |rtype|148# Look for missing entries in the target NS149if !(results[entry][:targ]) || !(results[entry][:targ][rtype])150results[entry][:base][rtype].sort.each do |ref|151n_sub << (" - #{entry} #{rtype} #{ref}")152end153next154end155156# Look for differences157next unless (results[entry][:base][rtype].sort != results[entry][:targ][rtype].sort)158159results[entry][:base][rtype].sort.each do |ref|160if !results[entry][:targ][rtype].include?(ref)161n_sub << (" - #{entry} #{rtype} #{ref}")162end163end164results[entry][:targ][rtype].sort.each do |ref|165if !results[entry][:base][rtype].include?(ref)166n_add << (" + #{entry} #{rtype} #{ref}")167end168end169end170end171172n_sub.each { |s| print_status(s) }173n_add.each { |s| print_status(s) }174end175end176end177178179