yawast 0.7.0.beta1 → 0.7.0.beta2

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 (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}"