-
Notifications
You must be signed in to change notification settings - Fork 6
/
dnsbrute.rb
executable file
·254 lines (222 loc) · 7.19 KB
/
dnsbrute.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#!/usr/bin/env ruby
# encoding: utf-8
require 'bundler/setup'
require 'pp'
require 'optparse'
require 'colorize'
require 'nokogiri'
require_relative 'DNSBruteForcer'
############
def parseOptions()
options = {
:domain => nil,
:dictionary => nil,
:dns => nil,
:geoinfo => false,
:whois => false,
:nthreads => 5
}
optparse = OptionParser.new do |opts|
opts.banner = "Usage: #{__FILE__} [OPTIONS]"
opts.on( '-d', '--domain DOMAIN', String, "Domain to explore for hosts by bruteforce." ) do |domain|
options[:domain] = domain
end
opts.on( '-D', '--dictionary FILE', String, "Dicionary file containing the list of subdomains to check" ) do |dictionary|
options[:dictionary] = dictionary
end
opts.on( '-t', '--threads [NTHREADS]', 'Number of threads used to ask DNS servers in bruteforce attack [Default: 5]' ) do |nthreads|
options[:nthreads] = nthreads
end
opts.on( '-g', '--geo-info', 'Get also the geographic information of the host from freegeoip.net' ) do
options[:geoinfo] = true
end
opts.on( '-w', '--whois', 'Get also the whois information of every hostname found' ) do
options[:whois] = true
end
opts.on( '-f', '--force-dns [DNS]', 'Force the enumeration against this DNS instead of the authoritative ones' ) do |dns|
options[:dns] = dns
end
opts.on( '-h', '--help', 'Help screen' ) do
puts optparse
exit
end
end
optparse.parse!
if options[:domain].nil? or options[:dictionary].nil?
puts optparse
raise OptionParser::MissingArgument
end
return options
end
##########################
def saveOutputKML(ofile,foundhosts)
oxml = File.open(ofile,"w")
builder = Nokogiri::XML::Builder.new {|xml|
xml.kml('xmlns' => "http://earth.google.com/kml/2.2") {
xml.Document{
xml.name "#{ofile}"
foundhosts.each {|h|
if !h[:geo].nil?
xml.Placemark {
xml.name "#{h[:name]} - #{h[:ip]}"
whois = h[:whois].gsub("\n","<br/>")
xml.description """
#{h[:name]} - #{h[:ip]}.<br/>
Record type: #{h[:type]}<br/>
Location: #{h[:geo]["city"]}, #{h[:geo]["region_name"]}, #{h[:geo]["country_name"]}<br/>
<br/>
==================
<br/>
#{whois}<br/>
"""
xml.Point {
xml.coordinates "#{h[:geo]["longitude"].to_f},#{h[:geo]["latitude"].to_f},0.0"
}
}
end
}
}
}
}
oxml.puts builder.to_xml
oxml.close
end
##########################
def avoidOverwritingOutput(ofile)
if File.exists?(ofile)
while 1 # Only finish when there is a correct choice
print "Output file '#{ofile}' already exists. What do you want to do? (O)verwrite,(S)kip saving output,(R)ename: "
c = gets.chomp.upcase
case c
when "O"
puts "Overwriting file..."
return ofile
when "S"
puts "Skipping the save output phase... "
return nil
when "R"
print "Please, provide the new name for the output file: "
newname = gets.chomp
return newname
end
end
else
return ofile
end
end
###################
def saveOutputCSV(ofile, foundhosts)
f = File.open(ofile,"w")
f.puts("hostname;address;type;Geo. Info.;Whois Info")
foundhosts.each{|h|
geoinfo = ""
if !h[:geo].nil?
geoinfo += "#{h[:geo]["city"]}, " if !h[:geo]["city"].nil? and h[:geo]["city"].size > 0
geoinfo += "#{h[:geo]["region_name"]}, " if !h[:geo]["region_name"].nil? and h[:geo]["region_name"].size > 0
geoinfo += "#{h[:geo]["country_name"]} " if !h[:geo]["country_name"].nil? and h[:geo]["country_name"].size > 0
geoinfo += "(Lat.: #{h[:geo]["latitude"]}, Long.: #{h[:geo]["longitude"]})" if !h[:geo]["latitude"].nil? and !h[:geo]["longitude"].nil?
end
f.puts "\"#{h[:name]}\";\"#{h[:ip]}\";\"#{h[:type]}\";\"#{geoinfo}\";\"#{h[:whois]}\""
}
f.close
end
############
def printBanner()
banner = %q{
###########################
# #
# DNS Brute Forcer #
# Version: 0.4 #
# Author: Felipe Molina #
# Twitter: @felmoltor #
# #
###########################
}
puts banner.cyan
end
############
def createFolders()
if !Dir.exists?("outputs")
Dir.mkdir("outputs")
end
if !Dir.exist?("outputs/csv")
Dir.mkdir("outputs/csv")
end
if !Dir.exist?("outputs/maps")
Dir.mkdir("outputs/maps")
end
end
########
# MAIN #
########
createFolders()
printBanner()
op = parseOptions()
dnsb = DNSBruteForcer.new()
dnsb.geodetails = op[:geoinfo]
dnsb.whois = op[:whois]
dnsb.threads = op[:nthreads]
auths = dnsb.getAuthDNSServers(op[:domain])
puts "The authoritative servers of #{op[:domain]} are: "
auths.each{|s|
puts "- #{s}"
}
nsservers = dnsb.getAllDNSServer(op[:domain])
puts "The name servers of #{op[:domain]} are:"
nsservers.each{|s|
puts "- #{s}"
}
if !op[:dns].nil?
puts "Forcing the enumeration against customiced DNS '#{op[:dns]}'"
dnsb.setNameServers(op[:dns])
else
puts "Forcing the enumeration against domain nameservers (#{nsservers.join(', ')})."
dnsb.setNameServers(nsservers)
end
zones = dnsb.transferZone(op[:domain])
if !zones.nil?
puts "Zone transfer is allowed in one or more of it's NS!".green
pp zones
else
puts "Zone transfer is not allowed in any of it's NS.".red
puts "Starting bruteforce scan. Please be patient..."
if File.exists?(op[:dictionary])
dnsb.dictionary = op[:dictionary]
hosts = dnsb.bruteforceSubdomains(op[:domain])
if !hosts.nil? and hosts.size > 0
puts ""
puts "#{hosts.size} hosts were found with the bruteforce attack!".green
hosts.each {|h|
print "- #{h[:name]} - #{h[:ip].to_s} (#{h[:type]})"
if op[:geoinfo] and !h[:geo].nil?
print " - "
print "#{h[:geo]["city"]}, " if !h[:geo]["city"].nil? and h[:geo]["city"].size > 0
print "#{h[:geo]["region_name"]}, " if !h[:geo]["city"].nil? and h[:geo]["region_name"].size > 0
print "#{h[:geo]["country_name"]} " if !h[:geo]["city"].nil? and h[:geo]["country_name"].size > 0
print "(Lat.: #{h[:geo]["latitude"]}, Long.: #{h[:geo]["longitude"]})" if !h[:geo]["latitude"].nil? and !h[:geo]["longitude"].nil?
end
puts ""
}
else
puts "No hosts were found with the bruteforce attack :-(".red
end
else
puts "Dictionary file #{op[:dictionary]} couldn't be found. Skipping bruteforce attack..."
end
end
# Save results into outfile
csvname = "outputs/csv/#{op[:domain].gsub("/","_").gsub(":","_")}.csv"
kmlname = "outputs/maps/#{op[:domain].gsub("/","_").gsub(":","_")}.kml"
ofc = avoidOverwritingOutput(csvname)
if !ofc.nil?
saveOutputCSV(ofc,hosts)
puts "Results were saved in '#{ofc}'."
end
# If no geographic data is requested by the user dont create the map file
if op[:geoinfo]
ofk = avoidOverwritingOutput(kmlname)
if !ofk.nil?
saveOutputKML(ofk,hosts)
puts "Maps were saved in '#{ofk}'."
end
end