ssl_scan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,209 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'socket'
4
+ require 'fcntl'
5
+
6
+ module Rex
7
+ module IO
8
+
9
+ ###
10
+ #
11
+ # This class provides an abstraction to a stream based
12
+ # connection through the use of a streaming socketpair.
13
+ #
14
+ ###
15
+ module StreamAbstraction
16
+
17
+ ###
18
+ #
19
+ # Extension information for required Stream interface.
20
+ #
21
+ ###
22
+ module Ext
23
+
24
+ #
25
+ # Initializes peer information.
26
+ #
27
+ def initinfo(peer,local)
28
+ @peer = peer
29
+ @local = local
30
+ end
31
+
32
+ #
33
+ # Symbolic peer information.
34
+ #
35
+ def peerinfo
36
+ (@peer || "Remote Pipe")
37
+ end
38
+
39
+ #
40
+ # Symbolic local information.
41
+ #
42
+ def localinfo
43
+ (@local || "Local Pipe")
44
+ end
45
+ end
46
+
47
+ #
48
+ # This method creates a streaming socket pair and initializes it.
49
+ #
50
+ def initialize_abstraction
51
+ self.lsock, self.rsock = Rex::Socket.tcp_socket_pair()
52
+ self.lsock.extend(Rex::IO::Stream)
53
+ self.lsock.extend(Ext)
54
+ self.rsock.extend(Rex::IO::Stream)
55
+
56
+ self.monitor_rsock
57
+ end
58
+
59
+ #
60
+ # This method cleans up the abstraction layer.
61
+ #
62
+ def cleanup_abstraction
63
+ self.lsock.close if (self.lsock)
64
+ self.rsock.close if (self.rsock)
65
+
66
+ self.lsock = nil
67
+ self.rsock = nil
68
+ end
69
+
70
+ #
71
+ # Low-level write to the local side.
72
+ #
73
+ def syswrite(buffer)
74
+ lsock.syswrite(buffer)
75
+ end
76
+
77
+ #
78
+ # Low-level read from the local side.
79
+ #
80
+ def sysread(length)
81
+ lsock.sysread(length)
82
+ end
83
+
84
+ #
85
+ # Shuts down the local side of the stream abstraction.
86
+ #
87
+ def shutdown(how)
88
+ lsock.shutdown(how)
89
+ end
90
+
91
+ #
92
+ # Closes both sides of the stream abstraction.
93
+ #
94
+ def close
95
+ cleanup_abstraction
96
+ end
97
+
98
+ #
99
+ # Symbolic peer information.
100
+ #
101
+ def peerinfo
102
+ "Remote-side of Pipe"
103
+ end
104
+
105
+ #
106
+ # Symbolic local information.
107
+ #
108
+ def localinfo
109
+ "Local-side of Pipe"
110
+ end
111
+
112
+ #
113
+ # The left side of the stream.
114
+ #
115
+ attr_reader :lsock
116
+ #
117
+ # The right side of the stream.
118
+ #
119
+ attr_reader :rsock
120
+
121
+ protected
122
+
123
+ def monitor_rsock
124
+ self.monitor_thread = Rex::ThreadFactory.spawn("StreamMonitorRemote", false) {
125
+ loop do
126
+ closed = false
127
+ buf = nil
128
+
129
+ if not self.rsock
130
+ wlog("monitor_rsock: the remote socket is nil, exiting loop")
131
+ break
132
+ end
133
+
134
+ begin
135
+ s = Rex::ThreadSafe.select( [ self.rsock ], nil, nil, 0.2 )
136
+ if( s == nil || s[0] == nil )
137
+ next
138
+ end
139
+ rescue Exception => e
140
+ wlog("monitor_rsock: exception during select: #{e.class} #{e}")
141
+ closed = true
142
+ end
143
+
144
+ if( closed == false )
145
+ begin
146
+ buf = self.rsock.sysread( 32768 )
147
+ if buf == nil
148
+ closed = true
149
+ wlog("monitor_rsock: closed remote socket due to nil read")
150
+ end
151
+ rescue EOFError => e
152
+ closed = true
153
+ dlog("monitor_rsock: EOF in rsock")
154
+ rescue ::Exception => e
155
+ closed = true
156
+ wlog("monitor_rsock: exception during read: #{e.class} #{e}")
157
+ end
158
+ end
159
+
160
+ if( closed == false )
161
+ total_sent = 0
162
+ total_length = buf.length
163
+ while( total_sent < total_length )
164
+ begin
165
+ data = buf[total_sent, buf.length]
166
+
167
+ # Note that this must be write() NOT syswrite() or put() or anything like it.
168
+ # Using syswrite() breaks SSL streams.
169
+ sent = self.write( data )
170
+
171
+ # sf: Only remove the data off the queue is write was successfull.
172
+ # This way we naturally perform a resend if a failure occured.
173
+ # Catches an edge case with meterpreter TCP channels where remote send
174
+ # failes gracefully and a resend is required.
175
+ if (sent.nil?)
176
+ closed = true
177
+ wlog("monitor_rsock: failed writing, socket must be dead")
178
+ break
179
+ elsif (sent > 0)
180
+ total_sent += sent
181
+ end
182
+ rescue ::IOError, ::EOFError => e
183
+ closed = true
184
+ wlog("monitor_rsock: exception during write: #{e.class} #{e}")
185
+ break
186
+ end
187
+ end
188
+ end
189
+
190
+ if( closed )
191
+ begin
192
+ self.close_write if self.respond_to?('close_write')
193
+ rescue IOError
194
+ end
195
+ break
196
+ end
197
+ end
198
+ }
199
+ end
200
+
201
+ protected
202
+ attr_accessor :monitor_thread
203
+ attr_writer :lsock
204
+ attr_writer :rsock
205
+
206
+ end
207
+
208
+ end; end
209
+
@@ -0,0 +1,221 @@
1
+ # -*- coding: binary -*-
2
+ require 'thread'
3
+
4
+ module SSLScan
5
+ module IO
6
+
7
+ ###
8
+ #
9
+ # This mixin provides the framework and interface for implementing a streaming
10
+ # server that can listen for and accept stream client connections. Stream
11
+ # servers extend this class and are required to implement the following
12
+ # methods:
13
+ #
14
+ # accept
15
+ # fd
16
+ #
17
+ ###
18
+ module StreamServer
19
+
20
+ ##
21
+ #
22
+ # Abstract methods
23
+ #
24
+ ##
25
+
26
+ ##
27
+ #
28
+ # Default server monitoring and client management implementation follows
29
+ # below.
30
+ #
31
+ ##
32
+
33
+ #
34
+ # This callback is notified when a client connects.
35
+ #
36
+ def on_client_connect(client)
37
+ if (on_client_connect_proc)
38
+ on_client_connect_proc.call(client)
39
+ end
40
+ end
41
+
42
+ #
43
+ # This callback is notified when a client connection has data that needs to
44
+ # be processed.
45
+ #
46
+ def on_client_data(client)
47
+ if (on_client_data_proc)
48
+ on_client_data_proc.call(client)
49
+ end
50
+ end
51
+
52
+ #
53
+ # This callback is notified when a client connection has closed.
54
+ #
55
+ def on_client_close(client)
56
+ if (on_client_close_proc)
57
+ on_client_close_proc.call(client)
58
+ end
59
+ end
60
+
61
+ #
62
+ # Start monitoring the listener socket for connections and keep track of
63
+ # all client connections.
64
+ #
65
+ def start
66
+ self.clients = []
67
+ self.client_waiter = ::Queue.new
68
+
69
+ self.listener_thread = SSLScan::ThreadFactory.spawn("StreamServerListener", false) {
70
+ monitor_listener
71
+ }
72
+ self.clients_thread = SSLScan::ThreadFactory.spawn("StreamServerClientMonitor", false) {
73
+ monitor_clients
74
+ }
75
+ end
76
+
77
+ #
78
+ # Terminates the listener monitoring threads and closes all active clients.
79
+ #
80
+ def stop
81
+ self.listener_thread.kill
82
+ self.clients_thread.kill
83
+
84
+ self.clients.each { |cli|
85
+ close_client(cli)
86
+ }
87
+ end
88
+
89
+ #
90
+ # This method closes a client connection and cleans up the resources
91
+ # associated with it.
92
+ #
93
+ def close_client(client)
94
+ if (client)
95
+ clients.delete(client)
96
+
97
+ begin
98
+ client.close
99
+ rescue IOError
100
+ end
101
+ end
102
+ end
103
+
104
+ #
105
+ # This method waits on the server listener thread
106
+ #
107
+ def wait
108
+ self.listener_thread.join if self.listener_thread
109
+ end
110
+
111
+ ##
112
+ #
113
+ # Callback procedures.
114
+ #
115
+ ##
116
+
117
+ #
118
+ # This callback procedure can be set and will be called when new clients
119
+ # connect.
120
+ #
121
+ attr_accessor :on_client_connect_proc
122
+ #
123
+ # This callback procedure can be set and will be called when clients
124
+ # have data to be processed.
125
+ #
126
+ attr_accessor :on_client_data_proc
127
+ #
128
+ # This callback procedure can be set and will be called when a client
129
+ # disconnects from the server.
130
+ #
131
+ attr_accessor :on_client_close_proc
132
+
133
+ attr_accessor :clients # :nodoc:
134
+ attr_accessor :listener_thread, :clients_thread # :nodoc:
135
+ attr_accessor :client_waiter
136
+
137
+ protected
138
+
139
+ #
140
+ # This method monitors the listener socket for new connections and calls
141
+ # the +on_client_connect+ callback routine.
142
+ #
143
+ def monitor_listener
144
+
145
+ while true
146
+ begin
147
+ cli = accept
148
+ if not cli
149
+ elog("The accept() returned nil in stream server listener monitor: #{fd.inspect}")
150
+ ::IO.select(nil, nil, nil, 0.10)
151
+ next
152
+ end
153
+
154
+ # Append to the list of clients
155
+ self.clients << cli
156
+
157
+ # Initialize the connection processing
158
+ on_client_connect(cli)
159
+
160
+ # Notify the client monitor
161
+ self.client_waiter.push(cli)
162
+
163
+ # Skip exceptions caused by accept() [ SSL ]
164
+ rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
165
+ rescue ::Interrupt
166
+ raise $!
167
+ rescue ::Exception
168
+ elog("Error in stream server server monitor: #{$!}")
169
+ rlog(ExceptionCallStack)
170
+ break
171
+ end
172
+ end
173
+ end
174
+
175
+ #
176
+ # This method monitors client connections for data and calls the
177
+ # +on_client_data+ routine when new data arrives.
178
+ #
179
+ def monitor_clients
180
+ begin
181
+
182
+ # Wait for a notify if our client list is empty
183
+ if (clients.length == 0)
184
+ self.client_waiter.pop
185
+ next
186
+ end
187
+
188
+ sd = SSLScan::ThreadSafe.select(clients, nil, nil, nil)
189
+
190
+ sd[0].each { |cfd|
191
+ begin
192
+ on_client_data(cfd)
193
+ rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
194
+ on_client_close(cfd)
195
+ close_client(cfd)
196
+ rescue ::Interrupt
197
+ raise $!
198
+ rescue ::Exception
199
+ close_client(cfd)
200
+ elog("Error in stream server client monitor: #{$!}")
201
+ rlog(ExceptionCallStack)
202
+
203
+ end
204
+ }
205
+
206
+ rescue ::SSLScan::StreamClosedError => e
207
+ # Remove the closed stream from the list
208
+ clients.delete(e.stream)
209
+ rescue ::Interrupt
210
+ raise $!
211
+ rescue ::Exception
212
+ elog("Error in stream server client monitor: #{$!}")
213
+ rlog(ExceptionCallStack)
214
+ end while true
215
+ end
216
+
217
+ end
218
+
219
+ end
220
+ end
221
+
@@ -0,0 +1,165 @@
1
+ module SSLScan
2
+ class Result
3
+
4
+ attr_accessor :openssl_sslv2
5
+
6
+ attr_reader :ciphers
7
+ attr_reader :supported_versions
8
+
9
+ def initialize()
10
+ @cert = nil
11
+ @ciphers = Set.new
12
+ @supported_versions = [:SSLv2, :SSLv3, :TLSv1]
13
+ end
14
+
15
+ def cert
16
+ @cert
17
+ end
18
+
19
+ def cert=(input)
20
+ unless input.kind_of? OpenSSL::X509::Certificate or input.nil?
21
+ raise ArgumentError, "Must be an X509 Cert!"
22
+ end
23
+ @cert = input
24
+ end
25
+
26
+ def sslv2
27
+ @ciphers.reject{|cipher| cipher[:version] != :SSLv2 }
28
+ end
29
+
30
+ def sslv3
31
+ @ciphers.reject{|cipher| cipher[:version] != :SSLv3 }
32
+ end
33
+
34
+ def tlsv1
35
+ @ciphers.reject{|cipher| cipher[:version] != :TLSv1 }
36
+ end
37
+
38
+ def weak_ciphers
39
+ accepted.reject{|cipher| cipher[:weak] == false }
40
+ end
41
+
42
+ def strong_ciphers
43
+ accepted.reject{|cipher| cipher[:weak] }
44
+ end
45
+
46
+ # Returns all accepted ciphers matching the supplied version
47
+ # @param version [Symbol, Array] The SSL Version to filter on
48
+ # @raise [ArgumentError] if the version supplied is invalid
49
+ # @return [Array] An array of accepted cipher details matching the supplied versions
50
+ def accepted(version = :all)
51
+ enum_ciphers(:accepted, version)
52
+ end
53
+
54
+ # Returns all rejected ciphers matching the supplied version
55
+ # @param version [Symbol, Array] The SSL Version to filter on
56
+ # @raise [ArgumentError] if the version supplied is invalid
57
+ # @return [Array] An array of rejected cipher details matching the supplied versions
58
+ def rejected(version = :all)
59
+ enum_ciphers(:rejected, version)
60
+ end
61
+
62
+ def each_accepted(version = :all)
63
+ accepted(version).each do |cipher_result|
64
+ yield cipher_result
65
+ end
66
+ end
67
+
68
+ def each_rejected(version = :all)
69
+ rejected(version).each do |cipher_result|
70
+ yield cipher_result
71
+ end
72
+ end
73
+
74
+ def supports_sslv2?
75
+ !(accepted(:SSLv2).empty?)
76
+ end
77
+
78
+ def supports_sslv3?
79
+ !(accepted(:SSLv3).empty?)
80
+ end
81
+
82
+ def supports_tlsv1?
83
+ !(accepted(:TLSv1).empty?)
84
+ end
85
+
86
+ def supports_ssl?
87
+ supports_sslv2? or supports_sslv3? or supports_tlsv1?
88
+ end
89
+
90
+ def supports_weak_ciphers?
91
+ !(weak_ciphers.empty?)
92
+ end
93
+
94
+ def standards_compliant?
95
+ if supports_ssl?
96
+ return false if supports_sslv2?
97
+ return false if supports_weak_ciphers?
98
+ end
99
+ true
100
+ end
101
+
102
+ # Adds the details of a cipher test to the Result object.
103
+ # @param version [Symbol] the SSL Version
104
+ # @param cipher [String] the SSL cipher
105
+ # @param key_length [Fixnum] the length of encryption key
106
+ # @param status [Symbol] :accepted or :rejected
107
+ def add_cipher(version, cipher, key_length, status)
108
+ unless @supported_versions.include? version
109
+ raise ArgumentError, "Must be a supported SSL Version"
110
+ end
111
+ unless OpenSSL::SSL::SSLContext.new(version).ciphers.flatten.include? cipher
112
+ raise ArgumentError, "Must be a valid SSL Cipher for #{version}!"
113
+ end
114
+ unless key_length.kind_of? Fixnum
115
+ raise ArgumentError, "Must supply a valid key length"
116
+ end
117
+ unless [:accepted, :rejected].include? status
118
+ raise ArgumentError, "Status must be either :accepted or :rejected"
119
+ end
120
+
121
+ strong_cipher_ctx = OpenSSL::SSL::SSLContext.new(version)
122
+ # OpenSSL Directive For Strong Ciphers
123
+ # See: http://www.rapid7.com/vulndb/lookup/ssl-weak-ciphers
124
+ strong_cipher_ctx.ciphers = "ALL:!aNULL:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM"
125
+
126
+ if strong_cipher_ctx.ciphers.flatten.include? cipher
127
+ weak = false
128
+ else
129
+ weak = true
130
+ end
131
+
132
+ cipher_details = {:version => version, :cipher => cipher, :key_length => key_length, :weak => weak, :status => status}
133
+ @ciphers << cipher_details
134
+ end
135
+
136
+ protected
137
+
138
+ # @param state [Symbol] Either :accepted or :rejected
139
+ # @param version [Symbol, Array] The SSL Version to filter on (:SSLv2, :SSLv3, :TLSv1, :all)
140
+ # @return [Set] The Set of cipher results matching the filter criteria
141
+ def enum_ciphers(state, version = :all)
142
+ case version
143
+ when Symbol
144
+ case version
145
+ when :all
146
+ return @ciphers.select { |cipher| cipher[:status] == state }
147
+ when :SSLv2, :SSLv3, :TLSv1
148
+ return @ciphers.select { |cipher| cipher[:status] == state and cipher[:version] == version }
149
+ else
150
+ raise ArgumentError, "Invalid SSL Version Supplied: #{version}"
151
+ end
152
+ when Array
153
+ version = version.reject{|v| !(@supported_versions.include? v)}
154
+ if version.empty?
155
+ return @ciphers.select{|cipher| cipher[:status] == state}
156
+ else
157
+ return @ciphers.select{|cipher| cipher[:status] == state and version.include? cipher[:version]}
158
+ end
159
+ else
160
+ raise ArgumentError, "Was expecting Symbol or Array and got #{version.class}"
161
+ end
162
+ end
163
+
164
+ end
165
+ end