1

我正在尝试构建一个子域暴力破解器以供我的客户使用 - 我从事安全/笔测试。目前,我能够让 Resolv 在 10 秒内查找大约 70 台主机,给予或接受,并想知道是否有办法让它做更多事情。我已经看到了替代脚本,主要是基于 Python 的,可以实现比这更快的速度。我不知道如何增加 Resolv 并行发出的请求数量,或者我是否应该拆分列表。请注意,我已将 Google 的 DNS 服务器放在示例代码中,但将使用内部服务器进行实时使用。

我调试这个问题的粗略代码是:

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

domain.txt 是我的客户端域名列表,例如 google.com、bbc.co.uk、apple.com 和“tiny.txt”是潜在子域名的列表,例如 ftp、www、dev、files、上传。例如,Resolv 将查找 files.bbc.co.uk 并让我知道它是否存在。

4

2 回答 2

0

[注意:我已经重写了代码,以便它以块的形式获取 IP 地址。请参阅https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed。您可以修改 RESOLVE_CHUNK_SIZE 常量以平衡性能与资源负载。]

我已经使用 dnsruby gem 重写了这段代码(主要由英国的 Alex Dalitz 编写,由我自己和其他人贡献)。此版本使用异步消息处理,因此几乎同时处理所有请求。我已经在https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed上发布了一个要点,但也会在此处发布代码。

请注意,由于您是 Ruby 新手,因此代码中有很多内容可能对您有指导意义,例如方法组织、可枚举方法的使用(例如惊人的“分区”方法)、Struct 类、拯救特定的异常类、%w 和基准。

注意:看起来堆栈溢出强制执行最大消息大小,因此此代码被截断。转到上面链接中的 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
于 2016-04-21T04:26:09.257 回答
0

一件事是您正在使用 Google 名称服务器创建一个新的 Resolv 实例,但从未使用它;您创建一个全新的 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 并将其设置为空字符串,但是在下一行中您测试不是 nil,所以所有结果都应该通过,我认为这不是您想要的。

我已经重构了您的代码,但没有对其进行测试。这是我想出的,可能更清楚:

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 gem ( https://github.com/alexdalitz/dnsruby )。它可能比 Resolv 做得更好。

于 2016-04-20T14:52:58.170 回答