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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -1
- data/CHANGELOG.md +8 -0
- data/LICENSE +29 -0
- data/README.md +142 -144
- data/Rakefile +3 -6
- data/bin/yawast +1 -0
- data/lib/resources/common_dir.txt +21332 -0
- data/lib/resources/common_file.txt +13982 -0
- data/lib/scanner/apache.rb +87 -13
- data/lib/scanner/cert.rb +1 -1
- data/lib/scanner/core.rb +4 -3
- data/lib/scanner/generic.rb +35 -3
- data/lib/scanner/iis.rb +7 -10
- data/lib/scanner/plugins/http/directory_search.rb +11 -6
- data/lib/scanner/plugins/http/file_presence.rb +89 -1
- data/lib/scanner/ssl.rb +149 -114
- data/lib/shared/http.rb +7 -3
- data/lib/version.rb +1 -1
- data/lib/yawast.rb +2 -2
- data/test/test_internalssl.rb +31 -0
- data/test/test_object_presence.rb +1 -1
- data/test/test_scan_apache_server_info.rb +1 -1
- metadata +8 -4
- data/lib/resources/common.txt +0 -1960
data/lib/scanner/apache.rb
CHANGED
@@ -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
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
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? '
|
54
|
-
|
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.
|
60
|
-
|
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?
|
66
|
-
Yawast::Utilities.puts_vuln "
|
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,
|
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
|
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
|
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
|
data/lib/scanner/generic.rb
CHANGED
@@ -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
|
-
|
28
|
-
|
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
|
-
|
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
|
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 ==
|
34
|
-
Yawast::Utilities.puts_warn "
|
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/
|
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
|
-
|
78
|
-
|
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
|
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
|
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
|