简体   繁体   English

提高Ruby Resolv速度

[英]Increasing Ruby Resolv Speed

Im trying to build a sub-domain brute forcer for use with my clients - I work in security/pen testing. 我正在尝试构建一个子域强盗,以便与我的客户一起使用 - 我从事安全/笔测试工作。 Currently, I am able to get Resolv to look up around 70 hosts in 10 seconds, give or take and wanted to know if there was a way to get it to do more. 目前,我能够让Resolv在10秒内查找大约70个主机,给予或接受并想知道是否有办法让它做更多。 I have seen alternative scripts out there, mainly Python based that can achieve far greater speeds than this. 我已经看到了替代脚本,主要是基于Python,可以实现比这更快的速度。 I don't know how to increase the number of requests Resolv makes in parallel, or if i should split the list up. 我不知道如何增加Resolv并行发出的请求数,或者我是否应该将列表拆分。 Please note I have put Google's DNS servers in the sample code, but will be using internal ones for live usage. 请注意我已将Google的DNS服务器放在示例代码中,但将使用内部的DNS服务器进行实时使用。

My rough code for debugging this issue is: 我调试此问题的粗略代码是:

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 is my list of client domain names, for example google.com, bbc.co.uk, apple.com and 'tiny.txt' is a list of potential subdomain names, for example ftp, www, dev, files, upload. domains.txt是我的客户域名列表,例如google.com,bbc.co.uk,apple.com和'tiny.txt'是潜在的子域名列表,例如ftp,www,dev,files,上传。 Resolv will then lookup files.bbc.co.uk for example and let me know if it exists. 然后Resolv会查找files.bbc.co.uk,让我知道它是否存在。

One thing is you are creating a new Resolv instance with the Google nameservers, but never using it; 有一件事是你用Google名称服务器创建一个新的Resolv实例,但从不使用它; you create a brand new Resolv instance to do the getaddress call, so that instance is probably using some default nameservers and not the Google ones. 您创建一个全新的Resolv实例来执行getaddress调用,因此该实例可能使用一些默认的名称服务器而不是Google的名称服务器。 You could change the code to something like this: 您可以将代码更改为以下内容:

resolv = Resolv.new(:nameserver => ['8.8.8.8', '8.8.4.4'])
# ...
ip = resolv.getaddress "#{subdomain}.#{d}" rescue ""

In addition, I suggest using the File.readlines method to simplify your code: 另外,我建议使用File.readlines方法来简化代码:

domains = File.readlines("domains.txt").map(&:chomp)
subdomains = File.readlines("tiny.txt").map(&:chomp)

Also, you're rescuing the bad ip and setting it to the empty string, but then in the next line you test for not nil, so all results should pass, and I don't think that's what you want. 此外,你正在抢救坏的ip并将其设置为空字符串,但是在下一行中你测试的不是nil,所以所有的结果都应该通过,我认为这不是你想要的。

I've refactored your code, but not tested it. 我重构了你的代码,但没有对它进行测试。 Here is what I came up with, and may be clearer: 这是我想出来的,可能更清楚:

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

Also, you might want to check out the dnsruby gem ( https://github.com/alexdalitz/dnsruby ). 此外,您可能想要查看dnsruby gem( https://github.com/alexdalitz/dnsruby )。 It might do what you want to do better than Resolv. 它可能会比Resolv做得更好。

[Note: I've rewritten the code so that it fetches the IP addresses in chunks. [注意:我已经重写了代码,以便以块的形式获取IP地址。 Please see https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed . 请参阅https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed You can modify the RESOLVE_CHUNK_SIZE constant to balance performance with resource load.] 您可以修改RESOLVE_CHUNK_SIZE常量以平衡性能和资源负载。]

I've rewritten this code using the dnsruby gem (written mainly by Alex Dalitz in the UK, and contributed to by myself and others). 我使用dnsruby gem重写了这段代码(主要由Alex Dalitz在英国编写,并由我和其他人贡献)。 This version uses asynchronous message processing so that all requests are being processed pretty much simultaneously. 此版本使用异步消息处理,以便几乎同时处理所有请求。 I've posted a gist at https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed but will also post the code here. 我在https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed上发布了一个要点,但也会在这里发布代码。

Note that since you are new to Ruby, there are lots of things in the code that might be instructive to you, such as method organization, use of Enumerable methods (eg the amazing 'partition' method), the Struct class, rescuing a specific Exception class, %w, and Benchmark. 请注意,由于您不熟悉Ruby,因此代码中有许多内容可能对您有所帮助,例如方法组织,使用Enumerable方法(例如惊人的“分区”方法),Struct类,拯救特定的异常类,%w和Benchmark。

NOTE: LOOKS LIKE STACK OVERFLOW ENFORCES A MAXIMUM MESSAGE SIZE, SO THIS CODE IS TRUNCATED. 注意:看起来像堆栈溢出执行最大消息大小,因此该代码被截断。 GO TO THE GIST IN THE LINK ABOVE FOR THE COMPLETE CODE. 转到上面链接的完整代码的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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM