aquatone 0.1.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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/CHANGELOG.md +18 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +168 -0
  7. data/Rakefile +10 -0
  8. data/aquatone.gemspec +29 -0
  9. data/aquatone.js +164 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/aquatone-discover +129 -0
  13. data/exe/aquatone-gather +55 -0
  14. data/exe/aquatone-scan +76 -0
  15. data/lib/aquatone.rb +43 -0
  16. data/lib/aquatone/assessment.rb +40 -0
  17. data/lib/aquatone/browser.rb +18 -0
  18. data/lib/aquatone/browser/drivers/nightmare.rb +52 -0
  19. data/lib/aquatone/collector.rb +106 -0
  20. data/lib/aquatone/collectors/dictionary.rb +20 -0
  21. data/lib/aquatone/collectors/dnsdb.rb +45 -0
  22. data/lib/aquatone/collectors/gtr.rb +58 -0
  23. data/lib/aquatone/collectors/hackertarget.rb +24 -0
  24. data/lib/aquatone/collectors/netcraft.rb +48 -0
  25. data/lib/aquatone/collectors/shodan.rb +45 -0
  26. data/lib/aquatone/collectors/threatcrowd.rb +25 -0
  27. data/lib/aquatone/collectors/virustotal.rb +24 -0
  28. data/lib/aquatone/command.rb +152 -0
  29. data/lib/aquatone/commands/discover.rb +187 -0
  30. data/lib/aquatone/commands/gather.rb +167 -0
  31. data/lib/aquatone/commands/scan.rb +108 -0
  32. data/lib/aquatone/domain.rb +33 -0
  33. data/lib/aquatone/http_client.rb +5 -0
  34. data/lib/aquatone/key_store.rb +72 -0
  35. data/lib/aquatone/port_lists.rb +36 -0
  36. data/lib/aquatone/report.rb +88 -0
  37. data/lib/aquatone/resolver.rb +47 -0
  38. data/lib/aquatone/thread_pool.rb +31 -0
  39. data/lib/aquatone/url_maker.rb +27 -0
  40. data/lib/aquatone/validation.rb +22 -0
  41. data/lib/aquatone/version.rb +3 -0
  42. data/subdomains.lst +8214 -0
  43. data/templates/default.html.erb +225 -0
  44. metadata +159 -0
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "aquatone"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "aquatone"
4
+
5
+ options = {
6
+ :fallback_nameservers => %w(8.8.8.8 8.8.4.4),
7
+ :threads => 5,
8
+ :ignore_private => true
9
+ }
10
+
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: aquatone-discover OPTIONS"
13
+
14
+ opts.on("-d", "--domain DOMAIN", "Domain name to assess") do |v|
15
+ if !Aquatone::Validation.valid_domain_name?(v)
16
+ puts "#{v} doesn't look like a valid domain name."
17
+ exit 1
18
+ end
19
+ options[:domain] = v
20
+ end
21
+
22
+ opts.on("--nameservers NAMESERVERS", "Nameservers to use") do |v|
23
+ ips = v.split(",").map(&:strip).uniq
24
+ if ips.empty?
25
+ puts "Nameservers can't be empty."
26
+ exit 1
27
+ end
28
+ ips.each do |ip|
29
+ if !Aquatone::Validation.valid_ip?(ip)
30
+ puts "#{ip} is not a valid IP address."
31
+ exit 1
32
+ end
33
+ end
34
+ options[:nameservers] = ips
35
+ end
36
+
37
+ opts.on("--fallback-nameservers NAMESERVERS", "Nameservers to fall back to") do |v|
38
+ ips = v.split(",").map(&:strip).uniq
39
+ if ips.empty?
40
+ puts "Fallback nameservers can't be empty."
41
+ exit 1
42
+ end
43
+ ips.each do |ip|
44
+ if !Aquatone::Validation.valid_ip?(ip)
45
+ puts "#{ip} is not a valid IP address."
46
+ exit 1
47
+ end
48
+ end
49
+ options[:fallback_nameservers] = ips
50
+ end
51
+
52
+ opts.on("--[no-]ignore-private", "Ignore hosts resolving to private IP addresses") do |v|
53
+ options[:ignore_private] = v
54
+ end
55
+
56
+ opts.on("--set-key KEY VALUE", "Save a key to key store") do |key|
57
+ if ARGV.empty?
58
+ puts "No key value given."
59
+ exit 1
60
+ end
61
+ key = key.to_s.downcase
62
+ value = ARGV.join(" ").strip
63
+ Aquatone::KeyStore.set(key, value)
64
+ puts "Saved key #{key} with value #{value}."
65
+ exit
66
+ end
67
+
68
+ opts.on("--list-collectors", "See information on collectors") do
69
+ Aquatone::Collector.descendants.each do |collector|
70
+ puts "Name.......: #{collector.meta[:name]}"
71
+ puts "Description: #{collector.meta[:description]}" if collector.meta[:description]
72
+ puts "Author.....: #{collector.meta[:author]}"
73
+ puts "Key........: #{collector.sluggified_name}\n\n"
74
+ puts "--------------------------------------------------\n\n"
75
+ end
76
+ exit
77
+ end
78
+
79
+ opts.on("--only-collectors COLLECTORS", "Only run specified collectors") do |v|
80
+ known_collectors = Aquatone::Collector.descendants.map(&:sluggified_name)
81
+ collectors = v.split(",").map(&:strip).uniq
82
+ collectors.each do |collector|
83
+ if !known_collectors.include?(collector)
84
+ puts "Unknown collector key: #{collector}"
85
+ exit 1
86
+ end
87
+ end
88
+ options[:only_collectors] = collectors
89
+ end
90
+
91
+ opts.on("--disable-collectors COLLECTORS", "Disable specified collectors") do |v|
92
+ known_collectors = Aquatone::Collector.descendants.map(&:sluggified_name)
93
+ collectors = v.split(",").map(&:strip).uniq
94
+ collectors.each do |collector|
95
+ if !known_collectors.include?(collector)
96
+ puts "Unknown collector key: #{collector}"
97
+ exit 1
98
+ end
99
+ end
100
+ options[:disable_collectors] = collectors
101
+ end
102
+
103
+ opts.on("-t", "--threads THREADS", "Number of concurrent threads to use") do |v|
104
+ options[:threads] = v.to_i
105
+ end
106
+
107
+ opts.on("-s", "--sleep SECONDS", "Seconds to sleep between lookups") do |v|
108
+ if v.to_i < 1
109
+ puts "Sleep can't be less than 1"
110
+ exit 1
111
+ end
112
+ options[:sleep] = v.to_i
113
+ end
114
+
115
+ opts.on("-j", "--jitter PERCENTAGE", "Jitter factor for sleep intervals") do |v|
116
+ if !v.to_i.between?(1, 100)
117
+ puts "Jitter factor must be between 1 and 100"
118
+ exit 1
119
+ end
120
+ options[:jitter] = v.to_f
121
+ end
122
+
123
+ opts.on("-h", "--help", "Show help") do
124
+ puts opts
125
+ exit 0
126
+ end
127
+ end.parse!
128
+
129
+ Aquatone::Commands::Discover.run(options)
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "aquatone"
4
+
5
+ options = {
6
+ :threads => 5,
7
+ :timeout => 15
8
+ }
9
+
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: aquatone-gather OPTIONS"
12
+
13
+ opts.on("-d", "--domain DOMAIN", "Domain name to assess") do |v|
14
+ if !Aquatone::Validation.valid_domain_name?(v)
15
+ puts "#{v} doesn't look like a valid domain name."
16
+ exit 1
17
+ end
18
+ options[:domain] = v
19
+ end
20
+
21
+ opts.on("-t", "--threads THREADS", "Number of concurrent threads to use") do |v|
22
+ options[:threads] = v.to_i
23
+ end
24
+
25
+ opts.on("--timeout SECONDS", "Timeout for page renderings") do |v|
26
+ if v.to_i < 1
27
+ puts "Timeout must be at least 1 second"
28
+ exit 1
29
+ end
30
+ options[:timeout] = v.to_i
31
+ end
32
+
33
+ opts.on("-s", "--sleep SECONDS", "Seconds to sleep between lookups") do |v|
34
+ if v.to_i < 1
35
+ puts "Sleep can't be less than 1"
36
+ exit 1
37
+ end
38
+ options[:sleep] = v.to_i
39
+ end
40
+
41
+ opts.on("-j", "--jitter PERCENTAGE", "Jitter factor for sleep intervals") do |v|
42
+ if !v.to_i.between?(1, 100)
43
+ puts "Jitter factor must be between 1 and 100"
44
+ exit 1
45
+ end
46
+ options[:jitter] = v.to_f
47
+ end
48
+
49
+ opts.on("-h", "--help", "Show help") do
50
+ puts opts
51
+ exit 0
52
+ end
53
+ end.parse!
54
+
55
+ Aquatone::Commands::Gather.run(options)
data/exe/aquatone-scan ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "aquatone"
4
+
5
+ options = {
6
+ :ports => Aquatone::PortLists::MEDIUM,
7
+ :threads => 5,
8
+ :timeout => 0.5
9
+ }
10
+
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: aquatone-scan OPTIONS"
13
+
14
+ opts.on("-d", "--domain DOMAIN", "Domain name to assess") do |v|
15
+ if !Aquatone::Validation.valid_domain_name?(v)
16
+ puts "#{v} doesn't look like a valid domain name."
17
+ exit 1
18
+ end
19
+ options[:domain] = v
20
+ end
21
+
22
+ opts.on("-p", "--ports PORTS", "Ports to scan") do |v|
23
+ if v =~ /\A[\d,\s]{1,}\z/
24
+ ports = v.split(",").map { |p| p.strip.to_i }
25
+ ports.each do |p|
26
+ if !Aquatone::Validation.valid_tcp_port?(p)
27
+ puts "#{p} is not a valid TCP port."
28
+ exit 1
29
+ end
30
+ end
31
+ options[:ports] = ports
32
+ else
33
+ begin
34
+ options[:ports] = Aquatone::PortLists.port_list_by_name(v.downcase)
35
+ rescue Aquatone::PortLists::UnknownPortListName => e
36
+ puts e.message
37
+ exit 1
38
+ end
39
+ end
40
+ end
41
+
42
+ opts.on("--timeout SECONDS", "Timeout in seconds for port probes") do |v|
43
+ if v.to_f <= 0
44
+ puts "Timeout can't be zero"
45
+ exit 1
46
+ end
47
+ options[:timeout] = v.to_f
48
+ end
49
+
50
+ opts.on("-t", "--threads THREADS", "Number of concurrent threads to use") do |v|
51
+ options[:threads] = v.to_i
52
+ end
53
+
54
+ opts.on("-s", "--sleep SECONDS", "Seconds to sleep between port probes") do |v|
55
+ if v.to_i < 1
56
+ puts "Sleep can't be less than 1"
57
+ exit 1
58
+ end
59
+ options[:sleep] = v.to_i
60
+ end
61
+
62
+ opts.on("-j", "--jitter PERCENTAGE", "Jitter factor for sleep intervals") do |v|
63
+ if !v.to_i.between?(1, 100)
64
+ puts "Jitter factor must be between 1 and 100"
65
+ exit 1
66
+ end
67
+ options[:jitter] = v.to_f
68
+ end
69
+
70
+ opts.on("-h", "--help", "Show help") do
71
+ puts opts
72
+ exit 0
73
+ end
74
+ end.parse!
75
+
76
+ Aquatone::Commands::Scan.run(options)
data/lib/aquatone.rb ADDED
@@ -0,0 +1,43 @@
1
+ require "resolv"
2
+ require "ipaddr"
3
+ require "socket"
4
+ require "timeout"
5
+ require "shellwords"
6
+ require "optparse"
7
+
8
+ require "httparty"
9
+ require "childprocess"
10
+
11
+ require "aquatone/version"
12
+ require "aquatone/port_lists"
13
+ require "aquatone/url_maker"
14
+ require "aquatone/validation"
15
+ require "aquatone/thread_pool"
16
+ require "aquatone/http_client"
17
+ require "aquatone/browser"
18
+ require "aquatone/browser/drivers/nightmare"
19
+ require "aquatone/domain"
20
+ require "aquatone/resolver"
21
+ require "aquatone/assessment"
22
+ require "aquatone/report"
23
+ require "aquatone/command"
24
+ require "aquatone/collector"
25
+
26
+ module Aquatone
27
+ AQUATONE_ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..")).freeze
28
+ DEFAULT_AQUATONE_PATH = File.join(Dir.home, "aquatone").freeze
29
+
30
+ def self.aquatone_path
31
+ ENV['AQUATONEPATH'] || DEFAULT_AQUATONE_PATH
32
+ end
33
+ end
34
+
35
+ require "aquatone/key_store"
36
+
37
+ Dir[File.join(File.dirname(__FILE__), "aquatone", "collectors", "*.rb")].each do |collector|
38
+ require collector
39
+ end
40
+
41
+ require "aquatone/commands/discover"
42
+ require "aquatone/commands/scan"
43
+ require "aquatone/commands/gather"
@@ -0,0 +1,40 @@
1
+ module Aquatone
2
+ class Assessment
3
+ attr_reader :domain
4
+
5
+ def initialize(domain)
6
+ @domain = domain
7
+ initialize_assessment_directory
8
+ end
9
+
10
+ def has_file?(name)
11
+ File.exist?(File.join(path, name))
12
+ end
13
+
14
+ def read_file(name)
15
+ File.read(File.join(path, name))
16
+ end
17
+
18
+ def write_file(name, data, mode = "w")
19
+ File.open(File.join(path, name), mode) do |file|
20
+ file.write(data)
21
+ end
22
+ end
23
+
24
+ def make_directory(name)
25
+ dir = File.join(path, name)
26
+ Dir.mkdir(dir) unless Dir.exist?(dir)
27
+ end
28
+
29
+ def path
30
+ File.join(Aquatone.aquatone_path, domain)
31
+ end
32
+
33
+ private
34
+
35
+ def initialize_assessment_directory
36
+ Dir.mkdir(Aquatone.aquatone_path) unless Dir.exist?(Aquatone.aquatone_path)
37
+ Dir.mkdir(path) unless Dir.exist?(path)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ module Aquatone
2
+ class Browser
3
+ def self.visit(url, vhost, html_destination, headers_destination, screenshot_destination, options)
4
+ driver = make_driver(url, vhost, html_destination, headers_destination, screenshot_destination, options)
5
+ visit = driver.visit
6
+ if !visit["success"]
7
+ visit = driver.visit
8
+ end
9
+ visit
10
+ end
11
+
12
+ protected
13
+
14
+ def self.make_driver(url, vhost, html_destination, headers_destination, screenshot_destination, options)
15
+ Aquatone::Browser::Drivers::Nightmare.new(url, vhost, html_destination, headers_destination, screenshot_destination, options)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,52 @@
1
+ module Aquatone
2
+ class Browser
3
+ module Drivers
4
+ class Nightmare
5
+ attr_reader :url, :vhost, :html_destination, :headers_destination, :screenshot_destination, :options
6
+
7
+ def initialize(url, vhost, html_destination, headers_destination, screenshot_destination, options)
8
+ @url = url
9
+ @vhost = vhost
10
+ @html_destination = html_destination
11
+ @headers_destination = headers_destination
12
+ @screenshot_destination = screenshot_destination
13
+ @options = options
14
+ end
15
+
16
+ def visit
17
+ rout, wout = IO.pipe
18
+ process = ChildProcess.build(*construct_command)
19
+ process.cwd = Aquatone::AQUATONE_ROOT
20
+ process.io.stdout = wout
21
+ process.start
22
+ process.poll_for_exit(options[:timeout])
23
+ wout.close
24
+ command_output = rout.readlines.join("\n").strip
25
+ JSON.parse(command_output)
26
+ rescue => e
27
+ process.stop if process
28
+ return {
29
+ "success" => false,
30
+ "error" => e.is_a?(ChildProcess::TimeoutError) ? "Timeout" : "#{e.class}: #{e.message}",
31
+ "code" => 0,
32
+ "details" => ""
33
+ }
34
+ end
35
+
36
+ private
37
+
38
+ def construct_command
39
+ [
40
+ "node",
41
+ File.join(Aquatone::AQUATONE_ROOT, "aquatone.js"),
42
+ Shellwords.escape(url),
43
+ Shellwords.escape(vhost),
44
+ Shellwords.escape(html_destination),
45
+ Shellwords.escape(headers_destination),
46
+ Shellwords.escape(screenshot_destination)
47
+ ]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end