yawast 0.5.0.beta1 → 0.5.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.
@@ -1,3 +1,5 @@
1
+ require "base64"
2
+
1
3
  module Yawast
2
4
  module Scanner
3
5
  class Apache
@@ -36,37 +38,109 @@ module Yawast
36
38
  end
37
39
 
38
40
  def self.check_all(uri)
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
41
+ #run all the defined checks
42
+ check_server_status(uri.copy)
43
+ check_server_info(uri.copy)
44
+ check_tomcat_manager(uri.copy)
45
+ check_tomcat_version(uri.copy)
45
46
  end
46
47
 
47
48
  def self.check_server_status(uri)
48
- uri.path = '/server-status'
49
+ check_page_for_string uri, '/server-status', 'Apache Server Status'
50
+ end
51
+
52
+ def self.check_server_info(uri)
53
+ check_page_for_string uri, '/server-info', 'Apache Server Information'
54
+ end
55
+
56
+ def self.check_tomcat_version(uri)
57
+ begin
58
+ req = Yawast::Shared::Http.get_http(uri)
59
+ req.use_ssl = uri.scheme == 'https'
60
+ headers = Yawast::Shared::Http.get_headers
61
+ res = req.request(Xyz.new('/', headers))
62
+
63
+ if res.body != nil && res.body.include?('Apache Tomcat') && res.code == '501'
64
+ #check to see if there's a version number
65
+ version = /Apache Tomcat\/\d*.\d*.\d*\b/.match res.body
66
+
67
+ if version != nil && version[0] != nil
68
+ Yawast::Utilities.puts_warn "Apache Tomcat Version Found: #{version[0]}"
69
+ puts "\t\t\"curl -X XYZ #{uri}\""
70
+
71
+ puts ''
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ def self.check_tomcat_manager(uri)
78
+ check_tomcat_manager_paths uri, 'manager', 'Manager'
79
+ check_tomcat_manager_paths uri, 'host-manager', 'Host Manager'
80
+ end
81
+
82
+ def self.check_tomcat_manager_paths(uri, base_path, manager)
83
+ uri.path = "/#{base_path}/html"
49
84
  uri.query = '' if uri.query != nil
50
85
 
51
86
  ret = Yawast::Shared::Http.get(uri)
52
87
 
53
- if ret.include? 'Apache Server Status'
54
- Yawast::Utilities.puts_vuln "Apache Server Status page found: #{uri}"
88
+ if ret.include? '<tt>conf/tomcat-users.xml</tt>'
89
+ #this will get Tomcat 7+
90
+ Yawast::Utilities.puts_warn "Apache Tomcat #{manager} page found: #{uri}"
91
+ check_tomcat_manager_passwords uri, manager
92
+
55
93
  puts ''
94
+ else
95
+ #check for Tomcat 6 and below
96
+ uri.path = "/#{base_path}"
97
+ ret = Yawast::Shared::Http.get(uri)
98
+
99
+ if ret.include? '<tt>conf/tomcat-users.xml</tt>'
100
+ Yawast::Utilities.puts_warn "Apache Tomcat #{manager} page found: #{uri}"
101
+ check_tomcat_manager_passwords uri, manager
102
+
103
+ puts ''
104
+ end
56
105
  end
57
106
  end
58
107
 
59
- def self.check_server_info(uri)
60
- uri.path = '/server-info'
108
+ def self.check_tomcat_manager_passwords(uri, manager)
109
+ #check for known passwords
110
+ check_tomcat_manager_pwd_check uri, manager, 'tomcat:tomcat'
111
+ check_tomcat_manager_pwd_check uri, manager, 'tomcat:password'
112
+ check_tomcat_manager_pwd_check uri, manager, 'tomcat:'
113
+ check_tomcat_manager_pwd_check uri, manager, 'admin:admin'
114
+ check_tomcat_manager_pwd_check uri, manager, 'admin:password'
115
+ check_tomcat_manager_pwd_check uri, manager, 'admin:'
116
+ end
117
+
118
+ def self.check_tomcat_manager_pwd_check(uri, manager, credentials)
119
+ ret = Yawast::Shared::Http.get(uri, {'Authorization' => "Basic #{Base64.encode64(credentials)}"})
120
+ if ret.include?('<font size="+2">Tomcat Web Application Manager</font>') ||
121
+ ret.include?('<font size="+2">Tomcat Virtual Host Manager</font>')
122
+ Yawast::Utilities.puts_vuln "Apache Tomcat #{manager} weak password: #{credentials}"
123
+ end
124
+ end
125
+
126
+ def self.check_page_for_string(uri, path, search)
127
+ uri.path = path
61
128
  uri.query = '' if uri.query != nil
62
129
 
63
130
  ret = Yawast::Shared::Http.get(uri)
64
131
 
65
- if ret.include? 'Apache Server Information'
66
- Yawast::Utilities.puts_vuln "Apache Server Info page found: #{uri}"
132
+ if ret.include? search
133
+ Yawast::Utilities.puts_vuln "#{search} page found: #{uri}"
67
134
  puts ''
68
135
  end
69
136
  end
70
137
  end
138
+
139
+ #Custom class to allow using the XYZ verb
140
+ class Xyz < Net::HTTPRequest
141
+ METHOD = 'XYZ'
142
+ REQUEST_HAS_BODY = false
143
+ RESPONSE_HAS_BODY = true
144
+ end
71
145
  end
72
146
  end
data/lib/scanner/cert.rb CHANGED
@@ -66,7 +66,7 @@ module Yawast
66
66
  return if domain == ''
67
67
 
68
68
  begin
69
- socket = Socket.tcp(domain, 443, opts={connect_timeout: 8})
69
+ socket = Socket.tcp(domain, 443, {connect_timeout: 8})
70
70
 
71
71
  ctx = OpenSSL::SSL::SSLContext.new
72
72
  ctx.ciphers = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers]
data/lib/scanner/core.rb CHANGED
@@ -4,7 +4,7 @@ module Yawast
4
4
  def self.print_header
5
5
  Yawast.header
6
6
 
7
- puts "Scanning: #{@uri.to_s}"
7
+ puts "Scanning: #{@uri}"
8
8
  puts
9
9
  end
10
10
 
@@ -17,7 +17,7 @@ module Yawast
17
17
  ssl_redirect = check_for_ssl_redirect
18
18
  if ssl_redirect
19
19
  @uri = ssl_redirect
20
- puts "Server redirects to TLS: Scanning: #{@uri.to_s}"
20
+ puts "Server redirects to TLS: Scanning: #{@uri}"
21
21
  end
22
22
 
23
23
  Yawast.set_openssl_options
@@ -48,7 +48,7 @@ module Yawast
48
48
  Yawast::Scanner::Apache.check_all(@uri)
49
49
  Yawast::Scanner::Iis.check_all(@uri, head)
50
50
 
51
- Yawast::Scanner::Plugins::Http::FilePresence.check_all @uri
51
+ Yawast::Scanner::Plugins::Http::FilePresence.check_all @uri, options.files
52
52
 
53
53
  Yawast::Scanner::Generic.check_propfind(@uri)
54
54
  Yawast::Scanner::Generic.check_options(@uri)
@@ -110,6 +110,7 @@ module Yawast
110
110
  end
111
111
 
112
112
  Yawast::Scanner::Ssl.check_hsts(head)
113
+ Yawast::Scanner::Ssl.check_hsts_preload @uri
113
114
  elsif @uri.scheme == 'http'
114
115
  puts 'Skipping TLS checks; URL is not HTTPS'
115
116
  end
@@ -1,4 +1,5 @@
1
1
  require 'ipaddr_extensions'
2
+ require 'json'
2
3
 
3
4
  module Yawast
4
5
  module Scanner
@@ -24,8 +25,12 @@ module Yawast
24
25
  if IPAddr.new(ip.address.to_s, Socket::AF_INET).private?
25
26
  options.internalssl = true
26
27
  else
27
- puts "\t\t\t\thttps://www.shodan.io/host/#{ip.address}"
28
- puts "\t\t\t\thttps://censys.io/ipv4/#{ip.address}"
28
+ #show network info
29
+ get_network_info ip
30
+ get_network_location_info ip
31
+
32
+ puts "\t\t\thttps://www.shodan.io/host/#{ip.address}"
33
+ puts "\t\t\thttps://censys.io/ipv4/#{ip.address}"
29
34
  end
30
35
  end
31
36
  end
@@ -45,7 +50,11 @@ module Yawast
45
50
  if IPAddr.new(ip.address.to_s, Socket::AF_INET6).private?
46
51
  options.internalssl = true
47
52
  else
48
- puts "\t\t\t\thttps://www.shodan.io/host/#{ip.address.to_s.downcase}"
53
+ #show network info
54
+ get_network_info ip
55
+ get_network_location_info ip
56
+
57
+ puts "\t\t\thttps://www.shodan.io/host/#{ip.address.to_s.downcase}"
49
58
  end
50
59
  end
51
60
  end
@@ -79,6 +88,29 @@ module Yawast
79
88
  end
80
89
  end
81
90
 
91
+ def self.get_network_info(ip)
92
+ begin
93
+ network_info = JSON.parse(Net::HTTP.get(URI("https://api.iptoasn.com/v1/as/ip/#{ip.address}")))
94
+
95
+ Yawast::Utilities.puts_info "\t\t\t#{network_info['as_country_code']} - #{network_info['as_description']}"
96
+ rescue => e
97
+ Yawast::Utilities.puts_error "Error getting network information: #{e.message}"
98
+ end
99
+ end
100
+
101
+ def self.get_network_location_info(ip)
102
+ begin
103
+ info = JSON.parse(Net::HTTP.get(URI("https://freegeoip.net/json/#{ip.address}")))
104
+ location = [info['city'], info['region_name'], info['country_code']].reject { |c| c.empty? }.join(', ')
105
+
106
+ if location != nil && !location.empty?
107
+ Yawast::Utilities.puts_info "\t\t\t#{location}"
108
+ end
109
+ rescue => e
110
+ Yawast::Utilities.puts_error "Error getting location information: #{e.message}"
111
+ end
112
+ end
113
+
82
114
  def self.head_info(head, uri)
83
115
  begin
84
116
  server = ''
data/lib/scanner/iis.rb CHANGED
@@ -11,8 +11,6 @@ module Yawast
11
11
  end
12
12
 
13
13
  def self.check_all(uri, head)
14
- return unless @iis
15
-
16
14
  #run all the defined checks
17
15
  check_asp_banner(head)
18
16
  check_mvc_version(head)
@@ -20,18 +18,17 @@ module Yawast
20
18
  end
21
19
 
22
20
  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
21
+ check_header_value head, 'x-aspnet-version', 'ASP.NET'
29
22
  end
30
23
 
31
24
  def self.check_mvc_version(head)
25
+ check_header_value head, 'x-aspnetmvc-version', 'ASP.NET MVC'
26
+ end
27
+
28
+ def self.check_header_value(head, search, message)
32
29
  head.each do |k, v|
33
- if k.downcase == 'x-aspnetmvc-version'
34
- Yawast::Utilities.puts_warn "ASP.NET MVC Version: #{v}"
30
+ if k.downcase == search
31
+ Yawast::Utilities.puts_warn "#{message} Version: #{v}"
35
32
  puts ''
36
33
  end
37
34
  end
@@ -16,7 +16,7 @@ module Yawast
16
16
  if search_list == nil
17
17
  @search_list = []
18
18
 
19
- File.open(File.dirname(__FILE__) + '/../../../resources/common.txt', 'r') do |f|
19
+ File.open(File.dirname(__FILE__) + '/../../../resources/common_dir.txt', 'r') do |f|
20
20
  f.each_line do |line|
21
21
  @search_list.push line.strip
22
22
  end
@@ -72,10 +72,15 @@ module Yawast
72
72
  def self.load_queue(uri)
73
73
  @search_list.each do |line|
74
74
  check = uri.copy
75
- check.path = check.path + "#{line}/"
76
75
 
77
- #add the job to the queue
78
- @jobs.push check
76
+ begin
77
+ check.path = check.path + "#{line}/"
78
+
79
+ #add the job to the queue
80
+ @jobs.push check
81
+ rescue
82
+ #who cares
83
+ end
79
84
  end
80
85
  end
81
86
 
@@ -84,11 +89,11 @@ module Yawast
84
89
  res = Yawast::Shared::Http.head uri
85
90
 
86
91
  if res.code == '200'
87
- @results.push "\tFound: '#{uri.to_s}'"
92
+ @results.push "\tFound: '#{uri}'"
88
93
 
89
94
  load_queue uri if @recursive
90
95
  elsif res.code == '301' && @list_redirects
91
- @results.push "\tFound Redirect: '#{uri.to_s} -> '#{res['Location']}'"
96
+ @results.push "\tFound Redirect: '#{uri} -> '#{res['Location']}'"
92
97
  end
93
98
  rescue => e
94
99
  Yawast::Utilities.puts_error "Error searching for directories (#{e.message})"
@@ -25,7 +25,7 @@ module Yawast
25
25
  end
26
26
  end
27
27
 
28
- def self.check_all(uri)
28
+ def self.check_all(uri, common_files)
29
29
  #first, we need to see if the site responds to 404 in a reasonable way
30
30
  fake_uri = uri.copy
31
31
  fake_uri.path = "/#{SecureRandom.hex}/"
@@ -44,6 +44,14 @@ module Yawast
44
44
  check_elmah_axd uri
45
45
  check_readme_html uri
46
46
  check_release_notes_txt uri
47
+
48
+ if common_files
49
+ puts ''
50
+ puts 'Checking for common files (this will take a few minutes)...'
51
+ check_common uri
52
+ end
53
+
54
+ puts ''
47
55
  end
48
56
 
49
57
  def self.check_source_control(uri)
@@ -51,6 +59,7 @@ module Yawast
51
59
  check_path(uri, '/.hg/', true)
52
60
  check_path(uri, '/.svn/', true)
53
61
  check_path(uri, '/.bzr/', true)
62
+ check_path(uri, '/.csv/', true)
54
63
  end
55
64
 
56
65
  def self.check_cross_domain(uri)
@@ -82,6 +91,85 @@ module Yawast
82
91
 
83
92
  def self.check_release_notes_txt(uri)
84
93
  check_path(uri, '/RELEASE-NOTES.txt', false)
94
+ check_path(uri, '/docs/RELEASE-NOTES.txt', false)
95
+ end
96
+
97
+ def self.check_common(uri)
98
+ begin
99
+ @search_list = []
100
+
101
+ File.open(File.dirname(__FILE__) + '/../../../resources/common_file.txt', 'r') do |f|
102
+ f.each_line do |line|
103
+ @search_list.push line.strip
104
+ end
105
+ end
106
+
107
+ pool_size = 16
108
+ @jobs = Queue.new
109
+ @results = Queue.new
110
+
111
+ #load the queue, starting at /
112
+ base = uri.copy
113
+ base.path = '/'
114
+ load_queue base
115
+
116
+ workers = (pool_size).times.map do
117
+ Thread.new do
118
+ begin
119
+ while (check = @jobs.pop(true))
120
+ process check
121
+ end
122
+ rescue ThreadError
123
+ #do nothing
124
+ end
125
+ end
126
+ end
127
+
128
+ results = Thread.new do
129
+ begin
130
+ while true
131
+ if @results.length > 0
132
+ out = @results.pop(true)
133
+ Yawast::Utilities.puts_info out
134
+ end
135
+ end
136
+ rescue ThreadError
137
+ #do nothing
138
+ end
139
+ end
140
+
141
+ workers.map(&:join)
142
+ results.terminate
143
+ rescue => e
144
+ Yawast::Utilities.puts_error "Error searching for files (#{e.message})"
145
+ end
146
+ end
147
+
148
+ def self.load_queue(uri)
149
+ @search_list.each do |line|
150
+ check = uri.copy
151
+
152
+ begin
153
+ check.path = "/#{line}"
154
+
155
+ #add the job to the queue
156
+ @jobs.push check
157
+ rescue
158
+ #who cares
159
+ end
160
+ end
161
+ end
162
+
163
+ def self.process(uri)
164
+ begin
165
+ res = Yawast::Shared::Http.head uri
166
+
167
+ if res.code == '200'
168
+ @results.push "'#{uri.path}' found: #{uri}"
169
+ end
170
+ rescue => e
171
+ Yawast::Utilities.puts_error "Error searching for file '#{uri.path}' (#{e.message})"
172
+ end
85
173
  end
86
174
  end
87
175
  end