Module: Dert

Defined in:
lib/dert/dns.rb,
lib/dert/version.rb,
lib/dert/methods/brt.rb,
lib/dert/methods/rvl.rb,
lib/dert/methods/srv.rb,
lib/dert/methods/std.rb,
lib/dert/methods/tld.rb,
lib/dert/methods/arin.rb,
lib/dert/methods/axfr.rb,
lib/dert/methods/ipv6.rb

Defined Under Namespace

Modules: CONSTANTS Classes: ARIN, AXFR, BRT, IPV6, RVL, SRV, STD, TLD

Constant Summary collapse

VERSION =
'1.0.2'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.query(domain, method, list = nil) ⇒ Object

Parameters:

  • domain: (String)

    Target Domain #

  • method: (Integer)

    Enum Method #

  • list: (String)

    Path to Wordlist #



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/dert/dns.rb', line 44

def self.query(domain, method, list=nil)
  case method
    when CONSTANTS::ARIN
      return ARIN.query(domain)
    when CONSTANTS::AXFR
      return AXFR.query(domain)
    when CONSTANTS::BRT
      return BRT.query(domain, list, Dnsruby::Types.A)
    when CONSTANTS::IPV6
      return BRT.query(domain, list, Dnsruby::Types.AAAA)
    when CONSTANTS::RVL
      return RVL.query(domain)
    when CONSTANTS::SRV
      return SRV.query(domain)
    when CONSTANTS::STD
      return STD.query(domain)
    when CONSTANTS::TLD
      return TLD.query(domain)
  end
end

.run(options) ⇒ Object

Parameters:

  • options: (Hash)

    DNS Enum Options #



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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/dert/dns.rb', line 160

def self.run(options)
  type = 0

  # RVL does not require a domain
  unless options[:type] == 'rvl'
    unless options[:domain]
      puts 'Invalid command. Try --help to view options.'
      exit
    end

    # remove http/https
    options[:domain].gsub!('https://', '')
    options[:domain].gsub!('http://', '')

    # Validate Domain
    unless options[:domain].match(/[a-zA-Z0-9\-]+\.[a-zA-z]{2,6}/)
      puts 'Invalid domain.'
      exit
    end
  end

  # Validate settings for brute force
  if %w(ipv6 brt).include? options[:type]
    if options[:threads] == nil or options[:domain] == nil or options[:wordlist] == nil
      puts "Usage #{File.basename($0)} -e <brt|ipv6> -d <domain> -w <wordlist> -t <threads>"
      exit
    end
  end

  # RVL requires threads and a word list
  if options[:type] == 'rvl'
    if options[:threads] == nil or options[:wordlist] == nil
      puts "Usage #{File.basename($0)} -e rvl -w <wordlist of ips> -t <threads>"
      exit
    end
  end

  # Validate wordlist
  if options[:wordlist]
    unless File.exist?(options[:wordlist])
      puts 'Word List not found.'
      exit
    end
  end

  # Validate threads
  if options[:threads]
    if options[:threads] > 100 or options[:threads] < 1
      puts 'Thread count must be between 1 and 100'
      exit
    end
  end

  # Validate Output
  if options[:output]
    unless Dir.exists?(File.dirname(options[:output]))
      puts 'Output directory does not exists.'
      exit
    end
  end

  # Convert string type to integer type
  case options[:type]
    when 'arin'
      type = 1
    when 'axfr'
      type = 2
    when 'brt'
      type = 3
    when 'ipv6'
      type = 4
    when 'rvl'
      type = 5
    when 'srv'
      type = 6
    when 'std'
      type = 7
    when 'tld'
      type = 8
    else
      puts 'Wrong enumeration type. Try --help to view accepted enumeration inputs.'
      exit
  end

  # Start Enumeration
  results = self.start(options[:domain], type, options[:threads], options[:wordlist])

  # Save results to a file if specified
  if options[:output]
    File.open(options[:output], 'w') do |f|
      f.write(JSON.pretty_generate(results))
    end
  end

  # Print output to terminal unless silent
  unless options[:silent]
    puts 'Results:'
    if type == 1
      results.each do |x|
        puts "  Range: #{x[:cidr]}"
        puts "  Handle: #{x[:handle]}"
        puts "  Customer: #{x[:customer]}"
        puts "  Zip Code: #{x[:zip]}"
        puts ''
      end
    else
      results.each do |x|
        puts "  Hostname: #{x[:hostname]}"
        puts "    IP: #{x[:address]}"
        puts "    Type: #{x[:type]}"
      end
    end
  end

  # Return results as a hash
  results
end

.start(domain, method, threads = nil, word_list = nil, output = nil) ⇒ Object

Parameters:

  • domain: (String)

    Target Domain #

  • method: (Integer)

    Enum Method #

  • threads: (Integer)

    Bruteforce Threads #

  • word_list: (String)

    Path to Wordlist #



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
# File 'lib/dert/dns.rb', line 74

def self.start(domain, method, threads = nil, word_list = nil, output = nil)

  results = []

  # Process for Brute Force DNS Enumeration
  if method == CONSTANTS::BRT or method == CONSTANTS::IPV6 or method == CONSTANTS::RVL

    # Count words/ips in list.
    count = File.foreach(word_list).inject(0) { |c, line| c+1 }
    # Words/IPs per thread
    per = (count / threads) + 1
    # Array of words/ips
    arr = []
    # Array of sets of words/ips per thread
    lists_per_thread = []

    thread_container = []

    # Parse words/ips from word list
    File.open(word_list).each_line do |x|
      if method == CONSTANTS::RVL
        tmp = Rex::Socket::RangeWalker.new(x.chomp.strip)
        if tmp.valid?
          tmp.each do |y|
            arr << y
          end
        end
      else
        arr << x.chomp
      end
    end

    # If word list count is greater than 50, use multiple threads.
    if arr.count > 50
      arr.each_slice(per) { |a| lists_per_thread << a }
    else
      lists_per_thread = [arr]
    end

    # Iterate through sets of words.
    lists_per_thread.each do |x|
      # Create a new thread and add it to a container
      thread_container << Thread.new {
        # Check if RVL, else BRT or IPV
        if method == CONSTANTS::RVL
          # Iterate through IP addresses
          ret = []
          x.each do |y|
            ret.concat(self.query(y, method))
          end
        else
          # Send a single set of words to brute force
          ret = self.query(domain, method, x)
        end
        # Grab thread output
        Thread.current[:output] = ret
      }
    end

    # Join all threads and grab their outputs
    thread_container.each do |t|
      t.join
      results += t[:output] unless t[:output].empty?
    end

    # Process for Single Enumeration
  else
    results = self.query(domain, method)
  end

  # Write output to file if specified
  if output
    File.open(output, 'w') do |f|
      f.write(JSON.pretty_generate(results))
    end
  end

  # Return Results for Console output
  results
end

Instance Method Details

#query(domain, method, list) ⇒ Object

Parameters:

  • domain: (String)

    Target Domain #

  • method: (Integer)

    Enum Method #

  • list: (String)

    Path to Wordlist #



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/dert/dns.rb', line 44

def self.query(domain, method, list=nil)
  case method
    when CONSTANTS::ARIN
      return ARIN.query(domain)
    when CONSTANTS::AXFR
      return AXFR.query(domain)
    when CONSTANTS::BRT
      return BRT.query(domain, list, Dnsruby::Types.A)
    when CONSTANTS::IPV6
      return BRT.query(domain, list, Dnsruby::Types.AAAA)
    when CONSTANTS::RVL
      return RVL.query(domain)
    when CONSTANTS::SRV
      return SRV.query(domain)
    when CONSTANTS::STD
      return STD.query(domain)
    when CONSTANTS::TLD
      return TLD.query(domain)
  end
end

#run(options#) ⇒ Object

Parameters:

  • options: (Hash)

    DNS Enum Options #



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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/dert/dns.rb', line 160

def self.run(options)
  type = 0

  # RVL does not require a domain
  unless options[:type] == 'rvl'
    unless options[:domain]
      puts 'Invalid command. Try --help to view options.'
      exit
    end

    # remove http/https
    options[:domain].gsub!('https://', '')
    options[:domain].gsub!('http://', '')

    # Validate Domain
    unless options[:domain].match(/[a-zA-Z0-9\-]+\.[a-zA-z]{2,6}/)
      puts 'Invalid domain.'
      exit
    end
  end

  # Validate settings for brute force
  if %w(ipv6 brt).include? options[:type]
    if options[:threads] == nil or options[:domain] == nil or options[:wordlist] == nil
      puts "Usage #{File.basename($0)} -e <brt|ipv6> -d <domain> -w <wordlist> -t <threads>"
      exit
    end
  end

  # RVL requires threads and a word list
  if options[:type] == 'rvl'
    if options[:threads] == nil or options[:wordlist] == nil
      puts "Usage #{File.basename($0)} -e rvl -w <wordlist of ips> -t <threads>"
      exit
    end
  end

  # Validate wordlist
  if options[:wordlist]
    unless File.exist?(options[:wordlist])
      puts 'Word List not found.'
      exit
    end
  end

  # Validate threads
  if options[:threads]
    if options[:threads] > 100 or options[:threads] < 1
      puts 'Thread count must be between 1 and 100'
      exit
    end
  end

  # Validate Output
  if options[:output]
    unless Dir.exists?(File.dirname(options[:output]))
      puts 'Output directory does not exists.'
      exit
    end
  end

  # Convert string type to integer type
  case options[:type]
    when 'arin'
      type = 1
    when 'axfr'
      type = 2
    when 'brt'
      type = 3
    when 'ipv6'
      type = 4
    when 'rvl'
      type = 5
    when 'srv'
      type = 6
    when 'std'
      type = 7
    when 'tld'
      type = 8
    else
      puts 'Wrong enumeration type. Try --help to view accepted enumeration inputs.'
      exit
  end

  # Start Enumeration
  results = self.start(options[:domain], type, options[:threads], options[:wordlist])

  # Save results to a file if specified
  if options[:output]
    File.open(options[:output], 'w') do |f|
      f.write(JSON.pretty_generate(results))
    end
  end

  # Print output to terminal unless silent
  unless options[:silent]
    puts 'Results:'
    if type == 1
      results.each do |x|
        puts "  Range: #{x[:cidr]}"
        puts "  Handle: #{x[:handle]}"
        puts "  Customer: #{x[:customer]}"
        puts "  Zip Code: #{x[:zip]}"
        puts ''
      end
    else
      results.each do |x|
        puts "  Hostname: #{x[:hostname]}"
        puts "    IP: #{x[:address]}"
        puts "    Type: #{x[:type]}"
      end
    end
  end

  # Return results as a hash
  results
end

#start(domain, method, threads, word_list, output) ⇒ Object

Parameters:

  • domain: (String)

    Target Domain #

  • method: (Integer)

    Enum Method #

  • threads: (Integer)

    Bruteforce Threads #

  • word_list: (String)

    Path to Wordlist #



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
# File 'lib/dert/dns.rb', line 74

def self.start(domain, method, threads = nil, word_list = nil, output = nil)

  results = []

  # Process for Brute Force DNS Enumeration
  if method == CONSTANTS::BRT or method == CONSTANTS::IPV6 or method == CONSTANTS::RVL

    # Count words/ips in list.
    count = File.foreach(word_list).inject(0) { |c, line| c+1 }
    # Words/IPs per thread
    per = (count / threads) + 1
    # Array of words/ips
    arr = []
    # Array of sets of words/ips per thread
    lists_per_thread = []

    thread_container = []

    # Parse words/ips from word list
    File.open(word_list).each_line do |x|
      if method == CONSTANTS::RVL
        tmp = Rex::Socket::RangeWalker.new(x.chomp.strip)
        if tmp.valid?
          tmp.each do |y|
            arr << y
          end
        end
      else
        arr << x.chomp
      end
    end

    # If word list count is greater than 50, use multiple threads.
    if arr.count > 50
      arr.each_slice(per) { |a| lists_per_thread << a }
    else
      lists_per_thread = [arr]
    end

    # Iterate through sets of words.
    lists_per_thread.each do |x|
      # Create a new thread and add it to a container
      thread_container << Thread.new {
        # Check if RVL, else BRT or IPV
        if method == CONSTANTS::RVL
          # Iterate through IP addresses
          ret = []
          x.each do |y|
            ret.concat(self.query(y, method))
          end
        else
          # Send a single set of words to brute force
          ret = self.query(domain, method, x)
        end
        # Grab thread output
        Thread.current[:output] = ret
      }
    end

    # Join all threads and grab their outputs
    thread_container.each do |t|
      t.join
      results += t[:output] unless t[:output].empty?
    end

    # Process for Single Enumeration
  else
    results = self.query(domain, method)
  end

  # Write output to file if specified
  if output
    File.open(output, 'w') do |f|
      f.write(JSON.pretty_generate(results))
    end
  end

  # Return Results for Console output
  results
end