yawast 0.5.0.beta2 → 0.5.0.beta3

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