aquatone 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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