Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add multi-threaded bruteforce mode and reporting

This commit allows splitting the DNS brute force process into
separate threads each running against a segment of the wordlist.
The wildcard method was modified to return the wildcard address if
one is found in order to exclude it from the brute force results.
Lastly, hosts found via brute force can be reported as new targets.
  • Loading branch information...
commit 3ac6f66c5be254f9bfc532d40c0627aa29b2be22 1 parent 3d999f1
RageLtMan authored
Showing with 131 additions and 49 deletions.
  1. +131 −49 modules/auxiliary/gather/enum_dns.rb
View
180 modules/auxiliary/gather/enum_dns.rb
@@ -50,6 +50,8 @@ def initialize(info = {})
[
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]),
+ OptInt.new('BRT_THREADS', [ true, "Number of threads to use for brute force", 1]),
+ OptBool.new('BRT_REPORT_HOST', [false, "Add hosts found via bruteforce to DB", false]),
OptBool.new('TCP_DNS', [false, "Run queries over TCP", false]),
], self.class)
end
@@ -76,17 +78,31 @@ def switchdns(target)
end
#---------------------------------------------------------------------------------
def wildcard(target)
+ addr = false
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
+ addr = rr.address.to_s
end
- return true
- else
- return false
end
+ return addr
+ end
+
+ def wildcard6(target)
+ addr = false
+ rendsub = rand(10000).to_s
+ query = @res.query("#{rendsub}.#{target}", "AAAA")
+ 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
+ addr = rr.address.to_s
+ end
+ end
+ return addr
end
#---------------------------------------------------------------------------------
def genrcd(target)
@@ -225,28 +241,44 @@ def tldexpnd(targetdom,nssrv)
end
#-------------------------------------------------------------------------------
- def dnsbrute(target, wordlist, nssrv)
+ def dnsbrute(target, wordlist, nssrv, wldcrd = nil, threads = 1)
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
+ # Read words, split list for threaded bruteforce
+ words = ::File.read(wordlist).split("\n").uniq
+ chunk_size = words.length/threads
+ words = words.each_slice(chunk_size).to_a
+ vprint_status("Running with #{words.flatten.length} words in #{threads} threads of #{chunk_size} each")
+ vprint_status("Ignoring #{wldcrd}") if wldcrd
+ # Give each thread a chunk of words to test
+ words.each do |chunk|
+ @brt_threads << Rex::ThreadFactory.spawn("DNS Brute Force #{target} #{@brt_threads.length + 1}", false) do
+ chunk.each 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 and rr.address.to_s != wldcrd
+ print_status("Hostname: #{line.chomp}.#{target} IP address: #{rr.address.to_s}")
+ report_note(:host => @nsinuse.to_s,
+ :proto => (datastore['Proxies'] || datastore['TCP_DNS']) ? 'tcp' : 'udp',
+ :sname => 'dns',
+ :port => 53 ,
+ :type => 'dns.enum',
+ :update => :unique_data,
+ :data => "#{rr.address.to_s},#{line.chomp}.#{target},A")
+ if datastore['BRT_REPORT_HOST']
+ report_host(
+ :host => rr.address.to_s,
+ :name => "#{line.chomp}.#{target}",
+ :state => Msf::HostState::Alive
+ )
+ end
+ next unless rr.class == Net::DNS::RR::CNAME
+ end
+ end
end
end
end
@@ -254,33 +286,76 @@ def dnsbrute(target, wordlist, nssrv)
end
#-------------------------------------------------------------------------------
- def bruteipv6(target, wordlist, nssrv)
+ # 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 bruteipv6(target, wordlist, nssrv, wldcrd = nil, threads = 1)
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
+ # Read words, split list for threaded bruteforce
+ words = ::File.read(wordlist).split("\n").uniq
+ chunk_size = words.length/threads
+ words = words.each_slice(chunk_size).to_a
+ print_status("Running with #{words.flatten.length} words in #{threads} threads of #{chunk_size} each")
+ print_good("Ignoring #{wldcrd}") if wldcrd
+ # Give each thread a chunk of words to test
+ words.each do |chunk|
+ @brt_threads << Rex::ThreadFactory.spawn("DNS Brute Force #{target} #{@brt_threads.length + 1}", false) do
+ chunk.each do |line|
+ if not nssrv.nil?
+ @res.nameserver=(nssrv)
+ @nsinuse = nssrv
+ end
+ query1 = @res.search("#{line.chomp}.#{target}", "AAAA")
+ if (query1)
+ query1.answer.each do |rr|
+ if rr.class == Net::DNS::RR::AAAA and rr.address.to_s != wldcrd
+ print_status("Hostname: #{line.chomp}.#{target} IPv6 address: #{rr.address.to_s}")
+ report_note(:host => @nsinuse.to_s,
+ :proto => (datastore['Proxies'] || datastore['TCP_DNS']) ? 'tcp' : 'udp',
+ :sname => 'dns',
+ :port => 53 ,
+ :type => 'dns.enum',
+ :update => :unique_data,
+ :data => "#{rr.address.to_s},#{line.chomp}.#{target},AAAA")
+ if datastore['BRT_REPORT_HOST']
+ report_host(
+ :host => rr.address.to_s,
+ :name => "#{line.chomp}.#{target}",
+ :state => Msf::HostState::Alive
+ )
+ end
+ next unless rr.class == Net::DNS::RR::CNAME
+ end
+ end
end
end
end
-
end
end
@@ -497,6 +572,7 @@ def axfr(target, nssrv)
end
def run
+ @brt_threads = []
@res = Net::DNS::Resolver.new()
if datastore['TCP_DNS']
vprint_status("Using DNS/TCP")
@@ -506,6 +582,7 @@ def run
@res.retry_interval = datastore['RETRY_INTERVAL'].to_i
@threadnum = datastore['THREADS'].to_i
wldcrd = wildcard(datastore['DOMAIN'])
+ wldcrd6 = wildcard6(datastore['DOMAIN'])
switchdns(datastore['DOMAIN'])
if(datastore['ENUM_STD'])
@@ -520,7 +597,7 @@ def run
if wldcrd and datastore['STOP_WLDCRD']
print_error("Wildcard record found!")
else
- dnsbrute(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS'])
+ dnsbrute(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS'],wldcrd,datastore['BRT_THREADS'])
end
end
@@ -528,7 +605,7 @@ def run
if wldcrd and datastore['STOP_WLDCRD']
print_status("Wildcard Record Found!")
else
- bruteipv6(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS'])
+ bruteipv6(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS'],wldcrd6,datastore['BRT_THREADS'])
end
end
@@ -543,5 +620,10 @@ def run
if(datastore['ENUM_RVL'] and datastore['IPRANGE'] and not datastore['IPRANGE'].empty?)
reverselkp(datastore['IPRANGE'],datastore['NS'])
end
+ # Do not let module finish while threads exist
+ while not @brt_threads.empty? do
+ vprint_status("Waiting on #{@brt_threads.length} DNS brute force threads to finish")
+ Rex::ThreadSafe.sleep(10)
+ end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.