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.
- 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
|