140 lines
4.8 KiB
Ruby
140 lines
4.8 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
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 amplication attack against a
|
|
third party.
|
|
},
|
|
'Author' => [ 'xistence <xistence[at]0x90.nl>'], # Original scanner module
|
|
'License' => MSF_LICENSE
|
|
)
|
|
|
|
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' ]),
|
|
], self.class)
|
|
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 => [ "CVE-2006-0987", "CVE-2006-0988" ])
|
|
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
|