yawast 0.5.0.beta1 → 0.5.0.beta2

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