yawast 0.7.0.beta1 → 0.7.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/CHANGELOG.md +5 -1
  4. data/Gemfile +2 -2
  5. data/README.md +8 -1
  6. data/Rakefile +1 -1
  7. data/bin/yawast +8 -0
  8. data/lib/commands/cms.rb +2 -0
  9. data/lib/commands/dns.rb +3 -3
  10. data/lib/commands/head.rb +2 -0
  11. data/lib/commands/scan.rb +2 -0
  12. data/lib/commands/ssl.rb +2 -0
  13. data/lib/commands/utils.rb +5 -3
  14. data/lib/scanner/core.rb +34 -26
  15. data/lib/scanner/generic.rb +33 -130
  16. data/lib/scanner/plugins/applications/cms/generic.rb +20 -0
  17. data/lib/scanner/plugins/applications/generic/password_reset.rb +180 -0
  18. data/lib/scanner/plugins/dns/caa.rb +30 -12
  19. data/lib/scanner/plugins/dns/generic.rb +38 -1
  20. data/lib/scanner/plugins/http/directory_search.rb +14 -12
  21. data/lib/scanner/plugins/http/file_presence.rb +21 -13
  22. data/lib/scanner/plugins/http/generic.rb +95 -0
  23. data/lib/scanner/plugins/servers/apache.rb +23 -23
  24. data/lib/scanner/plugins/servers/generic.rb +25 -0
  25. data/lib/scanner/plugins/servers/iis.rb +6 -6
  26. data/lib/scanner/plugins/servers/nginx.rb +3 -1
  27. data/lib/scanner/plugins/servers/python.rb +3 -1
  28. data/lib/scanner/plugins/spider/spider.rb +7 -7
  29. data/lib/scanner/plugins/ssl/ssl.rb +14 -14
  30. data/lib/scanner/plugins/ssl/ssl_labs/analyze.rb +14 -13
  31. data/lib/scanner/plugins/ssl/ssl_labs/info.rb +6 -4
  32. data/lib/scanner/plugins/ssl/sweet32.rb +68 -63
  33. data/lib/scanner/ssl.rb +33 -36
  34. data/lib/scanner/ssl_labs.rb +373 -110
  35. data/lib/scanner/vuln_scan.rb +27 -0
  36. data/lib/shared/http.rb +31 -27
  37. data/lib/shared/output.rb +7 -15
  38. data/lib/shared/uri.rb +14 -14
  39. data/lib/string_ext.rb +10 -4
  40. data/lib/uri_ext.rb +1 -1
  41. data/lib/util.rb +28 -0
  42. data/lib/version.rb +3 -1
  43. data/lib/yawast.rb +12 -2
  44. data/test/data/ssl_labs_analyze_data_cam_hmhreservations_com.json +1933 -0
  45. data/test/test_scan_cms.rb +2 -2
  46. data/test/test_ssl_labs_analyze.rb +15 -0
  47. data/yawast.gemspec +8 -5
  48. metadata +75 -28
  49. data/lib/scanner/cms.rb +0 -14
  50. data/lib/scanner/php.rb +0 -19
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'securerandom'
2
4
 
3
5
  module Yawast
@@ -6,10 +8,10 @@ module Yawast
6
8
  module Http
7
9
  class FilePresence
8
10
  def self.check_path(uri, path, vuln)
9
- #note: this only checks directly at the root, I'm not sure if this is what we want
11
+ # note: this only checks directly at the root, I'm not sure if this is what we want
10
12
  # should probably be relative to what's passed in, instead of overriding the path.
11
13
  check = uri.copy
12
- check.path = "#{path}"
14
+ check.path = path.to_s
13
15
  code = Yawast::Shared::Http.get_status_code(check)
14
16
 
15
17
  if code == '200'
@@ -26,7 +28,7 @@ module Yawast
26
28
  end
27
29
 
28
30
  def self.check_all(uri, common_files)
29
- #first, we need to see if the site responds to 404 in a reasonable way
31
+ # first, we need to see if the site responds to 404 in a reasonable way
30
32
  unless Yawast::Shared::Http.check_not_found(uri, true)
31
33
  puts 'Site does not respond properly to non-existent file requests; skipping some checks.'
32
34
 
@@ -41,6 +43,7 @@ module Yawast
41
43
  check_elmah_axd uri
42
44
  check_readme_html uri
43
45
  check_release_notes_txt uri
46
+ check_change_log_txt uri
44
47
 
45
48
  if common_files
46
49
  puts ''
@@ -69,7 +72,7 @@ module Yawast
69
72
  end
70
73
 
71
74
  def self.check_wsftp_log(uri)
72
- #check both upper and lower, as they are both seen in the wild
75
+ # check both upper and lower, as they are both seen in the wild
73
76
  check_path(uri, '/WS_FTP.LOG', false)
74
77
  check_path(uri, '/ws_ftp.log', false)
75
78
  end
@@ -91,6 +94,11 @@ module Yawast
91
94
  check_path(uri, '/docs/RELEASE-NOTES.txt', false)
92
95
  end
93
96
 
97
+ def self.check_change_log_txt(uri)
98
+ check_path(uri, '/CHANGELOG.txt', false)
99
+ check_path(uri, '/core/CHANGELOG.txt', false)
100
+ end
101
+
94
102
  def self.check_common(uri)
95
103
  begin
96
104
  @search_list = []
@@ -105,7 +113,7 @@ module Yawast
105
113
  @jobs = Queue.new
106
114
  @results = Queue.new
107
115
 
108
- #load the queue, starting at /
116
+ # load the queue, starting at /
109
117
  base = uri.copy
110
118
  base.path = '/'
111
119
  load_queue base
@@ -125,19 +133,19 @@ module Yawast
125
133
  results = Thread.new do
126
134
  begin
127
135
  while true
128
- if @results.length > 0
136
+ if @results.length.positive?
129
137
  out = @results.pop(true)
130
138
  Yawast::Utilities.puts_info out
131
139
  end
132
140
  end
133
- rescue ThreadError
134
- #do nothing
141
+ rescue ThreadError # rubocop:disable Lint/HandleExceptions
142
+ # do nothing
135
143
  end
136
144
  end
137
145
 
138
146
  workers.map(&:join)
139
147
  results.terminate
140
- rescue => e
148
+ rescue => e # rubocop:disable Style/RescueStandardError
141
149
  Yawast::Utilities.puts_error "Error searching for files (#{e.message})"
142
150
  end
143
151
  end
@@ -149,10 +157,10 @@ module Yawast
149
157
  begin
150
158
  check.path = "/#{line}"
151
159
 
152
- #add the job to the queue
160
+ # add the job to the queue
153
161
  @jobs.push check
154
- rescue
155
- #who cares
162
+ rescue # rubocop:disable Lint/HandleExceptions
163
+ # who cares
156
164
  end
157
165
  end
158
166
  end
@@ -165,7 +173,7 @@ module Yawast
165
173
  @results.push "'#{uri.path}' found: #{uri}"
166
174
  Yawast::Shared::Output.log_append_value 'http', 'http_file', uri
167
175
  end
168
- rescue => e
176
+ rescue => e # rubocop:disable Style/RescueStandardError
169
177
  unless e.message.include?('end of file') || e.message.include?('getaddrinfo')
170
178
  Yawast::Utilities.puts_error "Error searching for file '#{uri.path}' (#{e.message})"
171
179
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yawast
4
+ module Scanner
5
+ module Plugins
6
+ module Http
7
+ class Generic
8
+ def self.check_propfind(uri)
9
+ begin
10
+ req = Yawast::Shared::Http.get_http(uri)
11
+ req.use_ssl = uri.scheme == 'https'
12
+ headers = Yawast::Shared::Http.get_headers
13
+ res = req.request(Propfind.new('/', headers))
14
+
15
+ if res.code.to_i <= 400 && res.body.length.positive? && res['Content-Type'] == 'text/xml'
16
+ Yawast::Utilities.puts_warn 'Possible Info Disclosure: PROPFIND Enabled'
17
+ puts "\t\t\"curl -X PROPFIND #{uri}\""
18
+
19
+ puts ''
20
+ end
21
+
22
+ Yawast::Shared::Output.log_value 'http', 'propfind', 'raw', res.body
23
+ Yawast::Shared::Output.log_value 'http', 'propfind', 'code', res.code
24
+ Yawast::Shared::Output.log_value 'http', 'propfind', 'content-type', res['Content-Type']
25
+ Yawast::Shared::Output.log_value 'http', 'propfind', 'length', res.body.length
26
+ end
27
+ end
28
+
29
+ def self.check_trace(uri)
30
+ begin
31
+ req = Yawast::Shared::Http.get_http(uri)
32
+ req.use_ssl = uri.scheme == 'https'
33
+ headers = Yawast::Shared::Http.get_headers
34
+ res = req.request(Trace.new('/', headers))
35
+
36
+ if res.body.include?('TRACE / HTTP/1.1') && res.code == '200'
37
+ Yawast::Utilities.puts_warn 'HTTP TRACE Enabled'
38
+ puts "\t\t\"curl -X TRACE #{uri}\""
39
+
40
+ puts ''
41
+ end
42
+
43
+ Yawast::Shared::Output.log_value 'http', 'trace', 'raw', res.body
44
+ Yawast::Shared::Output.log_value 'http', 'trace', 'code', res.code
45
+ end
46
+ end
47
+
48
+ def self.check_options(uri)
49
+ begin
50
+ req = Yawast::Shared::Http.get_http(uri)
51
+ req.use_ssl = uri.scheme == 'https'
52
+ headers = Yawast::Shared::Http.get_headers
53
+ res = req.request(Options.new('/', headers))
54
+
55
+ unless res['Public'].nil?
56
+ Yawast::Utilities.puts_info "Public HTTP Verbs (OPTIONS): #{res['Public']}"
57
+ Yawast::Shared::Output.log_value 'http', 'options', 'public', res['Public']
58
+
59
+ puts ''
60
+ end
61
+
62
+ unless res['Allow'].nil?
63
+ Yawast::Utilities.puts_info "Allow HTTP Verbs (OPTIONS): #{res['Allow']}"
64
+ Yawast::Shared::Output.log_value 'http', 'options', 'allow', res['Allow']
65
+
66
+ puts ''
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # Custom class to allow using the PROPFIND verb
73
+ class Propfind < Net::HTTPRequest
74
+ METHOD = 'PROPFIND'
75
+ REQUEST_HAS_BODY = false
76
+ RESPONSE_HAS_BODY = true
77
+ end
78
+
79
+ # Custom class to allow using the TRACE verb
80
+ class Trace < Net::HTTPRequest
81
+ METHOD = 'TRACE'
82
+ REQUEST_HAS_BODY = false
83
+ RESPONSE_HAS_BODY = true
84
+ end
85
+
86
+ # Custom class to allow using the OPTIONS verb
87
+ class Options < Net::HTTPRequest
88
+ METHOD = 'OPTIONS'
89
+ REQUEST_HAS_BODY = false
90
+ RESPONSE_HAS_BODY = true
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
  require 'securerandom'
3
5
 
@@ -7,30 +9,30 @@ module Yawast
7
9
  module Servers
8
10
  class Apache
9
11
  def self.check_banner(banner)
10
- #don't bother if this doesn't look like Apache
12
+ # don't bother if this doesn't look like Apache
11
13
  return unless banner.include? 'Apache'
12
14
  @apache = true
13
15
 
14
16
  modules = banner.split(' ')
15
17
  server = modules[0]
16
18
 
17
- #fix '(distro)' issue, such as with 'Apache/2.2.22 (Ubuntu)'
19
+ # fix '(distro)' issue, such as with 'Apache/2.2.22 (Ubuntu)'
18
20
  # if we don't do this, it triggers a false positive on the module check
19
- if /\(\w*\)/.match modules[1]
21
+ if /\(\w*\)/.match? modules[1]
20
22
  server += " #{modules[1]}"
21
23
  modules.delete_at 1
22
24
  end
23
25
 
24
- #print the server info no matter what we do next
26
+ # print the server info no matter what we do next
25
27
  Yawast::Utilities.puts_info "Apache Server: #{server}"
26
28
  modules.delete_at 0
27
29
 
28
- if modules.count > 0
30
+ if modules.count.positive?
29
31
  Yawast::Utilities.puts_warn 'Apache Server: Module listing enabled'
30
32
  modules.each { |mod| Yawast::Utilities.puts_warn "\t\t#{mod}" }
31
33
  puts ''
32
34
 
33
- #check for special items
35
+ # check for special items
34
36
  modules.each do |mod|
35
37
  if mod.include? 'OpenSSL'
36
38
  Yawast::Utilities.puts_warn "OpenSSL Version Disclosure: #{mod}"
@@ -41,7 +43,7 @@ module Yawast
41
43
  end
42
44
 
43
45
  def self.check_all(uri)
44
- #run all the defined checks
46
+ # run all the defined checks
45
47
  check_server_status(uri.copy)
46
48
  check_server_info(uri.copy)
47
49
  check_tomcat_manager(uri.copy)
@@ -65,14 +67,14 @@ module Yawast
65
67
  headers = Yawast::Shared::Http.get_headers
66
68
  res = req.request(Xyz.new('/', headers))
67
69
 
68
- if res.body != nil && res.body.include?('Apache Tomcat') && res.code == '501'
69
- #check to see if there's a version number
70
+ if !res.body.nil? && res.body.include?('Apache Tomcat') && res.code == '501'
71
+ # check to see if there's a version number
70
72
  version = /Apache Tomcat\/\d*.\d*.\d*\b/.match res.body
71
73
 
72
- if version != nil && version[0] != nil
74
+ if !version.nil? && !version[0].nil?
73
75
  Yawast::Utilities.puts_warn "Apache Tomcat Version Found: #{version[0]}"
74
76
  Yawast::Shared::Output.log_value 'apache', 'tomcat_version', version[0]
75
-
77
+
76
78
  puts "\t\t\"curl -X XYZ #{uri}\""
77
79
 
78
80
  puts ''
@@ -88,19 +90,19 @@ module Yawast
88
90
 
89
91
  def self.check_tomcat_manager_paths(uri, base_path, manager)
90
92
  uri.path = "/#{base_path}/html"
91
- uri.query = '' if uri.query != nil
93
+ uri.query = '' unless uri.query.nil?
92
94
 
93
95
  ret = Yawast::Shared::Http.get(uri)
94
96
 
95
97
  if ret.include? '<tt>conf/tomcat-users.xml</tt>'
96
- #this will get Tomcat 7+
98
+ # this will get Tomcat 7+
97
99
  Yawast::Utilities.puts_warn "Apache Tomcat #{manager} page found: #{uri}"
98
100
  Yawast::Shared::Output.log_value 'apache', 'tomcat_mgr', manager, uri
99
101
  check_tomcat_manager_passwords uri, manager
100
102
 
101
103
  puts ''
102
104
  else
103
- #check for Tomcat 6 and below
105
+ # check for Tomcat 6 and below
104
106
  uri = uri.copy
105
107
  uri.path = "/#{base_path}"
106
108
  ret = Yawast::Shared::Http.get(uri)
@@ -116,7 +118,7 @@ module Yawast
116
118
  end
117
119
 
118
120
  def self.check_tomcat_manager_passwords(uri, manager)
119
- #check for known passwords
121
+ # check for known passwords
120
122
  check_tomcat_manager_pwd_check uri, manager, 'tomcat:tomcat'
121
123
  check_tomcat_manager_pwd_check uri, manager, 'tomcat:password'
122
124
  check_tomcat_manager_pwd_check uri, manager, 'tomcat:'
@@ -128,7 +130,7 @@ module Yawast
128
130
  def self.check_tomcat_manager_pwd_check(uri, manager, credentials)
129
131
  ret = Yawast::Shared::Http.get(uri, {'Authorization' => "Basic #{Base64.encode64(credentials)}"})
130
132
  if ret.include?('<font size="+2">Tomcat Web Application Manager</font>') ||
131
- ret.include?('<font size="+2">Tomcat Virtual Host Manager</font>')
133
+ ret.include?('<font size="+2">Tomcat Virtual Host Manager</font>')
132
134
  Yawast::Utilities.puts_vuln "Apache Tomcat #{manager} weak password: #{credentials}"
133
135
 
134
136
  Yawast::Shared::Output.log_value 'apache', 'tomcat_mgr_pwd', uri, credentials
@@ -138,7 +140,7 @@ module Yawast
138
140
  def self.check_tomcat_put_rce(uri)
139
141
  # CVE-2017-12615
140
142
  uri.path = "/#{SecureRandom.hex}.jsp/"
141
- uri.query = '' if uri.query != nil
143
+ uri.query = '' unless uri.query.nil?
142
144
 
143
145
  Yawast::Shared::Output.log_value 'apache', 'cve_2017_12615', 'path', uri
144
146
 
@@ -166,7 +168,7 @@ module Yawast
166
168
  end
167
169
 
168
170
  def self.check_struts2_samples(uri)
169
- search = Array.new
171
+ search = []
170
172
  search.push '/Struts2XMLHelloWorld/User/home.action'
171
173
  search.push '/struts2-showcase/showcase.action'
172
174
  search.push '/struts2-showcase/titles/index.action'
@@ -182,15 +184,13 @@ module Yawast
182
184
  ret = Yawast::Shared::Http.get_status_code uri
183
185
  Yawast::Shared::Output.log_value 'apache', 'struts2_sample_files', uri, ret
184
186
 
185
- if ret == 200
186
- Yawast::Utilities.puts_warn "Apache Struts2 Sample Files: #{uri}"
187
- end
187
+ Yawast::Utilities.puts_warn "Apache Struts2 Sample Files: #{uri}" if ret == 200
188
188
  end
189
189
  end
190
190
 
191
191
  def self.check_page_for_string(uri, path, search)
192
192
  uri.path = path
193
- uri.query = '' if uri.query != nil
193
+ uri.query = '' unless uri.query.nil?
194
194
 
195
195
  ret = Yawast::Shared::Http.get(uri)
196
196
 
@@ -202,7 +202,7 @@ module Yawast
202
202
  end
203
203
  end
204
204
 
205
- #Custom class to allow using the XYZ verb
205
+ # Custom class to allow using the XYZ verb
206
206
  class Xyz < Net::HTTPRequest
207
207
  METHOD = 'XYZ'
208
208
  REQUEST_HAS_BODY = false
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yawast
4
+ module Scanner
5
+ module Plugins
6
+ module Servers
7
+ class Generic
8
+ def self.check_banner_php(banner)
9
+ # don't bother if this doesn't include PHP
10
+ return unless banner.include? 'PHP/'
11
+
12
+ modules = banner.split(' ')
13
+
14
+ modules.each do |mod|
15
+ if mod.include? 'PHP/'
16
+ Yawast::Utilities.puts_warn "PHP Version: #{mod}"
17
+ puts ''
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Yawast
2
4
  module Scanner
3
5
  module Plugins
4
6
  module Servers
5
7
  class Iis
6
8
  def self.check_banner(banner)
7
- #don't bother if this doesn't include IIS
9
+ # don't bother if this doesn't include IIS
8
10
  return unless banner.include? 'Microsoft-IIS/'
9
11
  @iis = true
10
12
 
@@ -13,7 +15,7 @@ module Yawast
13
15
  end
14
16
 
15
17
  def self.check_all(uri, head)
16
- #run all the defined checks
18
+ # run all the defined checks
17
19
  check_asp_banner(head)
18
20
  check_mvc_version(head)
19
21
  check_asp_net_debug(uri)
@@ -45,9 +47,7 @@ module Yawast
45
47
  headers['Accept'] = '*/*'
46
48
  res = req.request(Debug.new('/', headers))
47
49
 
48
- if res.code == 200
49
- Yawast::Utilities.puts_vuln 'ASP.NET Debugging Enabled'
50
- end
50
+ Yawast::Utilities.puts_vuln 'ASP.NET Debugging Enabled' if res.code == 200
51
51
 
52
52
  Yawast::Shared::Output.log_value 'http', 'asp_net_debug', 'raw', res.body
53
53
  Yawast::Shared::Output.log_value 'http', 'asp_net_debug', 'code', res.code
@@ -55,7 +55,7 @@ module Yawast
55
55
  end
56
56
  end
57
57
 
58
- #Custom class to allow using the DEBUG verb
58
+ # Custom class to allow using the DEBUG verb
59
59
  class Debug < Net::HTTPRequest
60
60
  METHOD = 'DEBUG'
61
61
  REQUEST_HAS_BODY = false
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Yawast
2
4
  module Scanner
3
5
  module Plugins
4
6
  module Servers
5
7
  class Nginx
6
8
  def self.check_banner(banner)
7
- #don't bother if this doesn't include nginx
9
+ # don't bother if this doesn't include nginx
8
10
  return unless banner.include? 'nginx/'
9
11
 
10
12
  Yawast::Utilities.puts_warn "nginx Version: #{banner}"
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Yawast
2
4
  module Scanner
3
5
  module Plugins
4
6
  module Servers
5
7
  class Python
6
8
  def self.check_banner(banner)
7
- #don't bother if this doesn't include Python
9
+ # don't bother if this doesn't include Python
8
10
  return unless banner.include? 'Python/'
9
11
 
10
12
  Yawast::Utilities.puts_warn "Python Version: #{banner}"