yawast 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +7 -0
  5. data/README.md +454 -0
  6. data/Rakefile +9 -0
  7. data/bin/yawast +69 -0
  8. data/lib/commands/cms.rb +10 -0
  9. data/lib/commands/head.rb +12 -0
  10. data/lib/commands/scan.rb +11 -0
  11. data/lib/commands/ssl.rb +11 -0
  12. data/lib/commands/utils.rb +36 -0
  13. data/lib/resources/common.txt +1960 -0
  14. data/lib/scanner/apache.rb +72 -0
  15. data/lib/scanner/cms.rb +14 -0
  16. data/lib/scanner/core.rb +95 -0
  17. data/lib/scanner/generic.rb +323 -0
  18. data/lib/scanner/iis.rb +63 -0
  19. data/lib/scanner/nginx.rb +13 -0
  20. data/lib/scanner/obj_presence.rb +63 -0
  21. data/lib/scanner/php.rb +19 -0
  22. data/lib/scanner/ssl.rb +237 -0
  23. data/lib/scanner/ssl_labs.rb +491 -0
  24. data/lib/shared/http.rb +67 -0
  25. data/lib/string_ext.rb +16 -0
  26. data/lib/uri_ext.rb +5 -0
  27. data/lib/util.rb +25 -0
  28. data/lib/yawast.rb +57 -0
  29. data/test/base.rb +43 -0
  30. data/test/data/apache_server_info.txt +486 -0
  31. data/test/data/apache_server_status.txt +184 -0
  32. data/test/data/cms_none_body.txt +242 -0
  33. data/test/data/cms_wordpress_body.txt +467 -0
  34. data/test/data/iis_server_header.txt +13 -0
  35. data/test/data/tomcat_release_notes.txt +172 -0
  36. data/test/data/wordpress_readme_html.txt +86 -0
  37. data/test/test_cmd_util.rb +35 -0
  38. data/test/test_helper.rb +5 -0
  39. data/test/test_object_presence.rb +36 -0
  40. data/test/test_scan_apache_banner.rb +58 -0
  41. data/test/test_scan_apache_server_info.rb +22 -0
  42. data/test/test_scan_apache_server_status.rb +22 -0
  43. data/test/test_scan_cms.rb +27 -0
  44. data/test/test_scan_iis_headers.rb +40 -0
  45. data/test/test_scan_nginx_banner.rb +18 -0
  46. data/test/test_shared_http.rb +40 -0
  47. data/test/test_shared_util.rb +44 -0
  48. data/test/test_string_ext.rb +15 -0
  49. data/test/test_yawast.rb +17 -0
  50. data/yawast.gemspec +35 -0
  51. metadata +283 -0
@@ -0,0 +1,72 @@
1
+ module Yawast
2
+ module Scanner
3
+ class Apache
4
+ def self.check_banner(banner)
5
+ #don't bother if this doesn't look like Apache
6
+ return if !banner.include? 'Apache'
7
+ @apache = true
8
+
9
+ modules = banner.split(' ')
10
+ server = modules[0]
11
+
12
+ #hack - fix '(distro)' issue, such as with 'Apache/2.2.22 (Ubuntu)'
13
+ # if we don't do this, it triggers a false positive on the module check
14
+ if /\(\w*\)/.match modules[1]
15
+ server += " #{modules[1]}"
16
+ modules.delete_at 1
17
+ end
18
+
19
+ #print the server info no matter what we do next
20
+ Yawast::Utilities.puts_info "Apache Server: #{server}"
21
+ modules.delete_at 0
22
+
23
+ if modules.count > 0
24
+ Yawast::Utilities.puts_warn 'Apache Server: Module listing enabled'
25
+ modules.each { |mod| Yawast::Utilities.puts_warn "\t\t#{mod}" }
26
+ puts ''
27
+
28
+ #check for special items
29
+ modules.each do |mod|
30
+ if mod.include? 'OpenSSL'
31
+ Yawast::Utilities.puts_warn "OpenSSL Version Disclosure: #{mod}"
32
+ puts ''
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.check_all(uri, head)
39
+ #this check for @apache may yield false negatives.. meh.
40
+ if @apache
41
+ #run all the defined checks
42
+ check_server_status(uri.copy)
43
+ check_server_info(uri.copy)
44
+ end
45
+ end
46
+
47
+ def self.check_server_status(uri)
48
+ uri.path = '/server-status'
49
+ uri.query = '' if uri.query != nil
50
+
51
+ ret = Yawast::Shared::Http.get(uri)
52
+
53
+ if ret.include? 'Apache Server Status'
54
+ Yawast::Utilities.puts_vuln "Apache Server Status page found: #{uri}"
55
+ puts ''
56
+ end
57
+ end
58
+
59
+ def self.check_server_info(uri)
60
+ uri.path = '/server-info'
61
+ uri.query = '' if uri.query != nil
62
+
63
+ ret = Yawast::Shared::Http.get(uri)
64
+
65
+ if ret.include? 'Apache Server Information'
66
+ Yawast::Utilities.puts_vuln "Apache Server Info page found: #{uri}"
67
+ puts ''
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,14 @@
1
+ module Yawast
2
+ module Scanner
3
+ class Cms
4
+ def self.get_generator(body)
5
+ regex = /<meta name="generator[^>]+content\s*=\s*['"]([^'"]+)['"][^>]*>/
6
+ match = body.match regex
7
+
8
+ if match
9
+ Yawast::Utilities.puts_info "Meta Generator: #{match[1]}"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,95 @@
1
+ module Yawast
2
+ module Scanner
3
+ class Core
4
+ def self.print_header(uri)
5
+ Yawast.header
6
+
7
+ puts "Scanning: #{uri.to_s}"
8
+ puts
9
+ end
10
+
11
+ def self.setup(uri, options)
12
+ unless @setup
13
+ print_header(uri)
14
+ Yawast.set_openssl_options
15
+
16
+ Yawast::Scanner::Generic.server_info(uri, options)
17
+ end
18
+
19
+ @setup = true
20
+ end
21
+
22
+ def self.process(uri, options)
23
+ setup(uri, options)
24
+
25
+ begin
26
+ #setup the proxy
27
+ Yawast::Shared::Http.setup(options.proxy, options.cookie)
28
+
29
+ #cache the HEAD result, so that we can minimize hits
30
+ head = Yawast::Shared::Http.head(uri)
31
+ Yawast::Scanner::Generic.head_info(head)
32
+
33
+ #perfom SSL checks
34
+ check_ssl(uri, options, head)
35
+
36
+ #process the 'scan' stuff that goes beyond 'head'
37
+ unless options.head
38
+ #server specific checks
39
+ Yawast::Scanner::Apache.check_all(uri, head)
40
+ Yawast::Scanner::Iis.check_all(uri, head)
41
+
42
+ Yawast::Scanner::ObjectPresence.check_source_control(uri)
43
+ Yawast::Scanner::ObjectPresence.check_sitemap(uri)
44
+ Yawast::Scanner::ObjectPresence.check_cross_domain(uri)
45
+ Yawast::Scanner::ObjectPresence.check_wsftp_log(uri)
46
+ Yawast::Scanner::ObjectPresence.check_trace_axd(uri)
47
+ Yawast::Scanner::ObjectPresence.check_elmah_axd(uri)
48
+ Yawast::Scanner::ObjectPresence.check_readme_html(uri)
49
+ Yawast::Scanner::ObjectPresence.check_release_notes_txt(uri)
50
+
51
+ Yawast::Scanner::Generic.check_propfind(uri)
52
+ Yawast::Scanner::Generic.check_options(uri)
53
+ Yawast::Scanner::Generic.check_trace(uri)
54
+
55
+ #check for common directories
56
+ if options.dir
57
+ Yawast::Scanner::Generic.directory_search(uri, options.dirrecursive)
58
+ end
59
+
60
+ get_cms(uri, options)
61
+ end
62
+
63
+ puts 'Scan complete.'
64
+ rescue => e
65
+ Yawast::Utilities.puts_error "Fatal Error: Can not continue. (#{e.message})"
66
+ end
67
+ end
68
+
69
+ def self.get_cms(uri, options)
70
+ setup(uri, options)
71
+
72
+ body = Yawast::Shared::Http.get(uri)
73
+ Yawast::Scanner::Cms.get_generator(body)
74
+ end
75
+
76
+ def self.check_ssl(uri, options, head)
77
+ setup(uri, options)
78
+
79
+ if uri.scheme == 'https' && !options.nossl
80
+ head = Yawast::Shared::Http.head(uri) if head == nil
81
+
82
+ if options.internalssl
83
+ Yawast::Scanner::Ssl.info(uri, !options.nociphers, options.sweet32count)
84
+ else
85
+ Yawast::Scanner::SslLabs.info(uri, options.sslsessioncount)
86
+ end
87
+
88
+ Yawast::Scanner::Ssl.check_hsts(head)
89
+ elsif uri.scheme == 'http'
90
+ puts 'Skipping TLS checks; URL is not HTTPS'
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,323 @@
1
+ require 'ipaddr_extensions'
2
+
3
+ module Yawast
4
+ module Scanner
5
+ class Generic
6
+ def self.server_info(uri, options)
7
+ begin
8
+ puts 'DNS Information:'
9
+
10
+ dns = Resolv::DNS.new
11
+ Resolv::DNS.open do |resv|
12
+ a = resv.getresources(uri.host, Resolv::DNS::Resource::IN::A)
13
+ unless a.empty?
14
+ a.each do |ip|
15
+ begin
16
+ host_name = dns.getname(ip.address)
17
+ rescue
18
+ host_name = 'N/A'
19
+ end
20
+
21
+ Yawast::Utilities.puts_info "\t\t#{ip.address} (#{host_name})"
22
+
23
+ # if address is private, force internal SSL mode, don't show links
24
+ if IPAddr.new(ip.address.to_s, Socket::AF_INET).private?
25
+ options.internalssl = true
26
+ else
27
+ puts "\t\t\t\thttps://www.shodan.io/host/#{ip.address}"
28
+ puts "\t\t\t\thttps://censys.io/ipv4/#{ip.address}"
29
+ end
30
+ end
31
+ end
32
+
33
+ aaaa = resv.getresources(uri.host, Resolv::DNS::Resource::IN::AAAA)
34
+ unless aaaa.empty?
35
+ aaaa.each do |ip|
36
+ begin
37
+ host_name = dns.getname(ip.address)
38
+ rescue
39
+ host_name = 'N/A'
40
+ end
41
+
42
+ Yawast::Utilities.puts_info "\t\t#{ip.address} (#{host_name})"
43
+
44
+ # if address is private, force internal SSL mode, don't show links
45
+ if IPAddr.new(ip.address.to_s, Socket::AF_INET6).private?
46
+ options.internalssl = true
47
+ else
48
+ puts "\t\t\t\thttps://www.shodan.io/host/#{ip.address.to_s.downcase}"
49
+ end
50
+ end
51
+ end
52
+
53
+ txt = resv.getresources(uri.host, Resolv::DNS::Resource::IN::TXT)
54
+ unless txt.empty?
55
+ txt.each do |rec|
56
+ Yawast::Utilities.puts_info "\t\tTXT: #{rec.data}"
57
+ end
58
+ end
59
+
60
+ mx = resv.getresources(uri.host, Resolv::DNS::Resource::IN::MX)
61
+ unless mx.empty?
62
+ mx.each do |rec|
63
+ Yawast::Utilities.puts_info "\t\tMX: #{rec.exchange} (#{rec.preference})"
64
+ end
65
+ end
66
+
67
+ ns = resv.getresources(uri.host, Resolv::DNS::Resource::IN::NS)
68
+ unless ns.empty?
69
+ ns.each do |rec|
70
+ Yawast::Utilities.puts_info "\t\tNS: #{rec.name}"
71
+ end
72
+ end
73
+ end
74
+
75
+ puts
76
+ rescue => e
77
+ Yawast::Utilities.puts_error "Error getting basic information: #{e.message}"
78
+ raise
79
+ end
80
+ end
81
+
82
+ def self.head_info(head)
83
+ begin
84
+ server = ''
85
+ powered_by = ''
86
+ cookies = Array.new
87
+ pingback = ''
88
+ frame_options = ''
89
+ content_options = ''
90
+ csp = ''
91
+ backend_server = ''
92
+ runtime = ''
93
+ xss_protection = ''
94
+ via = ''
95
+ hpkp = ''
96
+
97
+ Yawast::Utilities.puts_info 'HEAD:'
98
+ head.each do |k, v|
99
+ Yawast::Utilities.puts_info "\t\t#{k}: #{v}"
100
+
101
+ server = v if k.downcase == 'server'
102
+ powered_by = v if k.downcase == 'x-powered-by'
103
+ pingback = v if k.downcase == 'x-pingback'
104
+ frame_options = v if k.downcase == 'x-frame-options'
105
+ content_options = v if k.downcase == 'x-content-type-options'
106
+ csp = v if k.downcase == 'content-security-policy'
107
+ backend_server = v if k.downcase == 'x-backend-server'
108
+ runtime = v if k.downcase == 'x-runtime'
109
+ xss_protection = v if k.downcase == 'x-xss-protection'
110
+ via = v if k.downcase == 'via'
111
+ hpkp = v if k.downcase == 'public-key-pins'
112
+
113
+ if k.downcase == 'set-cookie'
114
+ #this chunk of magic manages to properly split cookies, when multiple are sent together
115
+ v.gsub(/(,([^;,]*=)|,$)/) { "\r\n#{$2}" }.split(/\r\n/).each { |c| cookies.push(c) }
116
+ end
117
+ end
118
+ puts ''
119
+
120
+ if server != ''
121
+ Yawast::Scanner::Apache.check_banner(server)
122
+ Yawast::Scanner::Php.check_banner(server)
123
+ Yawast::Scanner::Iis.check_banner(server)
124
+ Yawast::Scanner::Nginx.check_banner(server)
125
+
126
+ if server == 'cloudflare-nginx'
127
+ Yawast::Utilities.puts_info 'NOTE: Server appears to be Cloudflare; WAF may be in place.'
128
+ puts
129
+ end
130
+ end
131
+
132
+ if powered_by != ''
133
+ Yawast::Utilities.puts_warn "X-Powered-By Header Present: #{powered_by}"
134
+ end
135
+
136
+ if xss_protection == '0'
137
+ Yawast::Utilities.puts_warn 'X-XSS-Protection Disabled Header Present'
138
+ end
139
+
140
+ unless pingback == ''
141
+ Yawast::Utilities.puts_info "X-Pingback Header Present: #{pingback}"
142
+ end
143
+
144
+ unless runtime == ''
145
+ if runtime.is_number?
146
+ Yawast::Utilities.puts_warn 'X-Runtime Header Present; likely indicates a RoR application'
147
+ else
148
+ Yawast::Utilities.puts_warn "X-Runtime Header Present: #{runtime}"
149
+ end
150
+ end
151
+
152
+ unless backend_server == ''
153
+ Yawast::Utilities.puts_warn "X-Backend-Server Header Present: #{backend_server}"
154
+ end
155
+
156
+ unless via == ''
157
+ Yawast::Utilities.puts_warn "Via Header Present: #{via}"
158
+ end
159
+
160
+ if frame_options == ''
161
+ Yawast::Utilities.puts_warn 'X-Frame-Options Header Not Present'
162
+ else
163
+ if frame_options.downcase == 'allow'
164
+ Yawast::Utilities.puts_vuln "X-Frame-Options Header: #{frame_options}"
165
+ else
166
+ Yawast::Utilities.puts_info "X-Frame-Options Header: #{frame_options}"
167
+ end
168
+ end
169
+
170
+ if content_options == ''
171
+ Yawast::Utilities.puts_warn 'X-Content-Type-Options Header Not Present'
172
+ else
173
+ Yawast::Utilities.puts_info "X-Content-Type-Options Header: #{content_options}"
174
+ end
175
+
176
+ if csp == ''
177
+ Yawast::Utilities.puts_warn 'Content-Security-Policy Header Not Present'
178
+ end
179
+
180
+ if hpkp == ''
181
+ Yawast::Utilities.puts_warn 'Public-Key-Pins Header Not Present'
182
+ end
183
+
184
+ puts ''
185
+
186
+ unless cookies.empty?
187
+ Yawast::Utilities.puts_info 'Cookies:'
188
+
189
+ cookies.each do |val|
190
+ Yawast::Utilities.puts_info "\t\t#{val.strip}"
191
+
192
+ elements = val.strip.split(';')
193
+
194
+ #check for secure cookies
195
+ unless elements.include? ' Secure'
196
+ Yawast::Utilities.puts_warn "\t\t\tCookie missing Secure flag"
197
+ end
198
+
199
+ #check for HttpOnly cookies
200
+ unless elements.include? ' HttpOnly'
201
+ Yawast::Utilities.puts_warn "\t\t\tCookie missing HttpOnly flag"
202
+ end
203
+ end
204
+
205
+ puts ''
206
+ end
207
+
208
+ puts ''
209
+ rescue => e
210
+ Yawast::Utilities.puts_error "Error getting head information: #{e.message}"
211
+ raise
212
+ end
213
+ end
214
+
215
+ def self.directory_search(uri, recursive, banner = true)
216
+ if banner
217
+ if recursive
218
+ puts 'Recursively searching for common directories (this will take a while)...'
219
+ else
220
+ puts 'Searching for common directories...'
221
+ end
222
+ end
223
+
224
+ begin
225
+ req = Yawast::Shared::Http.get_http(uri)
226
+ req.use_ssl = uri.scheme == 'https'
227
+ req.keep_alive_timeout = 600
228
+ headers = Yawast::Shared::Http.get_headers
229
+
230
+ req.start do |http|
231
+ File.open("lib/resources/common.txt", "r") do |f|
232
+ f.each_line do |line|
233
+ check = uri.copy
234
+ check.path = check.path + "#{line.strip}/"
235
+
236
+ res = http.head(check, headers)
237
+
238
+ if res.code == '200'
239
+ Yawast::Utilities.puts_info "\tFound: '#{check.to_s}'"
240
+ directory_search check, recursive, false if recursive
241
+ elsif res.code == '301'
242
+ Yawast::Utilities.puts_info "\tFound Redirect: '#{check.to_s} -> '#{res['Location']}'"
243
+ end
244
+ end
245
+ end
246
+ end
247
+ rescue => e
248
+ Yawast::Utilities.puts_error "Error searching for directories (#{e.message})"
249
+ end
250
+
251
+ puts '' if banner
252
+ end
253
+
254
+ def self.check_options(uri)
255
+ begin
256
+ req = Yawast::Shared::Http.get_http(uri)
257
+ req.use_ssl = uri.scheme == 'https'
258
+ headers = Yawast::Shared::Http.get_headers
259
+ res = req.request(Options.new('/', headers))
260
+
261
+ if res['Public'] != nil
262
+ Yawast::Utilities.puts_info "Public HTTP Verbs (OPTIONS): #{res['Public']}"
263
+
264
+ puts ''
265
+ end
266
+ end
267
+ end
268
+
269
+ def self.check_trace(uri)
270
+ begin
271
+ req = Yawast::Shared::Http.get_http(uri)
272
+ req.use_ssl = uri.scheme == 'https'
273
+ headers = Yawast::Shared::Http.get_headers
274
+ res = req.request(Trace.new('/', headers))
275
+
276
+ if res.body.include? 'TRACE / HTTP/1.1'
277
+ Yawast::Utilities.puts_warn 'HTTP TRACE Enabled'
278
+ puts "\t\t\"curl -X TRACE #{uri}\""
279
+
280
+ puts ''
281
+ end
282
+ end
283
+ end
284
+
285
+ def self.check_propfind(uri)
286
+ begin
287
+ req = Yawast::Shared::Http.get_http(uri)
288
+ req.use_ssl = uri.scheme == 'https'
289
+ headers = Yawast::Shared::Http.get_headers
290
+ res = req.request(Propfind.new('/', headers))
291
+
292
+ if res.code.to_i <= 400 && res.body.length > 0 && res['Content-Type'] == 'text/xml'
293
+ Yawast::Utilities.puts_warn 'Possible Info Disclosure: PROPFIND Enabled'
294
+ puts "\t\t\"curl -X PROPFIND #{uri}\""
295
+
296
+ puts ''
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ #Custom class to allow using the PROPFIND verb
303
+ class Propfind < Net::HTTPRequest
304
+ METHOD = "PROPFIND"
305
+ REQUEST_HAS_BODY = false
306
+ RESPONSE_HAS_BODY = true
307
+ end
308
+
309
+ #Custom class to allow using the OPTIONS verb
310
+ class Options < Net::HTTPRequest
311
+ METHOD = "OPTIONS"
312
+ REQUEST_HAS_BODY = false
313
+ RESPONSE_HAS_BODY = true
314
+ end
315
+
316
+ #Custom class to allow using the TRACE verb
317
+ class Trace < Net::HTTPRequest
318
+ METHOD = "TRACE"
319
+ REQUEST_HAS_BODY = false
320
+ RESPONSE_HAS_BODY = true
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,63 @@
1
+ module Yawast
2
+ module Scanner
3
+ class Iis
4
+ def self.check_banner(banner)
5
+ #don't bother if this doesn't include IIS
6
+ return if !banner.include? 'Microsoft-IIS/'
7
+ @iis = true
8
+
9
+ Yawast::Utilities.puts_warn "IIS Version: #{banner}"
10
+ puts ''
11
+ end
12
+
13
+ def self.check_all(uri, head)
14
+ return if !@iis
15
+
16
+ #run all the defined checks
17
+ check_asp_banner(head)
18
+ check_mvc_version(head)
19
+ check_asp_net_debug(uri)
20
+ end
21
+
22
+ def self.check_asp_banner(head)
23
+ head.each do |k, v|
24
+ if k.downcase == 'x-aspnet-version'
25
+ Yawast::Utilities.puts_warn "ASP.NET Version: #{v}"
26
+ puts ''
27
+ end
28
+ end
29
+ end
30
+
31
+ def self.check_mvc_version(head)
32
+ head.each do |k, v|
33
+ if k.downcase == 'x-aspnetmvc-version'
34
+ Yawast::Utilities.puts_warn "ASP.NET MVC Version: #{v}"
35
+ puts ''
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.check_asp_net_debug(uri)
41
+ begin
42
+ req = Yawast::Shared::Http.get_http(uri)
43
+ req.use_ssl = uri.scheme == 'https'
44
+ headers = Yawast::Shared::Http.get_headers
45
+ headers['Command'] = 'stop-debug'
46
+ headers['Accept'] = '*/*'
47
+ res = req.request(Debug.new('/', headers))
48
+
49
+ if res.code == 200
50
+ Yawast::Utilities.puts_vuln 'ASP.NET Debugging Enabled'
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ #Custom class to allow using the DEBUG verb
57
+ class Debug < Net::HTTPRequest
58
+ METHOD = "DEBUG"
59
+ REQUEST_HAS_BODY = false
60
+ RESPONSE_HAS_BODY = true
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ module Yawast
2
+ module Scanner
3
+ class Nginx
4
+ def self.check_banner(banner)
5
+ #don't bother if this doesn't include nginx
6
+ return if !banner.include? 'nginx/'
7
+
8
+ Yawast::Utilities.puts_warn "nginx Version: #{banner}"
9
+ puts ''
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,63 @@
1
+ module Yawast
2
+ module Scanner
3
+ class ObjectPresence
4
+ def self.check_source_control(uri)
5
+ check_path(uri, '/.git/', true)
6
+ check_path(uri, '/.hg/', true)
7
+ check_path(uri, '/.svn/', true)
8
+ check_path(uri, '/.bzr/', true)
9
+ end
10
+
11
+ def self.check_cross_domain(uri)
12
+ check_path(uri, '/crossdomain.xml', false)
13
+ check_path(uri, '/clientaccesspolicy.xml', false)
14
+ end
15
+
16
+ def self.check_sitemap(uri)
17
+ check_path(uri, '/sitemap.xml', false)
18
+ end
19
+
20
+ def self.check_wsftp_log(uri)
21
+ #check both upper and lower, as they are both seen in the wild
22
+ check_path(uri, '/WS_FTP.LOG', false)
23
+ check_path(uri, '/ws_ftp.log', false)
24
+ end
25
+
26
+ def self.check_trace_axd(uri)
27
+ check_path(uri, '/Trace.axd', false)
28
+ end
29
+
30
+ def self.check_elmah_axd(uri)
31
+ check_path(uri, '/elmah.axd', false)
32
+ end
33
+
34
+ def self.check_readme_html(uri)
35
+ check_path(uri, '/readme.html', false)
36
+ end
37
+
38
+ def self.check_release_notes_txt(uri)
39
+ check_path(uri, '/RELEASE-NOTES.txt', false)
40
+ end
41
+
42
+ def self.check_path(uri, path, vuln)
43
+ #note: this only checks directly at the root, I'm not sure if this is what we want
44
+ # should probably be relative to what's passed in, instead of overriding the path.
45
+ check = uri.copy
46
+ check.path = "#{path}"
47
+ code = Yawast::Shared::Http.get_status_code(check)
48
+
49
+ if code == "200"
50
+ msg = "'#{path}' found: #{check}"
51
+
52
+ if vuln
53
+ Yawast::Utilities.puts_vuln msg
54
+ else
55
+ Yawast::Utilities.puts_warn msg
56
+ end
57
+
58
+ puts ''
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ module Yawast
2
+ module Scanner
3
+ class Php
4
+ def self.check_banner(banner)
5
+ #don't bother if this doesn't include PHP
6
+ return if !banner.include? 'PHP/'
7
+
8
+ modules = banner.split(' ')
9
+
10
+ modules.each do |mod|
11
+ if mod.include? 'PHP/'
12
+ Yawast::Utilities.puts_warn "PHP Version: #{mod}"
13
+ puts ''
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end