ssl_scan 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +51 -0
- data/Rakefile +1 -0
- data/bin/ssl_scan +4 -0
- data/lib/ssl_scan/client.rb +0 -0
- data/lib/ssl_scan/compat.rb +388 -0
- data/lib/ssl_scan/exceptions.rb +274 -0
- data/lib/ssl_scan/io/bidirectional_pipe.rb +161 -0
- data/lib/ssl_scan/io/datagram_abstraction.rb +35 -0
- data/lib/ssl_scan/io/ring_buffer.rb +369 -0
- data/lib/ssl_scan/io/stream.rb +312 -0
- data/lib/ssl_scan/io/stream_abstraction.rb +209 -0
- data/lib/ssl_scan/io/stream_server.rb +221 -0
- data/lib/ssl_scan/result.rb +165 -0
- data/lib/ssl_scan/scanner.rb +241 -0
- data/lib/ssl_scan/socket/comm/local.rb +526 -0
- data/lib/ssl_scan/socket/comm.rb +120 -0
- data/lib/ssl_scan/socket/ip.rb +131 -0
- data/lib/ssl_scan/socket/parameters.rb +363 -0
- data/lib/ssl_scan/socket/range_walker.rb +470 -0
- data/lib/ssl_scan/socket/ssl_tcp.rb +345 -0
- data/lib/ssl_scan/socket/ssl_tcp_server.rb +188 -0
- data/lib/ssl_scan/socket/subnet_walker.rb +76 -0
- data/lib/ssl_scan/socket/switch_board.rb +289 -0
- data/lib/ssl_scan/socket/tcp.rb +79 -0
- data/lib/ssl_scan/socket/tcp_server.rb +67 -0
- data/lib/ssl_scan/socket/udp.rb +165 -0
- data/lib/ssl_scan/socket.rb +773 -0
- data/lib/ssl_scan/sync/thread_safe.rb +83 -0
- data/lib/ssl_scan/version.rb +9 -0
- data/lib/ssl_scan.rb +11 -0
- data/sslscan.gemspec +23 -0
- metadata +107 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'ssl_scan/socket'
|
2
|
+
require 'ssl_scan/result'
|
3
|
+
|
4
|
+
module SSLScan
|
5
|
+
|
6
|
+
class Scanner
|
7
|
+
|
8
|
+
attr_accessor :context
|
9
|
+
attr_accessor :host
|
10
|
+
attr_accessor :port
|
11
|
+
attr_accessor :timeout
|
12
|
+
|
13
|
+
attr_reader :supported_versions
|
14
|
+
attr_reader :peer_supported_versions
|
15
|
+
attr_reader :sslv2
|
16
|
+
|
17
|
+
# Initializes the scanner object
|
18
|
+
# @param host [String] IP address or hostname to scan
|
19
|
+
# @param port [Fixnum] Port number to scan, default: 443
|
20
|
+
# @param timeout [Fixnum] Timeout for connections, in seconds. default: 5
|
21
|
+
# @raise [StandardError] Raised when the configuration is invalid
|
22
|
+
def initialize(host,port = 443,context = {},timeout=5)
|
23
|
+
@host = host
|
24
|
+
@port = port
|
25
|
+
@timeout = timeout
|
26
|
+
@context = context
|
27
|
+
if check_opensslv2 == true
|
28
|
+
@supported_versions = [:SSLv2, :SSLv3, :TLSv1]
|
29
|
+
@sslv2 = true
|
30
|
+
else
|
31
|
+
@supported_versions = [:SSLv3, :TLSv1]
|
32
|
+
@sslv2 = false
|
33
|
+
end
|
34
|
+
@peer_supported_versions = []
|
35
|
+
raise StandardError, "The scanner configuration is invalid" unless valid?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Checks whether the scanner option has a valid configuration
|
39
|
+
# @return [Boolean] True or False, the configuration is valid.
|
40
|
+
def valid?
|
41
|
+
begin
|
42
|
+
@host = SSLScan::Socket.getaddress(@host, true)
|
43
|
+
rescue
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
return false unless @port.kind_of? Fixnum
|
47
|
+
return false unless @port >= 0 and @port <= 65535
|
48
|
+
return false unless @timeout.kind_of? Fixnum
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
|
52
|
+
# Initiate the Scan against the target. Will test each cipher one at a time.
|
53
|
+
# @return [Result] object containing the details of the scan
|
54
|
+
def scan(&block)
|
55
|
+
scan_result = SSLScan::Result.new
|
56
|
+
scan_result.openssl_sslv2 = sslv2
|
57
|
+
# If we can't get any SSL connection, then don't bother testing
|
58
|
+
# individual ciphers.
|
59
|
+
if test_ssl == :rejected and test_tls == :rejected
|
60
|
+
return scan_result
|
61
|
+
end
|
62
|
+
|
63
|
+
@supported_versions.each do |ssl_version|
|
64
|
+
sslctx = OpenSSL::SSL::SSLContext.new(ssl_version)
|
65
|
+
sslctx.ciphers.each do |cipher_name, ssl_ver, key_length, alg_length|
|
66
|
+
status = test_cipher(ssl_version, cipher_name)
|
67
|
+
scan_result.add_cipher(ssl_version, cipher_name, key_length, status)
|
68
|
+
if status == :accepted and scan_result.cert.nil?
|
69
|
+
scan_result.cert = get_cert(ssl_version, cipher_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
if block_given?
|
73
|
+
yield(ssl_version, cipher_name, key_length, status, scan_result.cert)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
@peer_supported_versions = [].tap do |psv|
|
79
|
+
psv << :SSLv2 if scan_result.supports_sslv2?
|
80
|
+
psv << :SSLv3 if scan_result.supports_sslv3?
|
81
|
+
psv << :TLSv1 if scan_result.supports_tlsv1?
|
82
|
+
end
|
83
|
+
scan_result
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def get_preferred_ciphers
|
88
|
+
ssl_versions = {}.tap do |v|
|
89
|
+
@supported_versions.each { |sv| v[sv] = [] }
|
90
|
+
end
|
91
|
+
|
92
|
+
ssl_versions.keys.each do |ssl_version|
|
93
|
+
begin
|
94
|
+
scan_client = SSLScan::Socket::Tcp.create(
|
95
|
+
'Context' => @context,
|
96
|
+
'PeerHost' => @host,
|
97
|
+
'PeerPort' => @port,
|
98
|
+
'SSL' => true,
|
99
|
+
'SSLVersion' => ssl_version,
|
100
|
+
'Timeout' => @timeout
|
101
|
+
)
|
102
|
+
ssl_versions[ssl_version] = scan_client.cipher
|
103
|
+
rescue => ex
|
104
|
+
ssl_versions.delete(ssl_version)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
ssl_versions
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_ssl
|
111
|
+
begin
|
112
|
+
scan_client = SSLScan::Socket::Tcp.create(
|
113
|
+
'Context' => @context,
|
114
|
+
'PeerHost' => @host,
|
115
|
+
'PeerPort' => @port,
|
116
|
+
'SSL' => true,
|
117
|
+
'SSLVersion' => :SSLv23,
|
118
|
+
'Timeout' => @timeout
|
119
|
+
)
|
120
|
+
rescue ::Exception => e
|
121
|
+
return :rejected
|
122
|
+
ensure
|
123
|
+
if scan_client
|
124
|
+
scan_client.close
|
125
|
+
end
|
126
|
+
end
|
127
|
+
return :accepted
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_tls
|
131
|
+
begin
|
132
|
+
scan_client = SSLScan::Socket::Tcp.create(
|
133
|
+
'Context' => @context,
|
134
|
+
'PeerHost' => @host,
|
135
|
+
'PeerPort' => @port,
|
136
|
+
'SSL' => true,
|
137
|
+
'SSLVersion' => :TLSv1,
|
138
|
+
'Timeout' => @timeout
|
139
|
+
)
|
140
|
+
rescue ::Exception => e
|
141
|
+
return :rejected
|
142
|
+
ensure
|
143
|
+
if scan_client
|
144
|
+
scan_client.close
|
145
|
+
end
|
146
|
+
end
|
147
|
+
return :accepted
|
148
|
+
end
|
149
|
+
|
150
|
+
# Tests the specified SSL Version and Cipher against the configured target
|
151
|
+
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
152
|
+
# @param cipher [String] The SSL Cipher to use
|
153
|
+
# @return [Symbol] Either :accepted or :rejected
|
154
|
+
def test_cipher(ssl_version, cipher)
|
155
|
+
validate_params(ssl_version,cipher)
|
156
|
+
begin
|
157
|
+
scan_client = SSLScan::Socket::Tcp.create(
|
158
|
+
'Context' => @context,
|
159
|
+
'PeerHost' => @host,
|
160
|
+
'PeerPort' => @port,
|
161
|
+
'SSL' => true,
|
162
|
+
'SSLVersion' => ssl_version,
|
163
|
+
'SSLCipher' => cipher,
|
164
|
+
'Timeout' => @timeout
|
165
|
+
)
|
166
|
+
rescue ::Exception => e
|
167
|
+
return :rejected
|
168
|
+
ensure
|
169
|
+
if scan_client
|
170
|
+
scan_client.close
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
return :accepted
|
175
|
+
end
|
176
|
+
|
177
|
+
# Retrieve the X509 Cert from the target service,
|
178
|
+
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
179
|
+
# @param cipher [String] The SSL Cipher to use
|
180
|
+
# @return [OpenSSL::X509::Certificate] if the certificate was retrieved
|
181
|
+
# @return [Nil] if the cert couldn't be retrieved
|
182
|
+
def get_cert(ssl_version, cipher)
|
183
|
+
validate_params(ssl_version, cipher)
|
184
|
+
begin
|
185
|
+
scan_client = SSLScan::Socket::Tcp.create(
|
186
|
+
'PeerHost' => @host,
|
187
|
+
'PeerPort' => @port,
|
188
|
+
'SSL' => true,
|
189
|
+
'SSLVersion' => ssl_version,
|
190
|
+
'SSLCipher' => cipher,
|
191
|
+
'Timeout' => @timeout
|
192
|
+
)
|
193
|
+
cert = scan_client.peer_cert
|
194
|
+
if cert.kind_of? OpenSSL::X509::Certificate
|
195
|
+
return cert
|
196
|
+
else
|
197
|
+
return nil
|
198
|
+
end
|
199
|
+
rescue ::Exception => e
|
200
|
+
return nil
|
201
|
+
ensure
|
202
|
+
if scan_client
|
203
|
+
scan_client.close
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
protected
|
210
|
+
|
211
|
+
# Validates that the SSL Version and Cipher are valid both seperately and
|
212
|
+
# together as part of an SSL Context.
|
213
|
+
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
214
|
+
# @param cipher [String] The SSL Cipher to use
|
215
|
+
# @raise [StandardError] If an invalid or unsupported SSL Version was supplied
|
216
|
+
# @raise [StandardError] If the cipher is not valid for that version of SSL
|
217
|
+
def validate_params(ssl_version, cipher)
|
218
|
+
raise StandardError, "The scanner configuration is invalid" unless valid?
|
219
|
+
unless @supported_versions.include? ssl_version
|
220
|
+
raise StandardError, "SSL Version must be one of: #{@supported_versions.to_s}"
|
221
|
+
end
|
222
|
+
if ssl_version == :SSLv2 and sslv2 == false
|
223
|
+
raise StandardError, "Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!"
|
224
|
+
else
|
225
|
+
unless OpenSSL::SSL::SSLContext.new(ssl_version).ciphers.flatten.include? cipher
|
226
|
+
raise StandardError, "Must be a valid SSL Cipher for #{ssl_version}!"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def check_opensslv2
|
232
|
+
begin
|
233
|
+
OpenSSL::SSL::SSLContext.new(:SSLv2)
|
234
|
+
rescue
|
235
|
+
return false
|
236
|
+
end
|
237
|
+
return true
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|