ssl_scan 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|