Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/dns/dns_amp.rb
19851 views
1
# encoding: binary
2
3
##
4
# This module requires Metasploit: https://metasploit.com/download
5
# Current source: https://github.com/rapid7/metasploit-framework
6
##
7
8
class MetasploitModule < Msf::Auxiliary
9
include Msf::Auxiliary::Report
10
include Msf::Exploit::Capture
11
include Msf::Auxiliary::UDPScanner
12
include Msf::Auxiliary::DRDoS
13
14
def initialize
15
super(
16
'Name' => 'DNS Amplification Scanner',
17
'Description' => %q{
18
This module can be used to discover DNS servers which expose recursive
19
name lookups which can be used in an amplification attack against a
20
third party.
21
},
22
'Author' => [ 'xistence <xistence[at]0x90.nl>'], # Original scanner module
23
'License' => MSF_LICENSE,
24
'References' => [
25
['CVE', '2006-0987'],
26
['CVE', '2006-0988'],
27
]
28
)
29
30
register_options([
31
Opt::RPORT(53),
32
OptString.new('DOMAINNAME', [true, 'Domain to use for the DNS request', 'isc.org' ]),
33
OptString.new('QUERYTYPE', [true, 'Query type(A, NS, SOA, MX, TXT, AAAA, RRSIG, DNSKEY, ANY)', 'ANY' ]),
34
])
35
end
36
37
def rport
38
datastore['RPORT']
39
end
40
41
def setup
42
super
43
44
# Check for DNS query types byte
45
case datastore['QUERYTYPE']
46
when 'A'
47
querypacket = "\x01"
48
when 'NS'
49
querypacket = "\x02"
50
when 'SOA'
51
querypacket = "\x06"
52
when 'MX'
53
querypacket = "\x0f"
54
when 'TXT'
55
querypacket = "\x10"
56
when 'AAAA'
57
querypacket = "\x1c"
58
when 'RRSIG'
59
querypacket = "\x2e"
60
when 'DNSKEY'
61
querypacket = "\x30"
62
when 'ANY'
63
querypacket = "\xff"
64
else
65
print_error("Invalid query type!")
66
return
67
end
68
69
targdomainpacket = []
70
# Before every part of the domainname there should be the length of that part (instead of a ".")
71
# So isc.org divided is 3isc3org
72
datastore['DOMAINNAME'].split('.').each do |domainpart|
73
# The length of the domain part in hex
74
domainpartlength = "%02x" % domainpart.length
75
# Convert the name part to a hex string
76
domainpart = domainpart.each_byte.map { |b| b.to_s(16) }.join()
77
# Combine the length of the name part and the name part
78
targdomainpacket.push(domainpartlength + domainpart)
79
end
80
# Convert the targdomainpacket to a string
81
targdomainpacket = targdomainpacket.join.to_s
82
# Create a correct hex character string to be used in the packet
83
targdomainpacket = targdomainpacket.scan(/../).map { |x| x.hex.chr }.join
84
# DNS Packet including our target domain and query type
85
@msearch_probe = "\x09\x8d\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" + targdomainpacket + "\x00\x00" + querypacket + "\x00\x01"
86
end
87
88
def scanner_prescan(batch)
89
print_status("Sending DNS probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
90
# Standard packet is 60 bytes. Add the domain size to this
91
sendpacketsize = 60 + datastore['DOMAINNAME'].length
92
print_status("Sending #{sendpacketsize} bytes to each host using the IN #{datastore['QUERYTYPE']} #{datastore['DOMAINNAME']} request")
93
@results = {}
94
end
95
96
def scan_host(ip)
97
if spoofed?
98
datastore['ScannerRecvWindow'] = 0
99
scanner_spoof_send(@msearch_probe, ip, datastore['RPORT'], datastore['SRCIP'], datastore['NUM_REQUESTS'])
100
else
101
scanner_send(@msearch_probe, ip, datastore['RPORT'])
102
end
103
end
104
105
def scanner_process(data, shost, sport)
106
# Check the response data for \x09\x8d and the next 2 bytes, which contain our DNS flags
107
if data =~ /\x09\x8d(..)/
108
flags = $1
109
flags = flags.unpack('B*')[0].scan(/./)
110
# Query Response
111
qr = flags[0]
112
# Recursion Available
113
ra = flags[8]
114
# Response Code
115
rcode = flags[12] + flags[13] + flags[14] + flags[15]
116
117
# If these flags are set, we get a valid response
118
# don't test recursion available if correct answer received
119
# at least the case with bind and "additional-from-cache no" or version < 9.5+
120
if qr == "1" and rcode == "0000"
121
sendlength = 60 + datastore['DOMAINNAME'].length
122
receivelength = 42 + data.length
123
amp = receivelength / sendlength.to_f
124
print_good("#{shost}:#{datastore['RPORT']} - Response is #{receivelength} bytes [#{amp.round(2)}x Amplification]")
125
report_service(:host => shost, :port => datastore['RPORT'], :proto => 'udp', :name => "dns")
126
report_vuln(
127
:host => shost,
128
:port => datastore['RPORT'],
129
:proto => 'udp', :name => "DNS",
130
:info => "DNS amplification - #{data.length} bytes [#{amp.round(2)}x Amplification]",
131
:refs => self.references
132
)
133
end
134
135
# If these flags are set, we get a valid response but recursion is not available
136
if qr == "1" and ra == "0" and rcode == "0101"
137
print_status("#{shost}:#{datastore['RPORT']} - Recursion not allowed")
138
report_service(:host => shost, :port => datastore['RPORT'], :proto => 'udp', :name => "dns")
139
end
140
end
141
end
142
end
143
144