aquatone 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/exe/aquatone-discover +8 -0
- data/lib/aquatone/collector.rb +27 -4
- data/lib/aquatone/collectors/censys.rb +17 -8
- data/lib/aquatone/collectors/dictionary.rb +15 -3
- data/lib/aquatone/collectors/gtr.rb +14 -4
- data/lib/aquatone/collectors/netcraft.rb +16 -6
- data/lib/aquatone/collectors/shodan.rb +15 -5
- data/lib/aquatone/commands/discover.rb +1 -1
- data/lib/aquatone/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8ad3da3d450ecd7fd41c64cd0b18f0a3e3ae00b
|
4
|
+
data.tar.gz: 240fbce3dc353f146a32be28566201b2b62fd2c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 244cc756b2cb1c65d36281fbeae199d290a71434f73a12c96e855995eff2a091aae442212d13ee604ac6b34d6d74196813ff3b01bd5b809b16512c86b438c44e
|
7
|
+
data.tar.gz: b109618403ee14536a67a8e75334e88279578f2eb9532edd8ea772c7bf1e443a8747a01038eadfc44bd5a42000ab90571be269432340eef1257161f6d3d40110
|
data/CHANGELOG.md
CHANGED
@@ -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.
|
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
|
data/exe/aquatone-discover
CHANGED
@@ -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
|
data/lib/aquatone/collector.rb
CHANGED
@@ -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
|
34
|
-
@
|
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
|
-
|
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
|
12
|
-
API_RESULTS_PER_PAGE
|
13
|
-
|
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 <=
|
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
|
-
|
13
|
+
DEFAULT_DICTIONARY = File.join(Aquatone::AQUATONE_ROOT, "subdomains.lst").freeze
|
11
14
|
|
12
15
|
def run
|
13
|
-
|
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
|
12
|
-
|
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
|
-
|
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
|
11
|
-
HOSTNAME_REGEX
|
12
|
-
RESULTS_PER_PAGE
|
13
|
-
|
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
|
-
|
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
|
12
|
-
API_RESULTS_PER_PAGE
|
13
|
-
|
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 <=
|
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
|
data/lib/aquatone/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2017-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|