yawast 0.5.0.beta2 → 0.5.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/scanner/core.rb CHANGED
@@ -22,7 +22,7 @@ module Yawast
22
22
 
23
23
  Yawast.set_openssl_options
24
24
 
25
- Yawast::Scanner::Generic.server_info(@uri, options)
25
+ Yawast::Scanner::Plugins::DNS::Generic.dns_info @uri, options
26
26
  end
27
27
 
28
28
  @setup = true
@@ -1,116 +1,10 @@
1
1
  require 'ipaddr_extensions'
2
2
  require 'json'
3
+ require 'public_suffix'
3
4
 
4
5
  module Yawast
5
6
  module Scanner
6
7
  class Generic
7
- def self.server_info(uri, options)
8
- begin
9
- puts 'DNS Information:'
10
-
11
- dns = Resolv::DNS.new
12
- Resolv::DNS.open do |resv|
13
- a = resv.getresources(uri.host, Resolv::DNS::Resource::IN::A)
14
- unless a.empty?
15
- a.each do |ip|
16
- begin
17
- host_name = dns.getname(ip.address)
18
- rescue
19
- host_name = 'N/A'
20
- end
21
-
22
- Yawast::Utilities.puts_info "\t\t#{ip.address} (#{host_name})"
23
-
24
- # if address is private, force internal SSL mode, don't show links
25
- if IPAddr.new(ip.address.to_s, Socket::AF_INET).private?
26
- options.internalssl = true
27
- else
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}"
34
- end
35
- end
36
- end
37
-
38
- aaaa = resv.getresources(uri.host, Resolv::DNS::Resource::IN::AAAA)
39
- unless aaaa.empty?
40
- aaaa.each do |ip|
41
- begin
42
- host_name = dns.getname(ip.address)
43
- rescue
44
- host_name = 'N/A'
45
- end
46
-
47
- Yawast::Utilities.puts_info "\t\t#{ip.address} (#{host_name})"
48
-
49
- # if address is private, force internal SSL mode, don't show links
50
- if IPAddr.new(ip.address.to_s, Socket::AF_INET6).private?
51
- options.internalssl = true
52
- else
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}"
58
- end
59
- end
60
- end
61
-
62
- txt = resv.getresources(uri.host, Resolv::DNS::Resource::IN::TXT)
63
- unless txt.empty?
64
- txt.each do |rec|
65
- Yawast::Utilities.puts_info "\t\tTXT: #{rec.data}"
66
- end
67
- end
68
-
69
- mx = resv.getresources(uri.host, Resolv::DNS::Resource::IN::MX)
70
- unless mx.empty?
71
- mx.each do |rec|
72
- Yawast::Utilities.puts_info "\t\tMX: #{rec.exchange} (#{rec.preference})"
73
- end
74
- end
75
-
76
- ns = resv.getresources(uri.host, Resolv::DNS::Resource::IN::NS)
77
- unless ns.empty?
78
- ns.each do |rec|
79
- Yawast::Utilities.puts_info "\t\tNS: #{rec.name}"
80
- end
81
- end
82
- end
83
-
84
- puts
85
- rescue => e
86
- Yawast::Utilities.puts_error "Error getting basic information: #{e.message}"
87
- raise
88
- end
89
- end
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
-
114
8
  def self.head_info(head, uri)
115
9
  begin
116
10
  server = ''
@@ -242,6 +136,11 @@ module Yawast
242
136
  unless elements.include?(' HttpOnly') || elements.include?(' httponly')
243
137
  Yawast::Utilities.puts_warn "\t\t\tCookie missing HttpOnly flag"
244
138
  end
139
+
140
+ #check for SameSite cookies
141
+ unless elements.include?(' SameSite') || elements.include?(' samesite')
142
+ Yawast::Utilities.puts_warn "\t\t\tCookie missing SameSite flag"
143
+ end
245
144
  end
246
145
 
247
146
  puts ''
@@ -0,0 +1,195 @@
1
+ module Yawast
2
+ module Scanner
3
+ module Plugins
4
+ module DNS
5
+ class Generic
6
+ def self.dns_info(uri, options)
7
+ begin
8
+ puts 'DNS Information:'
9
+ root_domain = PublicSuffix.parse(uri.host).domain
10
+
11
+ dns = Resolv::DNS.new
12
+ Resolv::DNS.open do |resv|
13
+ a = resv.getresources(uri.host, Resolv::DNS::Resource::IN::A)
14
+ unless a.empty?
15
+ a.each do |ip|
16
+ begin
17
+ host_name = dns.getname(ip.address)
18
+ rescue
19
+ host_name = 'N/A'
20
+ end
21
+
22
+ Yawast::Utilities.puts_info "\t\t#{ip.address} (#{host_name})"
23
+
24
+ # if address is private, force internal SSL mode, don't show links
25
+ if IPAddr.new(ip.address.to_s, Socket::AF_INET).private?
26
+ options.internalssl = true
27
+ else
28
+ #show network info
29
+ Yawast::Utilities.puts_info "\t\t\t#{get_network_info(ip.address)}"
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}"
34
+ end
35
+ end
36
+ end
37
+
38
+ aaaa = resv.getresources(uri.host, Resolv::DNS::Resource::IN::AAAA)
39
+ unless aaaa.empty?
40
+ aaaa.each do |ip|
41
+ begin
42
+ host_name = dns.getname(ip.address)
43
+ rescue
44
+ host_name = 'N/A'
45
+ end
46
+
47
+ Yawast::Utilities.puts_info "\t\t#{ip.address} (#{host_name})"
48
+
49
+ # if address is private, force internal SSL mode, don't show links
50
+ if IPAddr.new(ip.address.to_s, Socket::AF_INET6).private?
51
+ options.internalssl = true
52
+ else
53
+ #show network info
54
+ Yawast::Utilities.puts_info "\t\t\t#{get_network_info(ip.address)}"
55
+ get_network_location_info ip
56
+
57
+ puts "\t\t\thttps://www.shodan.io/host/#{ip.address.to_s.downcase}"
58
+ end
59
+ end
60
+ end
61
+
62
+ txt = resv.getresources(uri.host, Resolv::DNS::Resource::IN::TXT)
63
+ unless txt.empty?
64
+ txt.each do |rec|
65
+ Yawast::Utilities.puts_info "\t\tTXT: #{rec.data}"
66
+ end
67
+ end
68
+
69
+ #check for higher-level TXT records, if we aren't already at the top
70
+ if root_domain != uri.host
71
+ txt = resv.getresources(root_domain, Resolv::DNS::Resource::IN::TXT)
72
+ unless txt.empty?
73
+ txt.each do |rec|
74
+ Yawast::Utilities.puts_info "\t\tTXT (#{root_domain}): #{rec.data}"
75
+ end
76
+ end
77
+ end
78
+
79
+ mx = resv.getresources(uri.host, Resolv::DNS::Resource::IN::MX)
80
+ unless mx.empty?
81
+ mx.each do |rec|
82
+ ip = resv.getaddress rec.exchange
83
+
84
+ Yawast::Utilities.puts_info "\t\tMX: #{rec.exchange} (#{rec.preference}) - #{ip} (#{get_network_info(ip.to_s)})"
85
+ end
86
+ end
87
+
88
+ #check for higher-level MX records, if we aren't already at the top
89
+ if root_domain != uri.host
90
+ mx = resv.getresources(root_domain, Resolv::DNS::Resource::IN::MX)
91
+ unless mx.empty?
92
+ mx.each do |rec|
93
+ ip = resv.getaddress rec.exchange
94
+
95
+ Yawast::Utilities.puts_info "\t\tMX (#{root_domain}): #{rec.exchange} (#{rec.preference}) - #{ip} (#{get_network_info(ip.to_s)})"
96
+ end
97
+ end
98
+ end
99
+
100
+ ns = resv.getresources(root_domain, Resolv::DNS::Resource::IN::NS)
101
+ unless ns.empty?
102
+ ns.each do |rec|
103
+ ip = resv.getaddress rec.name
104
+
105
+ Yawast::Utilities.puts_info "\t\tNS: #{rec.name} - #{ip} (#{get_network_info(ip.to_s)})"
106
+ end
107
+ end
108
+
109
+ if options.srv
110
+ File.open(File.dirname(__FILE__) + '/../../../resources/srv_list.txt', 'r') do |f|
111
+ f.each_line do |line|
112
+ host = line.strip + '.' + root_domain
113
+ begin
114
+ srv = resv.getresources(host, Resolv::DNS::Resource::IN::SRV)
115
+
116
+ unless srv.empty?
117
+ srv.each do |rec|
118
+ ip = resv.getaddress rec.target
119
+
120
+ Yawast::Utilities.puts_info "\t\tSRV: #{host}: #{rec.target}:#{rec.port} - #{ip} (#{get_network_info(ip.to_s)})"
121
+ end
122
+ end
123
+ rescue
124
+ #if this fails, don't really care
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ if options.subdomains
131
+ File.open(File.dirname(__FILE__) + '/../../../resources/subdomain_list.txt', 'r') do |f|
132
+ f.each_line do |line|
133
+ host = line.strip + '.' + root_domain
134
+
135
+ begin
136
+ a = resv.getresources(host, Resolv::DNS::Resource::IN::A)
137
+
138
+ unless a.empty?
139
+ a.each do |ip|
140
+ if IPAddr.new(ip.address.to_s, Socket::AF_INET).private?
141
+ Yawast::Utilities.puts_info "\t\tA: #{host}: #{ip.address}"
142
+ else
143
+ Yawast::Utilities.puts_info "\t\tA: #{host}: #{ip.address} (#{get_network_info(ip.address)})"
144
+ end
145
+ end
146
+ end
147
+ rescue
148
+ #if this fails, don't really care
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ puts
156
+ rescue => e
157
+ Yawast::Utilities.puts_error "Error getting basic information: #{e.message}"
158
+ raise
159
+ end
160
+ end
161
+
162
+ def self.get_network_info(ip)
163
+ #check to see if we have this one cached
164
+ @netinfo = Hash.new if @netinfo == nil
165
+ return @netinfo[ip] if @netinfo[ip] != nil
166
+
167
+ begin
168
+ network_info = JSON.parse(Net::HTTP.get(URI("https://api.iptoasn.com/v1/as/ip/#{ip}")))
169
+
170
+ ret = "#{network_info['as_country_code']} - #{network_info['as_description']}"
171
+ @netinfo[ip] = ret
172
+
173
+ return ret
174
+ rescue => e
175
+ return "Error: getting network information failed (#{e.message})"
176
+ end
177
+ end
178
+
179
+ def self.get_network_location_info(ip)
180
+ begin
181
+ info = JSON.parse(Net::HTTP.get(URI("https://freegeoip.net/json/#{ip.address}")))
182
+ location = [info['city'], info['region_name'], info['country_code']].reject { |c| c.empty? }.join(', ')
183
+
184
+ if location != nil && !location.empty?
185
+ Yawast::Utilities.puts_info "\t\t\t#{location}"
186
+ end
187
+ rescue => e
188
+ Yawast::Utilities.puts_error "Error getting location information: #{e.message}"
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,85 @@
1
+ module Yawast
2
+ module Scanner
3
+ module Plugins
4
+ module SSL
5
+ class Sweet32
6
+ def self.get_tdes_session_msg_count(uri)
7
+ # this method will send a number of HEAD requests to see
8
+ # if the connection is eventually killed.
9
+ puts 'TLS Session Request Limit: Checking number of requests accepted using 3DES suites...'
10
+
11
+ count = 0
12
+ begin
13
+ req = Yawast::Shared::Http.get_http(uri)
14
+ req.use_ssl = uri.scheme == 'https'
15
+ req.keep_alive_timeout = 600
16
+ headers = Yawast::Shared::Http.get_headers
17
+
18
+ #force 3DES - this is to ensure that 3DES specific limits are caught
19
+ req.ciphers = ['3DES']
20
+
21
+ #attempt to find a version that supports 3DES
22
+ versions = OpenSSL::SSL::SSLContext::METHODS.find_all { |v| !v.to_s.include?('_client') && !v.to_s.include?('_server')}
23
+ versions.each do |version|
24
+ if version.to_s != 'SSLv23'
25
+ req.ssl_version = version
26
+
27
+ begin
28
+ req.start do |http|
29
+ head = http.head(uri.path, headers)
30
+
31
+ #check to see if this is on Cloudflare - they break Keep-Alive limits, creating a false positive
32
+ head.each do |k, v|
33
+ if k.downcase == 'server'
34
+ if v == 'cloudflare-nginx'
35
+ puts 'Cloudflare server found: SWEET32 mitigated: https://support.cloudflare.com/hc/en-us/articles/231510928'
36
+ return
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ print "Using #{version}"
43
+ break
44
+ rescue
45
+ #we don't care
46
+ end
47
+ end
48
+ end
49
+
50
+ req.start do |http|
51
+ 10000.times do |i|
52
+ http.head(uri.path, headers)
53
+
54
+ # hack to detect transparent disconnects
55
+ if http.instance_variable_get(:@ssl_context).session_cache_stats[:cache_hits] != 0
56
+ raise 'TLS Reconnected'
57
+ end
58
+
59
+ count += 1
60
+
61
+ if i % 20 == 0
62
+ print '.'
63
+ end
64
+ end
65
+ end
66
+ rescue => e
67
+ puts
68
+
69
+ if e.message.include? 'alert handshake failure'
70
+ Yawast::Utilities.puts_info 'TLS Session Request Limit: Server does not support 3DES cipher suites'
71
+ else
72
+ Yawast::Utilities.puts_info "TLS Session Request Limit: Connection terminated after #{count} requests (#{e.message})"
73
+ end
74
+
75
+ return
76
+ end
77
+
78
+ puts
79
+ Yawast::Utilities.puts_vuln 'TLS Session Request Limit: Connection not terminated after 10,000 requests; possibly vulnerable to SWEET32'
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
data/lib/scanner/ssl.rb CHANGED
@@ -34,7 +34,7 @@ module Yawast
34
34
 
35
35
  ssl.sysclose
36
36
 
37
- get_tdes_session_msg_count(uri) if tdes_session_count
37
+ Yawast::Scanner::Plugins::SSL::Sweet32.get_tdes_session_msg_count(uri) if tdes_session_count
38
38
  rescue => e
39
39
  Yawast::Utilities.puts_error "SSL: Error Reading X509 Details: #{e.message}"
40
40
  end
@@ -148,7 +148,7 @@ module Yawast
148
148
 
149
149
  if ciphers != nil
150
150
  check_version_suites uri, ip, ciphers, version
151
- elsif get_ciphers_failed == false
151
+ elsif !get_ciphers_failed
152
152
  Yawast::Utilities.puts_info "\t#{version}: No cipher suites available."
153
153
  end
154
154
  end
@@ -160,6 +160,19 @@ module Yawast
160
160
  def self.check_version_suites(uri, ip, ciphers, version)
161
161
  puts "\tChecking for #{version} suites (#{ciphers.count} possible suites)"
162
162
 
163
+ #first, let's see if we can connect using this version - so we don't do pointless checks
164
+ req = Yawast::Shared::Http.get_http(uri)
165
+ req.use_ssl = uri.scheme == 'https'
166
+ req.ssl_version = version
167
+ begin
168
+ req.start do |http|
169
+ http.head(uri.path, Yawast::Shared::Http.get_headers)
170
+ end
171
+ rescue
172
+ Yawast::Utilities.puts_info "\t\tVersion: #{version}\tNo Supported Cipher Suites"
173
+ return
174
+ end
175
+
163
176
  ciphers.each do |cipher|
164
177
  #try to connect and see what happens
165
178
  begin
@@ -183,7 +196,9 @@ module Yawast
183
196
  Yawast::Utilities.puts_error "\t\tVersion: #{ssl.ssl_version.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}\t(Supported But Failed)"
184
197
  end
185
198
  rescue => e
186
- Yawast::Utilities.puts_error "\t\tVersion: #{''.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}\t(#{e.message})"
199
+ unless e.message.include?('Connection reset by peer')
200
+ Yawast::Utilities.puts_error "\t\tVersion: #{''.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}\t(#{e.message})"
201
+ end
187
202
  ensure
188
203
  ssl.sysclose unless ssl == nil
189
204
  end
@@ -233,53 +248,6 @@ module Yawast
233
248
  end
234
249
  end
235
250
 
236
- def self.get_tdes_session_msg_count(uri)
237
- # this method will send a number of HEAD requests to see
238
- # if the connection is eventually killed.
239
- puts 'TLS Session Request Limit: Checking number of requests accepted using 3DES suites...'
240
-
241
- count = 0
242
- begin
243
- req = Yawast::Shared::Http.get_http(uri)
244
- req.use_ssl = uri.scheme == 'https'
245
- req.keep_alive_timeout = 600
246
- headers = Yawast::Shared::Http.get_headers
247
-
248
- #force 3DES - this is to ensure that 3DES specific limits are caught
249
- req.ciphers = ['3DES']
250
-
251
- req.start do |http|
252
- 10000.times do |i|
253
- http.head(uri.path, headers)
254
-
255
- # hack to detect transparent disconnects
256
- if http.instance_variable_get(:@ssl_context).session_cache_stats[:cache_hits] != 0
257
- raise 'TLS Reconnected'
258
- end
259
-
260
- count += 1
261
-
262
- if i % 20 == 0
263
- print '.'
264
- end
265
- end
266
- end
267
- rescue => e
268
- puts
269
-
270
- if e.message.include? 'alert handshake failure'
271
- Yawast::Utilities.puts_info 'TLS Session Request Limit: Server does not support 3DES cipher suites'
272
- else
273
- Yawast::Utilities.puts_info "TLS Session Request Limit: Connection terminated after #{count} requests (#{e.message})"
274
- end
275
-
276
- return
277
- end
278
-
279
- puts
280
- Yawast::Utilities.puts_vuln 'TLS Session Request Limit: Connection not terminated after 10,000 requests; possibly vulnerable to SWEET32'
281
- end
282
-
283
251
  #private methods
284
252
  class << self
285
253
  private
@@ -54,7 +54,7 @@ module Yawast
54
54
  Yawast::Utilities.puts_error "Error getting information for IP: #{ep.ip_address}: #{e.message}"
55
55
  end
56
56
 
57
- Yawast::Scanner::Ssl.get_tdes_session_msg_count(uri) if tdes_session_count
57
+ Yawast::Scanner::Plugins::SSL::Sweet32.get_tdes_session_msg_count(uri) if tdes_session_count
58
58
 
59
59
  puts
60
60
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yawast
2
- VERSION = '0.5.0.beta2'
2
+ VERSION = '0.5.0.beta3'
3
3
  end
data/yawast.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.add_runtime_dependency 'colorize', '~> 0.8'
21
21
  s.add_runtime_dependency 'ipaddr_extensions', '~> 1.0'
22
22
  s.add_runtime_dependency 'ipaddress', '~> 0.8'
23
+ s.add_runtime_dependency 'public_suffix', '~> 2.0'
23
24
 
24
25
  s.bindir = 'bin'
25
26
  s.files = `git ls-files`.split("\n")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yawast
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.beta2
4
+ version: 0.5.0.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Caudill
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-07 00:00:00.000000000 Z
11
+ date: 2017-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ssllabs
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.8'
111
+ - !ruby/object:Gem::Dependency
112
+ name: public_suffix
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.0'
111
125
  description: YAWAST is an application meant to simplify initial analysis and information
112
126
  gathering for penetration testers and security auditors.
113
127
  email: adam@adamcaudill.com
@@ -135,6 +149,8 @@ files:
135
149
  - lib/commands/utils.rb
136
150
  - lib/resources/common_dir.txt
137
151
  - lib/resources/common_file.txt
152
+ - lib/resources/srv_list.txt
153
+ - lib/resources/subdomain_list.txt
138
154
  - lib/scanner/apache.rb
139
155
  - lib/scanner/cert.rb
140
156
  - lib/scanner/cms.rb
@@ -143,8 +159,10 @@ files:
143
159
  - lib/scanner/iis.rb
144
160
  - lib/scanner/nginx.rb
145
161
  - lib/scanner/php.rb
162
+ - lib/scanner/plugins/dns/generic.rb
146
163
  - lib/scanner/plugins/http/directory_search.rb
147
164
  - lib/scanner/plugins/http/file_presence.rb
165
+ - lib/scanner/plugins/ssl/sweet32.rb
148
166
  - lib/scanner/ssl.rb
149
167
  - lib/scanner/ssl_labs.rb
150
168
  - lib/shared/http.rb