|
| 1 | +## |
| 2 | +# ## This file is part of the Metasploit Framework and may be subject to |
| 3 | +# redistribution and commercial restrictions. Please see the Metasploit |
| 4 | +# web site for more information on licensing and terms of use. |
| 5 | +# http://metasploit.com/ |
| 6 | +## |
| 7 | + |
| 8 | +require 'msf/core' |
| 9 | +require "net/dns/resolver" |
| 10 | +require 'rex' |
| 11 | + |
| 12 | +class Metasploit3 < Msf::Auxiliary |
| 13 | + include Msf::Auxiliary::Report |
| 14 | + |
| 15 | + def initialize(info = {}) |
| 16 | + super(update_info(info, |
| 17 | + 'Name' => 'DNS Basic Information Enumeration', |
| 18 | + 'Description' => %q{ |
| 19 | + This module enumerates basic DNS information for a given domain. The module |
| 20 | + gets information regarding to A (addresses), AAAA (IPv6 addresses), NS (name |
| 21 | + servers), SOA (start of authority) and MX (mail servers) records for a given |
| 22 | + domain. In addition, this module retrieves information stored in TXT records. |
| 23 | + }, |
| 24 | + 'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>' ], |
| 25 | + 'License' => BSD_LICENSE |
| 26 | + )) |
| 27 | + |
| 28 | + register_options( |
| 29 | + [ |
| 30 | + OptString.new('DOMAIN', [ true, "The target domain name"]), |
| 31 | + OptAddress.new('NS', [ false, "Specify the name server to use for queries, otherwise use the system configured DNS Server is used." ]), |
| 32 | + |
| 33 | + ], self.class) |
| 34 | + |
| 35 | + register_advanced_options( |
| 36 | + [ |
| 37 | + OptInt.new('RETRY', [ false, "Number of tries to resolve a record if no response is received.", 2]), |
| 38 | + OptInt.new('RETRY_INTERVAL', [ false, "Number of seconds to wait before doing a retry.", 2]), |
| 39 | + ], self.class) |
| 40 | + end |
| 41 | + |
| 42 | + def run |
| 43 | + print_status("Enumerating #{datastore['DOMAIN']}") |
| 44 | + @res = Net::DNS::Resolver.new() |
| 45 | + |
| 46 | + if datastore['RETRY'] |
| 47 | + @res.retry = datastore['RETRY'].to_i |
| 48 | + end |
| 49 | + |
| 50 | + if datastore['RETRY_INTERVAL'] |
| 51 | + @res.retry_interval = datastore['RETRY_INTERVAL'].to_i |
| 52 | + end |
| 53 | + |
| 54 | + wildcard(datastore['DOMAIN']) |
| 55 | + switchdns() unless datastore['NS'].nil? or datastore['NS'].empty? |
| 56 | + |
| 57 | + get_ip(datastore['DOMAIN']).each do |r| |
| 58 | + print_good("#{r[:host]} - Address #{r[:address]} found. Record type: #{r[:type]}") |
| 59 | + report_host(:host => r[:address]) |
| 60 | + end |
| 61 | + |
| 62 | + get_ns(datastore['DOMAIN']).each do |r| |
| 63 | + print_good("#{datastore['DOMAIN']} - Name server #{r[:host]} (#{r[:address]}) found. Record type: #{r[:type]}") |
| 64 | + report_host(:host => r[:address], :name => r[:host]) |
| 65 | + report_service( |
| 66 | + :host => r[:address], |
| 67 | + :name => "dns", |
| 68 | + :port => 53, |
| 69 | + :proto => "udp" |
| 70 | + ) |
| 71 | + end |
| 72 | + |
| 73 | + get_soa(datastore['DOMAIN']).each do |r| |
| 74 | + print_good("#{datastore['DOMAIN']} - #{r[:host]} (#{r[:address]}) found. Record type: #{r[:type]}") |
| 75 | + report_host(:host => r[:address], :name => r[:host]) |
| 76 | + end |
| 77 | + |
| 78 | + get_mx(datastore['DOMAIN']).each do |r| |
| 79 | + print_good("#{datastore['DOMAIN']} - Mail server #{r[:host]} (#{r[:address]}) found. Record type: #{r[:type]}") |
| 80 | + report_host(:host => r[:address], :name => r[:host]) |
| 81 | + report_service( |
| 82 | + :host => r[:address], |
| 83 | + :name => "smtp", |
| 84 | + :port => 25, |
| 85 | + :proto => "tcp" |
| 86 | + ) |
| 87 | + end |
| 88 | + |
| 89 | + get_txt(datastore['DOMAIN']).each do |r| |
| 90 | + print_good("#{datastore['DOMAIN']} - Text info found: #{r[:text]}. Record type: #{r[:type]}") |
| 91 | + report_note( |
| 92 | + :host => datastore['DOMAIN'], |
| 93 | + :proto => 'udp', |
| 94 | + :port => 53, |
| 95 | + :type => 'dns.info', |
| 96 | + :data => {:text => r[:text]} |
| 97 | + ) |
| 98 | + end |
| 99 | + end |
| 100 | + |
| 101 | + def wildcard(target) |
| 102 | + rendsub = rand(10000).to_s |
| 103 | + query = @res.query("#{rendsub}.#{target}", "A") |
| 104 | + if query.answer.length != 0 |
| 105 | + print_status("This Domain has Wild-cards Enabled!!") |
| 106 | + query.answer.each do |rr| |
| 107 | + print_status("Wild-card IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Net::DNS::RR::CNAME |
| 108 | + report_note( |
| 109 | + :host => datastore['DOMAIN'], |
| 110 | + :proto => 'UDP', |
| 111 | + :port => 53, |
| 112 | + :type => 'dns.wildcard', |
| 113 | + :data => "Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}" |
| 114 | + ) |
| 115 | + end |
| 116 | + return true |
| 117 | + else |
| 118 | + return false |
| 119 | + end |
| 120 | + end |
| 121 | + |
| 122 | + def get_ip(host) |
| 123 | + results = [] |
| 124 | + query = @res.search(host, "A") |
| 125 | + if query |
| 126 | + query.answer.each do |rr| |
| 127 | + record = {} |
| 128 | + record[:host] = host |
| 129 | + record[:type] = "A" |
| 130 | + record[:address] = rr.address.to_s |
| 131 | + results << record |
| 132 | + end |
| 133 | + end |
| 134 | + query1 = @res.search(host, "AAAA") |
| 135 | + if query1 |
| 136 | + query1.answer.each do |rr| |
| 137 | + record = {} |
| 138 | + record[:host] = host |
| 139 | + record[:type] = "AAAA" |
| 140 | + record[:address] = rr.address.to_s |
| 141 | + results << record |
| 142 | + end |
| 143 | + end |
| 144 | + return results |
| 145 | + end |
| 146 | + |
| 147 | + def get_ns(target) |
| 148 | + results = [] |
| 149 | + query = @res.query(target, "NS") |
| 150 | + return results if not query |
| 151 | + (query.answer.select { |i| i.class == Net::DNS::RR::NS}).each do |rr| |
| 152 | + get_ip(rr.nsdname).each do |r| |
| 153 | + record = {} |
| 154 | + record[:host] = rr.nsdname.gsub(/\.$/,'') |
| 155 | + record[:type] = "NS" |
| 156 | + record[:address] = r[:address].to_s |
| 157 | + results << record |
| 158 | + end |
| 159 | + end |
| 160 | + return results |
| 161 | + end |
| 162 | + |
| 163 | + def get_soa(target) |
| 164 | + results = [] |
| 165 | + query = @res.query(target, "SOA") |
| 166 | + return results if not query |
| 167 | + (query.answer.select { |i| i.class == Net::DNS::RR::SOA}).each do |rr| |
| 168 | + if Rex::Socket.dotted_ip?(rr.mname) |
| 169 | + record = {} |
| 170 | + record[:host] = rr.mname |
| 171 | + record[:type] = "SOA" |
| 172 | + record[:address] = rr.mname |
| 173 | + results << record |
| 174 | + else |
| 175 | + get_ip(rr.mname).each do |ip| |
| 176 | + record = {} |
| 177 | + record[:host] = rr.mname.gsub(/\.$/,'') |
| 178 | + record[:type] = "SOA" |
| 179 | + record[:address] = ip[:address].to_s |
| 180 | + results << record |
| 181 | + end |
| 182 | + end |
| 183 | + end |
| 184 | + return results |
| 185 | + end |
| 186 | + |
| 187 | + def get_txt(target) |
| 188 | + results = [] |
| 189 | + query = @res.query(target, "TXT") |
| 190 | + return results if not query |
| 191 | + query.answer.each do |rr| |
| 192 | + record = {} |
| 193 | + record[:host] = target |
| 194 | + record[:text] = rr.txt |
| 195 | + record[:type] = "TXT" |
| 196 | + results << record |
| 197 | + end |
| 198 | + return results |
| 199 | + end |
| 200 | + |
| 201 | + def get_mx(target) |
| 202 | + results = [] |
| 203 | + query = @res.query(target, "MX") |
| 204 | + return results if not query |
| 205 | + (query.answer.select { |i| i.class == Net::DNS::RR::MX}).each do |rr| |
| 206 | + if Rex::Socket.dotted_ip?(rr.exchange) |
| 207 | + record = {} |
| 208 | + record[:host] = rr.exchange |
| 209 | + record[:type] = "MX" |
| 210 | + record[:address] = rr.exchange |
| 211 | + results << record |
| 212 | + else |
| 213 | + get_ip(rr.exchange).each do |ip| |
| 214 | + record = {} |
| 215 | + record[:host] = rr.exchange.gsub(/\.$/,'') |
| 216 | + record[:type] = "MX" |
| 217 | + record[:address] = ip[:address].to_s |
| 218 | + results << record |
| 219 | + end |
| 220 | + end |
| 221 | + end |
| 222 | + return results |
| 223 | + end |
| 224 | + |
| 225 | + def switchdns() |
| 226 | + print_status("Using DNS server: #{datastore['NS']}") |
| 227 | + @res.nameserver=(datastore['NS']) |
| 228 | + @nsinuse = datastore['NS'] |
| 229 | + end |
| 230 | +end |
| 231 | + |
0 commit comments