Увеличение скорости Ruby Resolv
Я пытаюсь создать поддельный брутфорсер для использования с моими клиентами - я работаю в тестировании безопасности / пера. В настоящее время я могу заставить Resolv посмотреть около 70 хостов за 10 секунд, дать или взять и хотел узнать, есть ли способ заставить его делать больше. Я видел альтернативные сценарии, в основном основанные на Python, которые могут достигать гораздо большей скорости, чем эта. Я не знаю, как увеличить количество запросов, которые Resolv выполняет параллельно, или я должен разделить список. Обратите внимание, что я поместил DNS-серверы Google в пример кода, но буду использовать внутренние для живого использования.
Мой грубый код для отладки этой проблемы:
require 'resolv'
def subdomains
puts "Subdomain enumeration beginning at #{Time.now.strftime("%H:%M:%S")}"
subs = []
domains = File.open("domains.txt", "r") #list of domain names line by line.
Resolv.new(:nameserver => ['8.8.8.8', '8.8.4.4'])
File.open("tiny.txt", "r").each_line do |subdomain|
subdomain.chomp!
domains.each do |d|
puts "Checking #{subdomain}.#{d}"
ip = Resolv.new.getaddress "#{subdomain}.#{d}" rescue ""
if ip != nil
subs << subdomain+"."+d << ip
end
end
end
test = subs.each_slice(4).to_a
test.each do |z|
if !z[1].nil? and !z[3].nil?
puts z[0] + "\t" + z[1] + "\t\t" + z[2] + "\t" + z[3]
end
end
puts "Finished at #{Time.now.strftime("%H:%M:%S")}"
end
subdomains
domains.txt - это мой список имен доменов клиентов, например, google.com, bbc.co.uk, apple.com, а tiny.txt - список потенциальных имен субдоменов, например, ftp, www, dev, files, загрузить. Resolv, например, найдет файлы files.bbc.co.uk и сообщит мне, существует ли он.
2 ответа
[Примечание: я переписал код так, чтобы он выбирал IP-адреса порциями. Пожалуйста, смотрите https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed. Вы можете изменить константу RESOLVE_CHUNK_SIZE, чтобы сбалансировать производительность с нагрузкой на ресурсы.]
Я переписал этот код с помощью гема dnsruby (написанного главным образом Алексом Далицем в Великобритании и написанного мной и другими). В этой версии используется асинхронная обработка сообщений, поэтому все запросы обрабатываются практически одновременно. Я разместил суть на https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed но также опубликую код здесь.
Обратите внимание, что, поскольку вы новичок в Ruby, в коде есть много вещей, которые могут быть полезны для вас, такие как организация методов, использование методов Enumerable (например, удивительный метод 'partition'), класс Struct, спасение определенного Класс исключения, %w и Benchmark.
ПРИМЕЧАНИЕ: СМОТРИТЕ, КАК ПЕРЕНОСНЫЙ СТЕК, ПОДДЕРЖИВАЕТ МАКСИМАЛЬНЫЙ РАЗМЕР СООБЩЕНИЯ, ПОЭТОМУ ЭТОТ КОД УМЕНЬШЕН. Перейти к GIST в ссылке выше для полного кода.
#!/usr/bin/env ruby
# Takes a list of subdomain prefixes (e.g. %w(ftp xyz)) and a list of domains (e.g. %w(nytimes.com afp.com)),
# creates the subdomains combining them, fetches their IP addresses (or nil if not found).
require 'dnsruby'
require 'awesome_print'
RESOLVER = Dnsruby::Resolver.new(:nameserver => %w(8.8.8.8 8.8.4.4))
# Experiment with this to get fast throughput but not overload the dnsruby async mechanism:
RESOLVE_CHUNK_SIZE = 50
IpEntry = Struct.new(:name, :ip) do
def to_s
"#{name}: #{ip ? ip : '(nil)'}"
end
end
def assemble_subdomains(subdomain_prefixes, domains)
domains.each_with_object([]) do |domain, subdomains|
subdomain_prefixes.each do |prefix|
subdomains << "#{prefix}.#{domain}"
end
end
end
def create_query_message(name)
Dnsruby::Message.new(name, 'A')
end
def parse_response_for_address(response)
begin
a_answer = response.answer.detect { |a| a.type == 'A' }
a_answer ? a_answer.rdata.to_s : nil
rescue Dnsruby::NXDomain
return nil
end
end
def get_ip_entries(names)
queue = Queue.new
names.each do |name|
query_message = create_query_message(name)
RESOLVER.send_async(query_message, queue, name)
end
# Note: although map is used here, the record in the output array will not necessarily correspond
# to the record in the input array, since the order of the messages returned is not guaranteed.
# This is indicated by the lack of block variable specified (normally w/map you would use the element).
# That should not matter to us though.
names.map do
_id, result, error = queue.pop
name = _id
case error
when Dnsruby::NXDomain
IpEntry.new(name, nil)
when NilClass
ip = parse_response_for_address(result)
IpEntry.new(name, ip)
else
raise error
end
end
end
def main
# domains = File.readlines("domains.txt").map(&:chomp)
domains = %w(nytimes.com afp.com cnn.com bbc.com)
# subdomain_prefixes = File.readlines("subdomain_prefixes.txt").map(&:chomp)
subdomain_prefixes = %w(www xyz)
subdomains = assemble_subdomains(subdomain_prefixes, domains)
start_time = Time.now
ip_entries = subdomains.each_slice(RESOLVE_CHUNK_SIZE).each_with_object([]) do |ip_entries_chunk, results|
results.concat get_ip_entries(ip_entries_chunk)
end
duration = Time.now - start_time
found, not_found = ip_entries.partition { |entry| entry.ip }
puts "\nFound:\n\n"; puts found.map(&:to_s); puts "\n\n"
puts "Not Found:\n\n"; puts not_found.map(&:to_s); puts "\n\n"
stats = {
duration: duration,
domain_count: ip_entries.size,
found_count: found.size,
not_found_count: not_found.size,
}
ap stats
end
main
Во-первых, вы создаете новый экземпляр Resolv с помощью серверов имен Google, но никогда не используете его; вы создаете новый экземпляр Resolv для вызова getaddress, так что этот экземпляр, вероятно, использует некоторые серверы имен по умолчанию, а не Google. Вы можете изменить код на что-то вроде этого:
resolv = Resolv.new(:nameserver => ['8.8.8.8', '8.8.4.4'])
# ...
ip = resolv.getaddress "#{subdomain}.#{d}" rescue ""
Кроме того, я предлагаю использовать метод File.readlines для упрощения вашего кода:
domains = File.readlines("domains.txt").map(&:chomp)
subdomains = File.readlines("tiny.txt").map(&:chomp)
Кроме того, вы спасаете плохой IP и устанавливаете его в пустую строку, но затем в следующей строке вы проверяете не ноль, поэтому все результаты должны пройти, и я не думаю, что это то, что вы хотите.
Я переработал ваш код, но не проверял его. Вот то, что я придумал, и может быть более ясным:
def subdomains
puts "Subdomain enumeration beginning at #{Time.now.strftime("%H:%M:%S")}"
domains = File.readlines("domains.txt").map(&:chomp)
subdomains = File.readlines("tiny.txt").map(&:chomp)
resolv = Resolv.new(:nameserver => ['8.8.8.8', '8.8.4.4'])
valid_subdomains = subdomains.each_with_object([]) do |subdomain, valid_subdomains|
domains.each do |domain|
combined_name = "#{subdomain}.#{domain}"
puts "Checking #{combined_name}"
ip = resolv.getaddress(combined_name) rescue nil
valid_subdomains << "#{combined_name}#{ip}" if ip
end
end
valid_subdomains.each_slice(4).each do |z|
if z[1] && z[3]
puts "#{z[0]}\t#{z[1]}\t\t#{z[2]}\t#{z[3]}"
end
end
puts "Finished at #{Time.now.strftime("%H:%M:%S")}"
end
Кроме того, вы можете проверить гем dnsruby ( https://github.com/alexdalitz/dnsruby). Это может сделать то, что вы хотите сделать лучше, чем Resolv.