## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Report include Msf::Exploit::Capture include Msf::Auxiliary::UDPScanner include Msf::Auxiliary::DRDoS def initialize super( 'Name' => 'DNS Amplification Scanner', 'Description' => %q{ This module can be used to discover DNS servers which expose recursive name lookups which can be used in an amplification attack against a third party. }, 'Author' => [ 'xistence '], # Original scanner module 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2006-0987'], ['CVE', '2006-0988'], ] ) register_options( [ Opt::RPORT(53), OptString.new('DOMAINNAME', [true, 'Domain to use for the DNS request', 'isc.org' ]), OptString.new('QUERYTYPE', [true, 'Query type(A, NS, SOA, MX, TXT, AAAA, RRSIG, DNSKEY, ANY)', 'ANY' ]), ]) end def rport datastore['RPORT'] end def setup super # Check for DNS query types byte case datastore['QUERYTYPE'] when 'A' querypacket="\x01" when 'NS' querypacket="\x02" when 'SOA' querypacket="\x06" when 'MX' querypacket="\x0f" when 'TXT' querypacket="\x10" when 'AAAA' querypacket="\x1c" when 'RRSIG' querypacket="\x2e" when 'DNSKEY' querypacket="\x30" when 'ANY' querypacket="\xff" else print_error("Invalid query type!") return end targdomainpacket = [] # Before every part of the domainname there should be the length of that part (instead of a ".") # So isc.org divided is 3isc3org datastore['DOMAINNAME'].split('.').each do |domainpart| # The length of the domain part in hex domainpartlength = "%02x" % domainpart.length # Convert the name part to a hex string domainpart = domainpart.each_byte.map { |b| b.to_s(16) }.join() # Combine the length of the name part and the name part targdomainpacket.push(domainpartlength + domainpart) end # Convert the targdomainpacket to a string targdomainpacket = targdomainpacket.join.to_s # Create a correct hex character string to be used in the packet targdomainpacket = targdomainpacket.scan(/../).map { |x| x.hex.chr }.join # DNS Packet including our target domain and query type @msearch_probe = "\x09\x8d\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" + targdomainpacket + "\x00\x00" + querypacket + "\x00\x01" end def scanner_prescan(batch) print_status("Sending DNS probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)") # Standard packet is 60 bytes. Add the domain size to this sendpacketsize = 60 + datastore['DOMAINNAME'].length print_status("Sending #{sendpacketsize} bytes to each host using the IN #{datastore['QUERYTYPE']} #{datastore['DOMAINNAME']} request") @results = {} end def scan_host(ip) if spoofed? datastore['ScannerRecvWindow'] = 0 scanner_spoof_send(@msearch_probe, ip, datastore['RPORT'], datastore['SRCIP'], datastore['NUM_REQUESTS']) else scanner_send(@msearch_probe, ip, datastore['RPORT']) end end def scanner_process(data, shost, sport) # Check the response data for \x09\x8d and the next 2 bytes, which contain our DNS flags if data =~/\x09\x8d(..)/ flags = $1 flags = flags.unpack('B*')[0].scan(/./) # Query Response qr = flags[0] # Recursion Available ra = flags[8] # Response Code rcode = flags[12] + flags[13] + flags[14] + flags[15] # If these flags are set, we get a valid response # don't test recursion available if correct answer received # at least the case with bind and "additional-from-cache no" or version < 9.5+ if qr == "1" and rcode == "0000" sendlength = 60 + datastore['DOMAINNAME'].length receivelength = 42 + data.length amp = receivelength / sendlength.to_f print_good("#{shost}:#{datastore['RPORT']} - Response is #{receivelength} bytes [#{amp.round(2)}x Amplification]") report_service(:host => shost, :port => datastore['RPORT'], :proto => 'udp', :name => "dns") report_vuln( :host => shost, :port => datastore['RPORT'], :proto => 'udp', :name => "DNS", :info => "DNS amplification - #{data.length} bytes [#{amp.round(2)}x Amplification]", :refs => self.references) end # If these flags are set, we get a valid response but recursion is not available if qr == "1" and ra == "0" and rcode == "0101" print_status("#{shost}:#{datastore['RPORT']} - Recursion not allowed") report_service(:host => shost, :port => datastore['RPORT'], :proto => 'udp', :name => "dns") end end end end