|
|
|
@ -1,483 +1,424 @@
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
|
##
|
|
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
require "net/dns/resolver"
|
|
|
|
|
require 'net/dns/resolver'
|
|
|
|
|
|
|
|
|
|
class MetasploitModule < Msf::Auxiliary
|
|
|
|
|
include Msf::Auxiliary::Report
|
|
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
|
super(update_info(info,
|
|
|
|
|
'Name' => 'DNS Record Scanner and Enumerator ',
|
|
|
|
|
'Description' => %q{
|
|
|
|
|
'Name' => 'DNS Record Scanner and Enumerator',
|
|
|
|
|
'Description' => %q(
|
|
|
|
|
This module can be used to gather information about a domain from a
|
|
|
|
|
given DNS server by performing various DNS queries such as zone
|
|
|
|
|
transfers, reverse lookups, SRV record bruteforcing, and other techniques.
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>' ],
|
|
|
|
|
),
|
|
|
|
|
'Author' => [
|
|
|
|
|
'Carlos Perez <carlos_perez[at]darkoperator.com>',
|
|
|
|
|
'Nixawk'
|
|
|
|
|
],
|
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
|
'References' =>
|
|
|
|
|
[
|
|
|
|
|
'References' => [
|
|
|
|
|
['CVE', '1999-0532'],
|
|
|
|
|
['OSVDB', '492'],
|
|
|
|
|
]
|
|
|
|
|
))
|
|
|
|
|
['OSVDB', '492']
|
|
|
|
|
]))
|
|
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
|
[
|
|
|
|
|
OptString.new('DOMAIN', [ true, "The target domain name"]),
|
|
|
|
|
OptBool.new('ENUM_AXFR', [ true, 'Initiate a zone transfer against each NS record', true]),
|
|
|
|
|
OptBool.new('ENUM_TLD', [ true, 'Perform a TLD expansion by replacing the TLD with the IANA TLD list', false]),
|
|
|
|
|
OptBool.new('ENUM_STD', [ true, 'Enumerate standard record types (A,MX,NS,TXT and SOA)', true]),
|
|
|
|
|
OptBool.new('ENUM_BRT', [ true, 'Brute force subdomains and hostnames via the supplied wordlist', false]),
|
|
|
|
|
OptBool.new('ENUM_IP6', [ true, 'Brute force hosts with IPv6 AAAA records',false]),
|
|
|
|
|
OptString.new('DOMAIN', [true, 'The target domain']),
|
|
|
|
|
OptBool.new('ENUM_AXFR', [true, 'Initiate a zone transfer against each NS record', true]),
|
|
|
|
|
OptBool.new('ENUM_BRT', [true, 'Brute force subdomains and hostnames via the supplied wordlist', false]),
|
|
|
|
|
OptBool.new('ENUM_A', [true, 'Enumerate DNS A record', true]),
|
|
|
|
|
OptBool.new('ENUM_CNAME', [true, 'Enumerate DNS CNAME record', true]),
|
|
|
|
|
OptBool.new('ENUM_MX', [true, 'Enumerate DNS MX record', true]),
|
|
|
|
|
OptBool.new('ENUM_NS', [true, 'Enumerate DNS NS record', true]),
|
|
|
|
|
OptBool.new('ENUM_SOA', [true, 'Enumerate DNS SOA record', true]),
|
|
|
|
|
OptBool.new('ENUM_TXT', [true, 'Enumerate DNS TXT record', true]),
|
|
|
|
|
OptBool.new('ENUM_RVL', [ true, 'Reverse lookup a range of IP addresses', false]),
|
|
|
|
|
OptBool.new('ENUM_SRV', [ true, 'Enumerate the most common SRV records', true]),
|
|
|
|
|
OptPath.new('WORDLIST', [ false, "Wordlist for domain name bruteforcing", ::File.join(Msf::Config.data_directory, "wordlists", "namelist.txt")]),
|
|
|
|
|
OptAddress.new('NS', [ false, "Specify the nameserver to use for queries (default is system DNS)" ]),
|
|
|
|
|
OptBool.new('ENUM_TLD', [true, 'Perform a TLD expansion by replacing the TLD with the IANA TLD list', false]),
|
|
|
|
|
OptBool.new('ENUM_SRV', [true, 'Enumerate the most common SRV records', true]),
|
|
|
|
|
OptBool.new('STOP_WLDCRD', [true, 'Stops bruteforce enumeration if wildcard resolution is detected', false]),
|
|
|
|
|
OptAddress.new('NS', [false, 'Specify the nameserver to use for queries (default is system DNS)']),
|
|
|
|
|
OptAddressRange.new('IPRANGE', [false, "The target address range or CIDR identifier"]),
|
|
|
|
|
OptBool.new('STOP_WLDCRD', [ true, 'Stops bruteforce enumeration if wildcard resolution is detected', false])
|
|
|
|
|
OptInt.new('THREADS', [false, 'Threads for ENUM_BRT', 1]),
|
|
|
|
|
OptPath.new('WORDLIST', [false, 'Wordlist of subdomains', ::File.join(Msf::Config.data_directory, 'wordlists', 'namelist.txt')])
|
|
|
|
|
], self.class)
|
|
|
|
|
|
|
|
|
|
register_advanced_options(
|
|
|
|
|
[
|
|
|
|
|
OptInt.new('RETRY', [ false, "Number of times to try to resolve a record if no response is received", 2]),
|
|
|
|
|
OptInt.new('RETRY_INTERVAL', [ false, "Number of seconds to wait before doing a retry", 2]),
|
|
|
|
|
OptBool.new('TCP_DNS', [false, "Run queries over TCP", false]),
|
|
|
|
|
OptInt.new('TIMEOUT', [false, 'DNS TIMEOUT', 8]),
|
|
|
|
|
OptInt.new('RETRY', [false, 'Number of times to try to resolve a record if no response is received', 2]),
|
|
|
|
|
OptInt.new('RETRY_INTERVAL', [false, 'Number of seconds to wait before doing a retry', 2]),
|
|
|
|
|
OptBool.new('TCP_DNS', [false, 'Run queries over TCP', false])
|
|
|
|
|
], self.class)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def switchdns(target)
|
|
|
|
|
if not datastore['NS'].nil?
|
|
|
|
|
print_status("Using DNS Server: #{datastore['NS']}")
|
|
|
|
|
@res.nameserver=(datastore['NS'])
|
|
|
|
|
@nsinuse = datastore['NS']
|
|
|
|
|
else
|
|
|
|
|
querysoa = @res.query(target, "SOA")
|
|
|
|
|
if (querysoa)
|
|
|
|
|
(querysoa.answer.select { |i| i.class == Net::DNS::RR::SOA}).each do |rr|
|
|
|
|
|
query1soa = @res.search(rr.mname)
|
|
|
|
|
if (query1soa and query1soa.answer[0])
|
|
|
|
|
print_status("Setting DNS Server to #{target} NS: #{query1soa.answer[0].address}")
|
|
|
|
|
@res.nameserver=(query1soa.answer[0].address)
|
|
|
|
|
@nsinuse = query1soa.answer[0].address
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def wildcard(target)
|
|
|
|
|
rendsub = rand(10000).to_s
|
|
|
|
|
query = @res.query("#{rendsub}.#{target}", "A")
|
|
|
|
|
if query.answer.length != 0
|
|
|
|
|
print_status("This domain has wildcards enabled!!")
|
|
|
|
|
query.answer.each do |rr|
|
|
|
|
|
print_status("Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Net::DNS::RR::CNAME
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
else
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def genrcd(target)
|
|
|
|
|
print_status("Retrieving general DNS records")
|
|
|
|
|
query = @res.search(target)
|
|
|
|
|
if (query)
|
|
|
|
|
query.answer.each do |rr|
|
|
|
|
|
next unless rr.class == Net::DNS::RR::A
|
|
|
|
|
print_status("Domain: #{target} IP address: #{rr.address} Record: A ")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{rr.address.to_s},#{target},A")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
query = @res.query(target, "SOA")
|
|
|
|
|
if (query)
|
|
|
|
|
(query.answer.select { |i| i.class == Net::DNS::RR::SOA}).each do |rr|
|
|
|
|
|
query1 = @res.search(rr.mname)
|
|
|
|
|
if (query1)
|
|
|
|
|
query1.answer.each do |ip|
|
|
|
|
|
print_status("Start of Authority: #{rr.mname} IP address: #{ip.address} Record: SOA")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{ip.address.to_s},#{rr.mname},SOA")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
query = @res.query(target, "NS")
|
|
|
|
|
if (query)
|
|
|
|
|
(query.answer.select { |i| i.class == Net::DNS::RR::NS}).each do |rr|
|
|
|
|
|
query1 = @res.search(rr.nsdname)
|
|
|
|
|
if (query1)
|
|
|
|
|
query1.answer.each do |ip|
|
|
|
|
|
next unless ip.class == Net::DNS::RR::A
|
|
|
|
|
print_status("Name Server: #{rr.nsdname} IP address: #{ip.address} Record: NS")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{ip.address.to_s},#{rr.nsdname},NS")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
query = @res.query(target, "MX")
|
|
|
|
|
if (query)
|
|
|
|
|
(query.answer.select { |i| i.class == Net::DNS::RR::MX}).each do |rr|
|
|
|
|
|
print_status("Name: #{rr.exchange} Preference: #{rr.preference} Record: MX")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{rr.exchange},MX")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
query = @res.query(target, "TXT")
|
|
|
|
|
if (query)
|
|
|
|
|
query.answer.each do |rr|
|
|
|
|
|
print_status(rr.inspect)
|
|
|
|
|
print_status("Text: #{rr.inspect}")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => rr.inspect)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def tldexpnd(targetdom,nssrv)
|
|
|
|
|
target = targetdom.scan(/(\S*)[.]\w*\z/).join
|
|
|
|
|
target.chomp!
|
|
|
|
|
if not nssrv.nil?
|
|
|
|
|
@res.nameserver=(nssrv)
|
|
|
|
|
@nsinuse = nssrv
|
|
|
|
|
end
|
|
|
|
|
i, a = 0, []
|
|
|
|
|
tlds = [
|
|
|
|
|
"com", "org", "net", "edu", "mil", "gov", "uk", "af", "al", "dz",
|
|
|
|
|
"as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "ac", "au",
|
|
|
|
|
"at", "az", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm",
|
|
|
|
|
"bt", "bo", "ba", "bw", "bv", "br", "io", "bn", "bg", "bf", "bi",
|
|
|
|
|
"kh", "cm", "ca", "cv", "ky", "cf", "td", "cl", "cn", "cx", "cc",
|
|
|
|
|
"co", "km", "cd", "cg", "ck", "cr", "ci", "hr", "cu", "cy", "cz",
|
|
|
|
|
"dk", "dj", "dm", "do", "tp", "ec", "eg", "sv", "gq", "er", "ee",
|
|
|
|
|
"et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm",
|
|
|
|
|
"ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gg",
|
|
|
|
|
"gn", "gw", "gy", "ht", "hm", "va", "hn", "hk", "hu", "is", "in",
|
|
|
|
|
"id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo",
|
|
|
|
|
"kz", "ke", "ki", "kp", "kr", "kw", "kg", "la", "lv", "lb", "ls",
|
|
|
|
|
"lr", "ly", "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv",
|
|
|
|
|
"ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc",
|
|
|
|
|
"mn", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "an", "nc",
|
|
|
|
|
"nz", "ni", "ne", "ng", "nu", "nf", "mp", "no", "om", "pk", "pw",
|
|
|
|
|
"pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re",
|
|
|
|
|
"ro", "ru", "rw", "kn", "lc", "vc", "ws", "sm", "st", "sa", "sn",
|
|
|
|
|
"sc", "sl", "sg", "sk", "si", "sb", "so", "za", "gz", "es", "lk",
|
|
|
|
|
"sh", "pm", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj",
|
|
|
|
|
"tz", "th", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv",
|
|
|
|
|
"ug", "ua", "ae", "gb", "us", "um", "uy", "uz", "vu", "ve", "vn",
|
|
|
|
|
"vg", "vi", "wf", "eh", "ye", "yu", "za", "zr", "zm", "zw", "int",
|
|
|
|
|
"gs", "info", "biz", "su", "name", "coop", "aero" ]
|
|
|
|
|
print_status("Performing Top Level Domain expansion using #{tlds.size} TLDs")
|
|
|
|
|
|
|
|
|
|
tlds.each do |tld|
|
|
|
|
|
query1 = @res.search("#{target}.#{tld}")
|
|
|
|
|
if (query1)
|
|
|
|
|
query1.answer.each do |rr|
|
|
|
|
|
print_status("Domain: #{target}.#{tld} Name: #{rr.name} IP address: #{rr.address} Record: A ") if rr.class == Net::DNS::RR::A
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{rr.address.to_s},#{target}.#{tld},A") if rr.class == Net::DNS::RR::A
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def dnsbrute(target, wordlist, nssrv)
|
|
|
|
|
print_status("Running bruteforce against domain #{target}")
|
|
|
|
|
arr = []
|
|
|
|
|
i, a = 0, []
|
|
|
|
|
::File.open(wordlist, "rb").each_line do |line|
|
|
|
|
|
if not nssrv.nil?
|
|
|
|
|
@res.nameserver=(nssrv)
|
|
|
|
|
@nsinuse = nssrv
|
|
|
|
|
end
|
|
|
|
|
query1 = @res.search("#{line.chomp}.#{target}")
|
|
|
|
|
if (query1)
|
|
|
|
|
query1.answer.each do |rr|
|
|
|
|
|
if rr.class == Net::DNS::RR::A
|
|
|
|
|
print_status("Hostname: #{line.chomp}.#{target} IP address: #{rr.address.to_s}")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{rr.address.to_s},#{line.chomp}.#{target},A")
|
|
|
|
|
next unless rr.class == Net::DNS::RR::CNAME
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def bruteipv6(target, wordlist, nssrv)
|
|
|
|
|
print_status("Bruteforcing IPv6 addresses against domain #{target}")
|
|
|
|
|
arr = []
|
|
|
|
|
i, a = 0, []
|
|
|
|
|
arr = IO.readlines(wordlist)
|
|
|
|
|
if not nssrv.nil?
|
|
|
|
|
@res.nameserver=(nssrv)
|
|
|
|
|
@nsinuse = nssrv
|
|
|
|
|
end
|
|
|
|
|
arr.each do |line|
|
|
|
|
|
query1 = @res.search("#{line.chomp}.#{target}", "AAAA")
|
|
|
|
|
if (query1)
|
|
|
|
|
query1.answer.each do |rr|
|
|
|
|
|
if rr.class == Net::DNS::RR::AAAA
|
|
|
|
|
print_status("Hostname: #{line.chomp}.#{target} IPv6 Address: #{rr.address.to_s}")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{rr.address.to_s},#{line.chomp}.#{target},AAAA")
|
|
|
|
|
next unless rr.class == Net::DNS::RR::CNAME
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def reverselkp(iprange,nssrv)
|
|
|
|
|
print_status("Running reverse lookup against IP range #{iprange}")
|
|
|
|
|
if not nssrv.nil?
|
|
|
|
|
@res.nameserver = (nssrv)
|
|
|
|
|
@nsinuse = nssrv
|
|
|
|
|
end
|
|
|
|
|
ar = Rex::Socket::RangeWalker.new(iprange)
|
|
|
|
|
tl = []
|
|
|
|
|
while (true)
|
|
|
|
|
# Spawn threads for each host
|
|
|
|
|
while (tl.length < @threadnum)
|
|
|
|
|
ip = ar.next_ip
|
|
|
|
|
break if not ip
|
|
|
|
|
tl << framework.threads.spawn("Module(#{self.refname})-#{ip}", false, ip.dup) do |tip|
|
|
|
|
|
begin
|
|
|
|
|
query = @res.query(tip)
|
|
|
|
|
raise ::Rex::ConnectionError
|
|
|
|
|
query.each_ptr do |addresstp|
|
|
|
|
|
print_status("Hostname: #{addresstp} IP address: #{tip.to_s}")
|
|
|
|
|
report_note(:host => @nsinuse.to_s,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => "#{addresstp},#{tip},A")
|
|
|
|
|
end
|
|
|
|
|
rescue ::Interrupt
|
|
|
|
|
raise $!
|
|
|
|
|
rescue ::Rex::ConnectionError
|
|
|
|
|
rescue ::Exception => e
|
|
|
|
|
print_error("Error: #{tip}: #{e.message}")
|
|
|
|
|
elog("Error running against host #{tip}: #{e.message}\n#{e.backtrace.join("\n")}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
# Exit once we run out of hosts
|
|
|
|
|
if(tl.length == 0)
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
tl.first.join
|
|
|
|
|
tl.delete_if { |t| not t.alive? }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# SRV Record Enumeration
|
|
|
|
|
def srvqry(dom,nssrv)
|
|
|
|
|
print_status("Enumerating SRV records for #{dom}")
|
|
|
|
|
i, a = 0, []
|
|
|
|
|
# Most common SRV Records
|
|
|
|
|
srvrcd = [
|
|
|
|
|
"_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp.","_test._tcp.",
|
|
|
|
|
"_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.",
|
|
|
|
|
"_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp.","_h323cs._tcp.",
|
|
|
|
|
"_h323cs._udp.","_h323be._tcp.","_h323be._udp.","_h323ls._tcp.","_h323ls._udp.",
|
|
|
|
|
"_sipinternal._tcp.","_sipinternaltls._tcp.","_sip._tls.","_sipfederationtls._tcp.",
|
|
|
|
|
"_jabber._tcp.","_xmpp-server._tcp.","_xmpp-client._tcp.","_imap._tcp.","_certificates._tcp.",
|
|
|
|
|
"_crls._tcp.","_pgpkeys._tcp.","_pgprevokations._tcp.","_cmp._tcp.","_svcp._tcp.","_crl._tcp.",
|
|
|
|
|
"_ocsp._tcp.","_PKIXREP._tcp.","_smtp._tcp.","_hkp._tcp.","_hkps._tcp.","_jabber._udp.",
|
|
|
|
|
"_xmpp-server._udp.","_xmpp-client._udp.","_jabber-client._tcp.","_jabber-client._udp."]
|
|
|
|
|
srvrcd.each do |srvt|
|
|
|
|
|
trg = "#{srvt}#{dom}"
|
|
|
|
|
query = @res.query(trg , Net::DNS::SRV)
|
|
|
|
|
next unless query
|
|
|
|
|
query.answer.each do |srv|
|
|
|
|
|
next if srv.type == "CNAME"
|
|
|
|
|
print_status("SRV Record: #{trg} Host: #{srv.host} Port: #{srv.port} Priority: #{srv.priority}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
print_status("Done")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# For Performing Zone Transfers
|
|
|
|
|
def axfr(target, nssrv)
|
|
|
|
|
print_status("Performing zone transfer against all nameservers in #{target}")
|
|
|
|
|
if not nssrv.nil?
|
|
|
|
|
@res.nameserver=(nssrv)
|
|
|
|
|
@nsinuse = nssrv
|
|
|
|
|
end
|
|
|
|
|
@res.tcp_timeout=15
|
|
|
|
|
query = @res.query(target, "NS")
|
|
|
|
|
if query && query.answer.length != 0
|
|
|
|
|
(query.answer.select { |i| i.class == Net::DNS::RR::NS}).each do |nsrcd|
|
|
|
|
|
print_status("Testing nameserver: #{nsrcd.nsdname}")
|
|
|
|
|
nssrvquery = @res.query(nsrcd.nsdname, "A")
|
|
|
|
|
if nssrvquery.answer.length == 0
|
|
|
|
|
nssrvip = Rex::Socket.gethostbyname(nsrcd.nsdname)[3].bytes.reduce {|a,b| [a,b].join(".")}
|
|
|
|
|
else
|
|
|
|
|
nssrvip = nssrvquery.answer[0].address.to_s
|
|
|
|
|
end
|
|
|
|
|
begin
|
|
|
|
|
@res.nameserver=(nssrvip)
|
|
|
|
|
@nsinuse = nssrvip
|
|
|
|
|
zone = []
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
zone = @res.axfr(target)
|
|
|
|
|
rescue ::NoResponseError
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if zone.length != 0
|
|
|
|
|
print_status("Zone transfer successful")
|
|
|
|
|
report_note(:host => nssrvip,
|
|
|
|
|
:proto => 'udp',
|
|
|
|
|
:sname => 'dns',
|
|
|
|
|
:port => 53 ,
|
|
|
|
|
:type => 'dns.enum',
|
|
|
|
|
:update => :unique_data,
|
|
|
|
|
:data => zone)
|
|
|
|
|
# Prints each record according to its type
|
|
|
|
|
zone.each do |response|
|
|
|
|
|
response.answer.each do |rr|
|
|
|
|
|
begin
|
|
|
|
|
case rr.type
|
|
|
|
|
when "A"
|
|
|
|
|
print_status("Name: #{rr.name} IP address: #{rr.address} Record: A ")
|
|
|
|
|
when "SOA"
|
|
|
|
|
print_status("Name: #{rr.mname} Record: SOA")
|
|
|
|
|
when "MX"
|
|
|
|
|
print_status("Name: #{rr.exchange} Preference: #{rr.preference} Record: MX")
|
|
|
|
|
when "CNAME"
|
|
|
|
|
print_status("Name: #{rr.cname} Record: CNAME")
|
|
|
|
|
when "HINFO"
|
|
|
|
|
print_status("CPU: #{rr.cpu} OS: #{rr.os} Record: HINFO")
|
|
|
|
|
when "AAAA"
|
|
|
|
|
print_status("IPv6 Address: #{rr.address} Record: AAAA")
|
|
|
|
|
when "NS"
|
|
|
|
|
print_status("Name: #{rr.nsdname} Record: NS")
|
|
|
|
|
when "TXT"
|
|
|
|
|
print_status("Text: #{rr.inspect}")
|
|
|
|
|
when "SRV"
|
|
|
|
|
print_status("Host: #{rr.host} Port: #{rr.port} Priority: #{rr.priority} Record: SRV")
|
|
|
|
|
end
|
|
|
|
|
rescue ActiveRecord::RecordInvalid
|
|
|
|
|
# Do nothing. Probably tried to store :host => 127.0.0.1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
print_error("Zone transfer failed (length was zero)")
|
|
|
|
|
end
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
print_error("Error executing zone transfer: #{e.message}")
|
|
|
|
|
elog("Error executing zone transfer: #{e.message}\n#{e.backtrace.join("\n")}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
print_error("Could not resolve domain #{target}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def run
|
|
|
|
|
@res = Net::DNS::Resolver.new()
|
|
|
|
|
if datastore['TCP_DNS']
|
|
|
|
|
vprint_status("Using DNS/TCP")
|
|
|
|
|
@res.use_tcp = true
|
|
|
|
|
end
|
|
|
|
|
@res.retry = datastore['RETRY'].to_i
|
|
|
|
|
@res.retry_interval = datastore['RETRY_INTERVAL'].to_i
|
|
|
|
|
@threadnum = datastore['THREADS'].to_i
|
|
|
|
|
wldcrd = wildcard(datastore['DOMAIN'])
|
|
|
|
|
switchdns(datastore['DOMAIN'])
|
|
|
|
|
domain = datastore['DOMAIN']
|
|
|
|
|
is_wildcard = dns_wildcard_enabled?(domain)
|
|
|
|
|
|
|
|
|
|
if(datastore['ENUM_STD'])
|
|
|
|
|
genrcd(datastore['DOMAIN'])
|
|
|
|
|
end
|
|
|
|
|
axfr(domain) if datastore['ENUM_AXFR']
|
|
|
|
|
get_a(domain) if datastore['ENUM_A']
|
|
|
|
|
get_cname(domain) if datastore['ENUM_CNAME']
|
|
|
|
|
get_ns(domain) if datastore['ENUM_NS']
|
|
|
|
|
get_mx(domain) if datastore['ENUM_MX']
|
|
|
|
|
get_soa(domain) if datastore['ENUM_SOA']
|
|
|
|
|
get_txt(domain) if datastore['ENUM_TXT']
|
|
|
|
|
get_tld(domain) if datastore['ENUM_TLD']
|
|
|
|
|
get_srv(domain) if datastore['ENUM_SRV']
|
|
|
|
|
threads = datastore['THREADS']
|
|
|
|
|
dns_reverse(datastore['IPRANGE'], threads) if datastore['ENUM_RVL']
|
|
|
|
|
|
|
|
|
|
if(datastore['ENUM_TLD'])
|
|
|
|
|
tldexpnd(datastore['DOMAIN'],datastore['NS'])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if(datastore['ENUM_BRT'])
|
|
|
|
|
if wldcrd and datastore['STOP_WLDCRD']
|
|
|
|
|
print_error("Wildcard record found!")
|
|
|
|
|
return unless datastore['ENUM_BRT']
|
|
|
|
|
if is_wildcard
|
|
|
|
|
dns_bruteforce(domain, threads) unless datastore['STOP_WLDCRD']
|
|
|
|
|
else
|
|
|
|
|
dnsbrute(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS'])
|
|
|
|
|
dns_bruteforce(domain, threads)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if(datastore['ENUM_IP6'])
|
|
|
|
|
if wldcrd and datastore['STOP_WLDCRD']
|
|
|
|
|
print_status("Wildcard Record Found!")
|
|
|
|
|
def dns_query(domain, type)
|
|
|
|
|
begin
|
|
|
|
|
nameserver = datastore['NS']
|
|
|
|
|
if nameserver.blank?
|
|
|
|
|
dns = Net::DNS::Resolver.new
|
|
|
|
|
else
|
|
|
|
|
bruteipv6(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS'])
|
|
|
|
|
dns = Net::DNS::Resolver.new(nameservers: ::Rex::Socket.resolv_to_dotted(nameserver))
|
|
|
|
|
end
|
|
|
|
|
dns.use_tcp = datastore['TCP_DNS']
|
|
|
|
|
dns.udp_timeout = datastore['TIMEOUT']
|
|
|
|
|
dns.retry_number = datastore['RETRY']
|
|
|
|
|
dns.retry_interval = datastore['RETRY_INTERVAL']
|
|
|
|
|
dns.query(domain, type)
|
|
|
|
|
rescue ResolverArgumentError, Errno::ETIMEDOUT, ::NoResponseError, ::Timeout::Error => e
|
|
|
|
|
print_error("Query #{domain} DNS #{type} - exception: #{e}")
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if(datastore['ENUM_AXFR'])
|
|
|
|
|
axfr(datastore['DOMAIN'],datastore['NS'])
|
|
|
|
|
def dns_bruteforce(domain, threads)
|
|
|
|
|
wordlist = datastore['WORDLIST']
|
|
|
|
|
return if wordlist.blank?
|
|
|
|
|
threads = 1 if threads <= 0
|
|
|
|
|
|
|
|
|
|
queue = []
|
|
|
|
|
File.foreach(wordlist) do |line|
|
|
|
|
|
queue << "#{line.chomp}.#{domain}"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if(datastore['ENUM_SRV'])
|
|
|
|
|
srvqry(datastore['DOMAIN'],datastore['NS'])
|
|
|
|
|
records = []
|
|
|
|
|
until queue.empty?
|
|
|
|
|
t = []
|
|
|
|
|
threads = 1 if threads <= 0
|
|
|
|
|
|
|
|
|
|
if queue.length < threads
|
|
|
|
|
# work around issue where threads not created as the queue isn't large enough
|
|
|
|
|
threads = queue.length
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if(datastore['ENUM_RVL'] and datastore['IPRANGE'] and not datastore['IPRANGE'].empty?)
|
|
|
|
|
reverselkp(datastore['IPRANGE'],datastore['NS'])
|
|
|
|
|
begin
|
|
|
|
|
1.upto(threads) do
|
|
|
|
|
t << framework.threads.spawn("Module(#{self.refname})", false, queue.shift) do |test_current|
|
|
|
|
|
Thread.current.kill unless test_current
|
|
|
|
|
a = get_a(test_current, 'dns_bruteforce')
|
|
|
|
|
records |= a if a
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
t.map{ |x| x.join }
|
|
|
|
|
|
|
|
|
|
rescue ::Timeout::Error
|
|
|
|
|
ensure
|
|
|
|
|
t.each { |x| x.kill rescue nil }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def dns_reverse(cidr, threads)
|
|
|
|
|
iplst = []
|
|
|
|
|
ipadd = Rex::Socket::RangeWalker.new(cidr)
|
|
|
|
|
numip = ipadd.num_ips
|
|
|
|
|
while iplst.length < numip
|
|
|
|
|
ipa = ipadd.next_ip
|
|
|
|
|
break unless ipa
|
|
|
|
|
iplst << ipa
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
while !iplst.nil? && !iplst.empty?
|
|
|
|
|
t = []
|
|
|
|
|
threads = 1 if threads <= 0
|
|
|
|
|
begin
|
|
|
|
|
1.upto(threads) do
|
|
|
|
|
t << framework.threads.spawn("Module(#{self.refname})", false, iplst.shift) do |ip_text|
|
|
|
|
|
next if ip_text.nil?
|
|
|
|
|
a = get_ptr(ip_text)
|
|
|
|
|
records |= a if a
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
t.map { |x| x.join }
|
|
|
|
|
|
|
|
|
|
rescue ::Timeout::Error
|
|
|
|
|
ensure
|
|
|
|
|
t.each { |x| x.kill rescue nil }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def dns_wildcard_enabled?(domain)
|
|
|
|
|
records = get_a("#{Rex::Text.rand_text_alpha(16)}.#{domain}", 'dns_wildcard')
|
|
|
|
|
if records.blank?
|
|
|
|
|
false
|
|
|
|
|
else
|
|
|
|
|
print_warning('dns wildcard is enable OR fake dns server')
|
|
|
|
|
true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_ptr(ip)
|
|
|
|
|
resp = dns_query(ip, nil)
|
|
|
|
|
return if resp.blank? || resp.answer.blank?
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next unless r.class == Net::DNS::RR::PTR
|
|
|
|
|
records << "#{r.ptr}"
|
|
|
|
|
print_good("#{ip}: PTR: #{r.ptr} ")
|
|
|
|
|
end
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(ip, 'get_ptr', records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_a(domain, type='get_a')
|
|
|
|
|
resp = dns_query(domain, 'A')
|
|
|
|
|
return if resp.blank? || resp.answer.blank?
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next unless r.class == Net::DNS::RR::A
|
|
|
|
|
records << "#{r.address}"
|
|
|
|
|
print_good("#{domain} A: #{r.address} ") if datastore['ENUM_BRT']
|
|
|
|
|
end
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(domain, type, records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_cname(domain)
|
|
|
|
|
print_status("querying DNS CNAME records for #{domain}")
|
|
|
|
|
resp = dns_query(domain, 'CNAME')
|
|
|
|
|
return if resp.blank? || resp.answer.blank?
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next unless r.class == Net::DNS::RR::CNAME
|
|
|
|
|
records << r.cname
|
|
|
|
|
print_good("#{domain} CNAME: #{r.cname}")
|
|
|
|
|
end
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(domain, 'get_cname', records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_ns(domain)
|
|
|
|
|
print_status("querying DNS NS records for #{domain}")
|
|
|
|
|
resp = dns_query(domain, 'NS')
|
|
|
|
|
return if resp.blank? || resp.answer.blank?
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next unless r.class == Net::DNS::RR::NS
|
|
|
|
|
records << "#{r.nsdname}"
|
|
|
|
|
print_good("#{domain} NS: #{r.nsdname}")
|
|
|
|
|
end
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(domain, 'get_ns', records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_mx(domain)
|
|
|
|
|
print_status("querying DNS MX records for #{domain}")
|
|
|
|
|
begin
|
|
|
|
|
resp = dns_query(domain, 'MX')
|
|
|
|
|
return if resp.blank? || resp.answer.blank?
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next unless r.class == Net::DNS::RR::MX
|
|
|
|
|
records << "#{r.exchange}"
|
|
|
|
|
print_good("#{domain} MX: #{r.exchange}")
|
|
|
|
|
end
|
|
|
|
|
rescue SocketError => e
|
|
|
|
|
print_error("Query #{domain} DNS MX - exception: #{e}")
|
|
|
|
|
ensure
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(domain, 'get_mx', records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_soa(domain)
|
|
|
|
|
print_status("querying DNS SOA records for #{domain}")
|
|
|
|
|
resp = dns_query(domain, 'SOA')
|
|
|
|
|
return if resp.blank? || resp.answer.blank?
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next unless r.class == Net::DNS::RR::SOA
|
|
|
|
|
records << r.mname
|
|
|
|
|
print_good("#{domain} SOA: #{r.mname}")
|
|
|
|
|
end
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(domain, 'get_soa', records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_txt(domain)
|
|
|
|
|
print_status("querying DNS TXT records for #{domain}")
|
|
|
|
|
resp = dns_query(domain, 'TXT')
|
|
|
|
|
return if resp.blank? || resp.answer.blank?
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next unless r.class == Net::DNS::RR::TXT
|
|
|
|
|
records << r.txt
|
|
|
|
|
print_good("#{domain} TXT: #{r.txt}")
|
|
|
|
|
end
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(domain, 'get_txt', records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_tld(domain)
|
|
|
|
|
begin
|
|
|
|
|
print_status("querying DNS TLD records for #{domain}")
|
|
|
|
|
domain_ = domain.split('.')
|
|
|
|
|
domain_.pop
|
|
|
|
|
domain_ = domain_.join('.')
|
|
|
|
|
|
|
|
|
|
tlds = [
|
|
|
|
|
'com', 'org', 'net', 'edu', 'mil', 'gov', 'uk', 'af', 'al', 'dz',
|
|
|
|
|
'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'ac', 'au',
|
|
|
|
|
'at', 'az', 'bs', 'bh', 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm',
|
|
|
|
|
'bt', 'bo', 'ba', 'bw', 'bv', 'br', 'io', 'bn', 'bg', 'bf', 'bi',
|
|
|
|
|
'kh', 'cm', 'ca', 'cv', 'ky', 'cf', 'td', 'cl', 'cn', 'cx', 'cc',
|
|
|
|
|
'co', 'km', 'cd', 'cg', 'ck', 'cr', 'ci', 'hr', 'cu', 'cy', 'cz',
|
|
|
|
|
'dk', 'dj', 'dm', 'do', 'tp', 'ec', 'eg', 'sv', 'gq', 'er', 'ee',
|
|
|
|
|
'et', 'fk', 'fo', 'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm',
|
|
|
|
|
'ge', 'de', 'gh', 'gi', 'gr', 'gl', 'gd', 'gp', 'gu', 'gt', 'gg',
|
|
|
|
|
'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is', 'in',
|
|
|
|
|
'id', 'ir', 'iq', 'ie', 'im', 'il', 'it', 'jm', 'jp', 'je', 'jo',
|
|
|
|
|
'kz', 'ke', 'ki', 'kp', 'kr', 'kw', 'kg', 'la', 'lv', 'lb', 'ls',
|
|
|
|
|
'lr', 'ly', 'li', 'lt', 'lu', 'mo', 'mk', 'mg', 'mw', 'my', 'mv',
|
|
|
|
|
'ml', 'mt', 'mh', 'mq', 'mr', 'mu', 'yt', 'mx', 'fm', 'md', 'mc',
|
|
|
|
|
'mn', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np', 'nl', 'an', 'nc',
|
|
|
|
|
'nz', 'ni', 'ne', 'ng', 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw',
|
|
|
|
|
'pa', 'pg', 'py', 'pe', 'ph', 'pn', 'pl', 'pt', 'pr', 'qa', 're',
|
|
|
|
|
'ro', 'ru', 'rw', 'kn', 'lc', 'vc', 'ws', 'sm', 'st', 'sa', 'sn',
|
|
|
|
|
'sc', 'sl', 'sg', 'sk', 'si', 'sb', 'so', 'za', 'gz', 'es', 'lk',
|
|
|
|
|
'sh', 'pm', 'sd', 'sr', 'sj', 'sz', 'se', 'ch', 'sy', 'tw', 'tj',
|
|
|
|
|
'tz', 'th', 'tg', 'tk', 'to', 'tt', 'tn', 'tr', 'tm', 'tc', 'tv',
|
|
|
|
|
'ug', 'ua', 'ae', 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn',
|
|
|
|
|
'vg', 'vi', 'wf', 'eh', 'ye', 'yu', 'za', 'zr', 'zm', 'zw', 'int',
|
|
|
|
|
'gs', 'info', 'biz', 'su', 'name', 'coop', 'aero']
|
|
|
|
|
|
|
|
|
|
records = []
|
|
|
|
|
tlds.each do |tld|
|
|
|
|
|
tldr = get_a("#{domain_}.#{tld}", 'get_tld')
|
|
|
|
|
next if tldr.blank?
|
|
|
|
|
records |= tldr
|
|
|
|
|
print_good("#{domain_}.#{tld}: TLD: #{tldr.join(',')}")
|
|
|
|
|
end
|
|
|
|
|
rescue ArgumentError => e
|
|
|
|
|
print_error("Query #{domain} DNS TLD - exception: #{e}")
|
|
|
|
|
ensure
|
|
|
|
|
return if records.blank?
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_srv(domain)
|
|
|
|
|
print_status("querying DNS SRV records for #{domain}")
|
|
|
|
|
srv_protos = %w(tcp udp tls)
|
|
|
|
|
srv_record_types = %w(gc kerberos ldap test sips sip aix finger ftp http
|
|
|
|
|
nntp telnet whois h323cs h323be h323ls sipinternal sipinternaltls
|
|
|
|
|
sipfederationtls jabber jabber-client jabber-server xmpp-server xmpp-client
|
|
|
|
|
imap certificates crls pgpkeys pgprevokations cmp svcp crl oscp pkixrep
|
|
|
|
|
smtp hkp hkps)
|
|
|
|
|
|
|
|
|
|
srv_records_data = []
|
|
|
|
|
srv_record_types.each do |srv_record_type|
|
|
|
|
|
srv_protos.each do |srv_proto|
|
|
|
|
|
srv_record = "_#{srv_record_type}._#{srv_proto}.#{domain}"
|
|
|
|
|
resp = dns_query(srv_record, Net::DNS::SRV)
|
|
|
|
|
next if resp.blank? || resp.answer.blank?
|
|
|
|
|
srv_record_data = []
|
|
|
|
|
resp.answer.each do |r|
|
|
|
|
|
next if r.type == Net::DNS::RR::CNAME
|
|
|
|
|
host = r.host.gsub(/\.$/, '')
|
|
|
|
|
data = {
|
|
|
|
|
host: host,
|
|
|
|
|
port: r.port,
|
|
|
|
|
priority: r.priority
|
|
|
|
|
}
|
|
|
|
|
print_good("#{srv_record} SRV: #{data}")
|
|
|
|
|
srv_record_data << data
|
|
|
|
|
end
|
|
|
|
|
srv_records_data << {
|
|
|
|
|
"#{srv_record}" => srv_record_data
|
|
|
|
|
}
|
|
|
|
|
report_note(
|
|
|
|
|
type: srv_record,
|
|
|
|
|
data: srv_record_data
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return if srv_records_data.empty?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def axfr(domain)
|
|
|
|
|
nameservers = get_ns(domain)
|
|
|
|
|
return if nameservers.blank?
|
|
|
|
|
records = []
|
|
|
|
|
nameservers.each do |nameserver|
|
|
|
|
|
next if nameserver.blank?
|
|
|
|
|
print_status("Attempting DNS AXFR for #{domain} from #{nameserver}")
|
|
|
|
|
dns = Net::DNS::Resolver.new
|
|
|
|
|
dns.use_tcp = datastore['TCP_DNS']
|
|
|
|
|
dns.udp_timeout = datastore['TIMEOUT']
|
|
|
|
|
dns.retry_number = datastore['RETRY']
|
|
|
|
|
dns.retry_interval = datastore['RETRY_INTERVAL']
|
|
|
|
|
|
|
|
|
|
ns_a_records = []
|
|
|
|
|
# try to get A record for nameserver from target NS, which may fail
|
|
|
|
|
target_ns_a = get_a(nameserver, 'axfr')
|
|
|
|
|
ns_a_records |= target_ns_a if target_ns_a
|
|
|
|
|
ns_a_records << ::Rex::Socket.resolv_to_dotted(nameserver)
|
|
|
|
|
begin
|
|
|
|
|
dns.nameservers -= dns.nameservers
|
|
|
|
|
dns.nameservers = ns_a_records
|
|
|
|
|
zone = dns.axfr(domain)
|
|
|
|
|
rescue ResolverArgumentError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, ::NoResponseError, ::Timeout::Error => e
|
|
|
|
|
print_error("Query #{domain} DNS AXFR - exception: #{e}")
|
|
|
|
|
end
|
|
|
|
|
next if zone.blank?
|
|
|
|
|
records << "#{zone}"
|
|
|
|
|
print_good("#{domain} Zone Transfer: #{zone}")
|
|
|
|
|
end
|
|
|
|
|
return if records.blank?
|
|
|
|
|
save_note(domain, 'axfr', records)
|
|
|
|
|
records
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def save_note(target, type, records)
|
|
|
|
|
data = {'target' => target, 'records' => records}
|
|
|
|
|
report_note(:host => target, :sname => 'dns', :type => type, :data => data, :update => :unique_data)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|