aquatone 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 89f0cbe4f00ec77bae2d323be2978261680a75d8
4
- data.tar.gz: 32948fb248e6614400c878ea2ac4681a5da9558b
3
+ metadata.gz: d8ad3da3d450ecd7fd41c64cd0b18f0a3e3ae00b
4
+ data.tar.gz: 240fbce3dc353f146a32be28566201b2b62fd2c6
5
5
  SHA512:
6
- metadata.gz: 941e0e136a451eb295184a6b35dde232cbcae72624a4e421a5c304f89f70c281744cc85c05eafe951f210fd744ec6b3406362b554a4cab9a46b666e58c7be254
7
- data.tar.gz: 48ccd44ff3e7fa4cfd05bd7221e6287479f9b80b362342915789ffe673c12a9f8a25fc8ef63524286eee401f4fccddf0fb8395810ca1e352aa6b81c0445325df
6
+ metadata.gz: 244cc756b2cb1c65d36281fbeae199d290a71434f73a12c96e855995eff2a091aae442212d13ee604ac6b34d6d74196813ff3b01bd5b809b16512c86b438c44e
7
+ data.tar.gz: b109618403ee14536a67a8e75334e88279578f2eb9532edd8ea772c7bf1e443a8747a01038eadfc44bd5a42000ab90571be269432340eef1257161f6d3d40110
@@ -10,6 +10,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
10
10
  ### Changed
11
11
 
12
12
 
13
+ ## [0.4.0]
14
+ ### Added
15
+ - Collector module defined CLI options: Collectors can now define their own CLI options for `aquatone-discover`,
16
+ e.g. `--wordlist` to make the Dictionary collector use a custom wordlist instead of the built-in one.
17
+ See `aquatone-discover --help` for all new options.
18
+
19
+ ### Changed
20
+
21
+ ### Fixed
22
+ - Performance improvement in the way collector modules check for duplicate hosts (was only an issue with
23
+ very large results or dictionaries)
24
+
25
+
13
26
  ## [0.3.0]
14
27
  ### Added
15
28
  - New Tool: aquatone-takeover: Check discovered hosts for subdomain takeover vulnerabilities
@@ -47,7 +60,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
47
60
 
48
61
  ### Changed
49
62
 
50
- [Unreleased]: https://github.com/michenriksen/aquatone/compare/v0.3.0...HEAD
63
+ [Unreleased]: https://github.com/michenriksen/aquatone/compare/v0.4.0...HEAD
64
+ [0.4.0]: https://github.com/michenriksen/aquatone/compare/v0.3.0...v0.4.0
51
65
  [0.3.0]: https://github.com/michenriksen/aquatone/compare/v0.2.0...v0.3.0
52
66
  [0.2.0]: https://github.com/michenriksen/aquatone/compare/v0.1.1...v0.2.0
53
67
  [0.1.1]: https://github.com/michenriksen/aquatone/compare/v0.1.0...v0.1.1
@@ -121,6 +121,14 @@ OptionParser.new do |opts|
121
121
  options[:jitter] = v.to_f
122
122
  end
123
123
 
124
+ Aquatone::Collector.descendants.each do |collector|
125
+ collector.cli_options.each_pair do |option, description|
126
+ opts.on("--#{option}", description) do |v|
127
+ options[option.split(" ").first.gsub("-", "_").to_sym] = v
128
+ end
129
+ end
130
+ end
131
+
124
132
  opts.on("-h", "--help", "Show help") do
125
133
  puts opts
126
134
  exit 0
@@ -23,15 +23,21 @@ module Aquatone
23
23
  collectors.sort { |x, y| x.priority <=> y.priority }
24
24
  end
25
25
 
26
+ def self.cli_options
27
+ meta.key?(:cli_options) ? meta[:cli_options] : {}
28
+ end
29
+
26
30
  def self.sluggified_name
27
31
  return meta[:slug].downcase if meta[:slug]
28
32
  meta[:name].strip.downcase.gsub(/[^a-z0-9]+/, '-').gsub("--", "-")
29
33
  end
30
34
 
31
- def initialize(domain)
35
+ def initialize(domain, options)
32
36
  check_key_requirements!
33
- @domain = domain
34
- @hosts = []
37
+ @domain = domain
38
+ @options = options
39
+ @hosts = []
40
+ @host_dictionary = {}
35
41
  end
36
42
 
37
43
  def run
@@ -52,7 +58,9 @@ module Aquatone
52
58
  def add_host(host)
53
59
  host.downcase!
54
60
  return unless Aquatone::Validation.valid_domain_name?(host)
55
- @hosts << host unless @hosts.include?(host)
61
+ return if @host_dictionary.key?(host)
62
+ @host_dictionary[host] = true
63
+ @hosts << host
56
64
  end
57
65
 
58
66
  def get_request(uri, options={})
@@ -83,6 +91,14 @@ module Aquatone
83
91
  Aquatone::KeyStore.key?(name)
84
92
  end
85
93
 
94
+ def get_cli_option(name)
95
+ @options[name.to_s.gsub("-", "_").to_sym]
96
+ end
97
+
98
+ def has_cli_option?(name)
99
+ @options.key?(name.to_s.gsub("-", "_").to_sym)
100
+ end
101
+
86
102
  def failure(message)
87
103
  fail Error, message
88
104
  end
@@ -101,6 +117,13 @@ module Aquatone
101
117
  fail InvalidMetadataError, "Metadata is missing key: name" unless meta.key?(:name)
102
118
  fail InvalidMetadataError, "Metadata is missing key: author" unless meta.key?(:author)
103
119
  fail InvalidMetadataError, "Metadata is missing key: description" unless meta.key?(:description)
120
+ if meta.key?(:cli_options)
121
+ fail InvalidMetadataError, "Metadata CLI options is not a hash" unless meta[:cli_options].is_a?(Hash)
122
+ meta[:cli_options].each_pair do |option, description|
123
+ fail InvalidMetadataError, "CLI option name is not a string" unless option.is_a?(String)
124
+ fail InvalidMetadataError, "CLI option details is not a string" unless description.is_a?(String)
125
+ end
126
+ end
104
127
  end
105
128
  end
106
129
  end
@@ -6,11 +6,14 @@ module Aquatone
6
6
  :author => "James McLean (@vortexau)",
7
7
  :description => "Uses the Censys API to find hostnames in TLS certificates",
8
8
  :require_keys => ["censys_secret","censys_id"],
9
+ :cli_options => {
10
+ "censys-pages PAGES" => "Number of Censys API pages to process (default: 10)"
11
+ }
9
12
  }
10
13
 
11
- API_BASE_URI = "https://www.censys.io/api/v1".freeze
12
- API_RESULTS_PER_PAGE = 100.freeze
13
- PAGES_TO_PROCESS = 10.freeze
14
+ API_BASE_URI = "https://www.censys.io/api/v1".freeze
15
+ API_RESULTS_PER_PAGE = 100.freeze
16
+ DEFAULT_PAGES_TO_PROCESS = 10.freeze
14
17
 
15
18
  def run
16
19
  request_censys_page
@@ -21,10 +24,10 @@ module Aquatone
21
24
 
22
25
  # Censys expects Basic Auth for requests.
23
26
  auth = {
24
- :username => get_key('censys_id'),
27
+ :username => get_key('censys_id'),
25
28
  :password => get_key('censys_secret')
26
29
  }
27
-
30
+
28
31
  # Define this is JSON content
29
32
  headers = {
30
33
  'Content-Type' => 'application/json',
@@ -41,11 +44,11 @@ module Aquatone
41
44
 
42
45
  # Search API documented at https://censys.io/api/v1/docs/search
43
46
  response = post_request(
44
- "#{API_BASE_URI}/search/certificates",
47
+ "#{API_BASE_URI}/search/certificates",
45
48
  query.to_json,
46
49
  {
47
50
  :basic_auth => auth,
48
- :headers => headers
51
+ :headers => headers
49
52
  }
50
53
  )
51
54
 
@@ -75,9 +78,15 @@ module Aquatone
75
78
  end
76
79
 
77
80
  def next_page?(page, body)
78
- page <= PAGES_TO_PROCESS && body["metadata"]["pages"] && API_RESULTS_PER_PAGE * page < body["metadata"]["count"].to_i
81
+ page <= pages_to_process && body["metadata"]["pages"] && API_RESULTS_PER_PAGE * page < body["metadata"]["count"].to_i
79
82
  end
80
83
 
84
+ def pages_to_process
85
+ if has_cli_option?("censys-pages")
86
+ return get_cli_option("censys-pages").to_i
87
+ end
88
+ DEFAULT_PAGES_TO_PROCESS
89
+ end
81
90
  end
82
91
  end
83
92
  end
@@ -4,13 +4,25 @@ module Aquatone
4
4
  self.meta = {
5
5
  :name => "Dictionary",
6
6
  :author => "Michael Henriksen (@michenriksen)",
7
- :description => "Uses a dictionary to find hostnames"
7
+ :description => "Uses a dictionary to find hostnames",
8
+ :cli_options => {
9
+ "wordlist WORDLIST" => "OPTIONAL: wordlist/dictionary file to use for subdomain bruteforcing"
10
+ }
8
11
  }
9
12
 
10
- DICTIONARY = File.join(Aquatone::AQUATONE_ROOT, "subdomains.lst").freeze
13
+ DEFAULT_DICTIONARY = File.join(Aquatone::AQUATONE_ROOT, "subdomains.lst").freeze
11
14
 
12
15
  def run
13
- dictionary = File.open(DICTIONARY, "r")
16
+ if has_cli_option?("wordlist")
17
+ file = File.expand_path(get_cli_option("wordlist"))
18
+ if !File.readable?(file)
19
+ failure("Wordlist file #{file} is not readable or does not exist")
20
+ end
21
+ dictionary = File.open(file, "r")
22
+ else
23
+ dictionary = File.open(DEFAULT_DICTIONARY, "r")
24
+ end
25
+
14
26
  dictionary.each_line do |subdomain|
15
27
  add_host("#{subdomain.strip}.#{domain.name}")
16
28
  end
@@ -5,15 +5,18 @@ module Aquatone
5
5
  :name => "Google Transparency Report",
6
6
  :author => "Michael Henriksen (@michenriksen)",
7
7
  :description => "Uses Google Transparency Report to find hostnames",
8
- :slug => "gtr"
8
+ :slug => "gtr",
9
+ :cli_options => {
10
+ "gtr-pages PAGES" => "Number of Google Transparency Report pages to process (default: 30)"
11
+ }
9
12
  }
10
13
 
11
- BASE_URI = "https://www.google.com/transparencyreport/jsonp/ct/search"
12
- PAGES_TO_PROCESS = 30.freeze
14
+ BASE_URI = "https://www.google.com/transparencyreport/jsonp/ct/search"
15
+ DEFAULT_PAGES_TO_PROCESS = 30.freeze
13
16
 
14
17
  def run
15
18
  token = nil
16
- PAGES_TO_PROCESS.times do
19
+ pages_to_process.times do
17
20
  response = parse_response(request_page(token))
18
21
  response["results"].each do |result|
19
22
  host = result["subject"]
@@ -53,6 +56,13 @@ module Aquatone
53
56
  return false unless host.end_with?(".#{domain.name}")
54
57
  true
55
58
  end
59
+
60
+ def pages_to_process
61
+ if has_cli_option?("gtr-pages")
62
+ return get_cli_option("gtr-pages").to_i
63
+ end
64
+ DEFAULT_PAGES_TO_PROCESS
65
+ end
56
66
  end
57
67
  end
58
68
  end
@@ -4,18 +4,21 @@ module Aquatone
4
4
  self.meta = {
5
5
  :name => "Netcraft",
6
6
  :author => "Michael Henriksen (@michenriksen)",
7
- :description => "Uses searchdns.netcraft.com to find hostnames"
7
+ :description => "Uses searchdns.netcraft.com to find hostnames",
8
+ :cli_options => {
9
+ "netcraft-pages PAGES" => "Number of Netcraft pages to process (default: 10)"
10
+ }
8
11
  }
9
12
 
10
- BASE_URI = "http://searchdns.netcraft.com/".freeze
11
- HOSTNAME_REGEX = /<a href="http:\/\/(.*?)\/" rel="nofollow">/.freeze
12
- RESULTS_PER_PAGE = 20.freeze
13
- PAGES_TO_PROCESS = 10.freeze
13
+ BASE_URI = "http://searchdns.netcraft.com/".freeze
14
+ HOSTNAME_REGEX = /<a href="http:\/\/(.*?)\/" rel="nofollow">/.freeze
15
+ RESULTS_PER_PAGE = 20.freeze
16
+ DEFAULT_PAGES_TO_PROCESS = 10.freeze
14
17
 
15
18
  def run
16
19
  last = nil
17
20
  count = 0
18
- PAGES_TO_PROCESS.times do |i|
21
+ pages_to_process.times do |i|
19
22
  page = i + 1
20
23
  if page == 1
21
24
  uri = "#{BASE_URI}/?restriction=site+contains&host=*.#{url_escape(domain.name)}&lookup=wait..&position=limited"
@@ -43,6 +46,13 @@ module Aquatone
43
46
  end
44
47
  hosts
45
48
  end
49
+
50
+ def pages_to_process
51
+ if has_cli_option?("netcraft-pages")
52
+ return get_cli_option("netcraft-pages").to_i
53
+ end
54
+ DEFAULT_PAGES_TO_PROCESS
55
+ end
46
56
  end
47
57
  end
48
58
  end
@@ -5,12 +5,15 @@ module Aquatone
5
5
  :name => "Shodan",
6
6
  :author => "Michael Henriksen (@michenriksen)",
7
7
  :description => "Uses the Shodan API to find hostnames",
8
- :require_keys => ["shodan"]
8
+ :require_keys => ["shodan"],
9
+ :cli_options => {
10
+ "shodan-pages PAGES" => "Number of Shodan API pages to process (default: 10)"
11
+ }
9
12
  }
10
13
 
11
- API_BASE_URI = "https://api.shodan.io/shodan".freeze
12
- API_RESULTS_PER_PAGE = 100.freeze
13
- PAGES_TO_PROCESS = 10.freeze
14
+ API_BASE_URI = "https://api.shodan.io/shodan".freeze
15
+ API_RESULTS_PER_PAGE = 100.freeze
16
+ DEFAULT_PAGES_TO_PROCESS = 10.freeze
14
17
 
15
18
  def run
16
19
  request_shodan_page
@@ -38,7 +41,14 @@ module Aquatone
38
41
  end
39
42
 
40
43
  def next_page?(page, body)
41
- page <= PAGES_TO_PROCESS && body["total"] && API_RESULTS_PER_PAGE * page < body["total"].to_i
44
+ page <= pages_to_process && body["total"] && API_RESULTS_PER_PAGE * page < body["total"].to_i
45
+ end
46
+
47
+ def pages_to_process
48
+ if has_cli_option?("shodan-pages")
49
+ return get_cli_option("shodan-pages").to_i
50
+ end
51
+ DEFAULT_PAGES_TO_PROCESS
42
52
  end
43
53
  end
44
54
  end
@@ -77,7 +77,7 @@ module Aquatone
77
77
  next if skip_collector?(collector)
78
78
  output("Running collector: #{bold(collector.meta[:name])}... ")
79
79
  begin
80
- collector_instance = collector.new(@domain)
80
+ collector_instance = collector.new(@domain, options)
81
81
  hosts = collector_instance.execute!
82
82
  output("Done (#{hosts.count} #{hosts.count == 1 ? 'host' : 'hosts'})\n")
83
83
  @hosts += hosts
@@ -1,3 +1,3 @@
1
1
  module Aquatone
2
- VERSION = "0.3.0".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aquatone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Henriksen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-21 00:00:00.000000000 Z
11
+ date: 2017-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty