sslsmart 1.0

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.
@@ -0,0 +1,287 @@
1
+ # Gursev Singh Kalra @ Foundstone(McAfee)
2
+ # Please see LICENSE.txt for licensing information
3
+
4
+ require 'net/https'
5
+ require 'singleton'
6
+ require 'uri'
7
+ require 'resultcontainer'
8
+ require 'sslsmartlog'
9
+
10
+ $log = SSLSmartLog.instance
11
+
12
+ module OpenSSL
13
+ module SSL
14
+ if(RUBY_VERSION =~ /^1\.8\.6/)
15
+ class SSLContext
16
+ RAW = 0
17
+ SPLIT = 1
18
+ DEFAULT_PARAMS = {
19
+ :verify_mode => OpenSSL::SSL::VERIFY_PEER,
20
+ :ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
21
+ :options => OpenSSL::SSL::OP_ALL,
22
+ }
23
+
24
+
25
+ def set_params(params={})
26
+ begin
27
+ $log.debug("Setting parameters #{params.inspect}") if(params!= {})
28
+ params = DEFAULT_PARAMS.merge(params)
29
+ params.each do|name, value|
30
+ next if(name == :ssl_version) #1.8.6 does not have ssl_version method
31
+ self.__send__("#{name}=", value)
32
+ end
33
+ rescue => ex
34
+ $log.error("#{ex.class}\t#{ex.message}")
35
+ raise
36
+ end
37
+ return params
38
+ end
39
+
40
+ end
41
+
42
+ end #End of if(RUBY_VERSION =~ /^1\.8/)
43
+
44
+
45
+ def SSL.get_cipher_suites(filter)
46
+ $log.debug("requesting cipher suites with filter")
47
+ # Making the filters case insensitive
48
+ filter = filter.gsub(/tlsv1/i, "TLSv1").gsub(/sslv3/i, "SSLv3").gsub(/sslv2/i, "SSLv2")
49
+ version = :SSLv23
50
+ begin
51
+ # RUBY 1.8.6 OpenSSL api is not compatible with 1.9 for purpose required here
52
+ # Using the existing implementation to get ciphers suites
53
+ ncontext = OpenSSL::SSL::SSLContext.new
54
+ ohash = {:ciphers => filter, :ssl_version => version}
55
+ ncontext.set_params(ohash)
56
+ return ncontext.ciphers
57
+ rescue => ex
58
+ $log.error(ex.message)
59
+ raise ex
60
+ end
61
+ end
62
+
63
+
64
+ def SSL.get_mod_cipher_suites(filter)
65
+ $log.debug("requesting modified cipher suites with filter")
66
+ begin
67
+ suites = OpenSSL::SSL.get_cipher_suites(filter)
68
+ rescue => ex
69
+ $log.error(ex.message)
70
+ raise ex
71
+ end
72
+
73
+ sslv2 = []
74
+ sslv3 = []
75
+ tlsv1 = []
76
+ suites.each do |x|
77
+ case x[1]
78
+ when "SSLv2"
79
+ sslv2 << x
80
+ when "TLSv1/SSLv3"
81
+ #Suite Name -- Version -- Key Length -- Cipher Supported Key Length
82
+ sslv3 << [x[0], "SSLv3", x[2],x[3]]
83
+ tlsv1 << [x[0], "TLSv1", x[2],x[3]]
84
+ end
85
+ end
86
+ sslv2 + sslv3 + tlsv1
87
+ end
88
+
89
+ end
90
+ end
91
+
92
+
93
+ module Net
94
+ class HTTP
95
+ # Required for Ruby 1.9 support.
96
+ attr_accessor :enable_post_connection_check
97
+ end
98
+ end
99
+
100
+ module Net
101
+ class HTTP
102
+
103
+ CONNECT = 0
104
+ CONTENT = 1
105
+ CA_ROOT_BUNDLE = "rootcerts.pem"
106
+
107
+
108
+ # Verifies a given SSL configuration. It performs
109
+ # Few valid function calls
110
+ # rq.verify_ssl_config(:SSLv2, nil, Net::HTTP::CONTENT, "/")
111
+ # rq.verify_ssl_config(:SSLv3, ["AES256-SHA", "DES-CBC3-SHA","EXP-RC2-CBC-MD5"], Net::HTTP::CONTENT, "/")
112
+ # rq.verify_ssl_config(:SSLv3, "AES256-SHA", Net::HTTP::CONTENT, "/")
113
+ def verify_ssl_config(version, cipher_suite, scan_type, *args)
114
+ $log.debug("Verifying ssl configuration #{version}\t#{cipher_suite}\t#{scan_type}")
115
+ set_ssl_config(version, cipher_suite)
116
+ disable_validations
117
+
118
+ case scan_type
119
+ when CONNECT
120
+ return self.ssl_connect
121
+ when CONTENT
122
+ begin
123
+ response = self.get(*args)
124
+ return ResultContainer.new(true, response)
125
+ rescue OpenSSL::SSL::SSLError => ex
126
+ return ResultContainer.new(false, ex)
127
+ rescue => ex1
128
+ return ResultContainer.new(false, ex1)
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ # Set SSL configuration for an HTTP Request.
135
+ # version provides the SSL version to be used for request. The value can be :SSLv2, :SSLv3, TLSv1 or SSLv23
136
+ # cipher_suite accepts the value of cipher suite to be used for a given request.
137
+ # The cipher suite values supported for a give version can be obtained by Net::HTTP::get_ssl_ciphers or Net::HTTP::get_ssl_ciphers_and_keylen class methods
138
+ def set_ssl_config(version, cipher_suite = nil)
139
+ $log.debug("Setting ssl configuration #{version}, #{cipher_suite}")
140
+ self.use_ssl = true
141
+
142
+ self.set_context = version
143
+ if(self.respond_to?(:ssl_version)) # For Ruby 1.9
144
+ self.ssl_version = version
145
+ end
146
+ self.ciphers = cipher_suite if(cipher_suite)
147
+ disable_validations
148
+ end
149
+
150
+
151
+ # Returns SSL Certificate details
152
+ def get_cert_details()
153
+ $log.debug("Returning certificate details")
154
+ self.use_ssl = true
155
+ disable_validations
156
+ begin
157
+ self.start do |x|
158
+ return ResultContainer.new(true, x.peer_cert)
159
+ end
160
+ rescue => ex
161
+ return ResultContainer.new(false, ex)
162
+ end
163
+ end
164
+
165
+
166
+ # PATH to Bundle of CA Root Certificates has to be prvided as a ca_root_bundle parameter.
167
+ # Mozilla Firefox Bundle of CA Root Certificates can be downloade from http://curl.haxx.se/ca/cacert.pem for your own use.
168
+ def verify_cert(ca_root_bundle = CA_ROOT_BUNDLE)
169
+ $log.debug("Verifying certificate")
170
+ ca_root_bundle = CA_ROOT_BUNDLE unless(ca_root_bundle)
171
+ unless (File.exist?(ca_root_bundle) && File.file?(ca_root_bundle))
172
+ return ResultContainer.new(false, "Invalid certificate file #{ca_root_bundle}")
173
+ end
174
+
175
+ self.use_ssl = true
176
+ enable_validations
177
+ self.ca_file = ca_root_bundle
178
+ begin
179
+ self.start do |x|
180
+ x.peer_cert
181
+ return ResultContainer.new(true, x.peer_cert)
182
+ end
183
+ rescue => ex
184
+ return ResultContainer.new(false, ex)
185
+ end
186
+ end
187
+
188
+
189
+ # This block supports connect scans only
190
+ def ssl_connect()
191
+ $log.debug("Initiating SSL Connect Test")
192
+ disable_validations
193
+ s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
194
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
195
+ s.sync_close = true
196
+
197
+ @socket = BufferedIO.new(s)
198
+ @socket.read_timeout = @read_timeout
199
+ @socket.debug_output = @debug_output
200
+
201
+ if proxy?
202
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', @address, @port, HTTPVersion)
203
+ @socket.writeline "Host: #{@address}:#{@port}"
204
+ @socket.writeline ''
205
+ HTTPResponse.read_new(@socket).value
206
+ end
207
+
208
+ begin
209
+ s.connect
210
+ return ResultContainer.new(true, nil)
211
+ rescue OpenSSL::SSL::SSLError => ex
212
+ return ResultContainer.new(false, ex)
213
+ rescue => ex2
214
+ return ResultContainer.new(false, ex2)
215
+ end
216
+ end
217
+
218
+
219
+ def enable_validations
220
+ #$log.debug("Enabling all validations")
221
+ self.enable_post_connection_check = true
222
+ verify_peer
223
+ end
224
+
225
+
226
+ def disable_validations
227
+ #$log.debug("Disabling all validations")
228
+ self.enable_post_connection_check = false
229
+ verify_none
230
+ end
231
+
232
+
233
+ # Turns on certificate validation for given HTTP request. If not used, a warning will be seen during the enumeration.
234
+ def verify_peer
235
+ #$log.debug("Enabling peer verification")
236
+ self.verify_mode = OpenSSL::SSL::VERIFY_PEER
237
+ end
238
+
239
+
240
+ # Turns off certificate validation for given HTTP request. If not used, a warning will be seen during the enumeration.
241
+ def verify_none
242
+ #$log.debug("Disabling peer validation")
243
+ self.verify_mode = OpenSSL::SSL::VERIFY_NONE
244
+ end
245
+
246
+
247
+ # Sets context for a given HTTP request. The values accepted are any of valid SSL versions
248
+ def set_context=(value)
249
+ $log.debug("Setting context to #{value}")
250
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new #Create a new context
251
+ @ssl_context &&= OpenSSL::SSL::SSLContext.new(value)
252
+ end
253
+
254
+
255
+ # Creating a private which allows ciphers to be set for a given HTTP request.
256
+ if RUBY_VERSION =~ /^1\.8/
257
+ ssl_context_accessor :ciphers
258
+ end
259
+
260
+ end
261
+ end
262
+
263
+ # Single unified interface to generate ciphers for OpenSSL filters
264
+ def get_cipher_suites(filter)
265
+
266
+ $log.debug("Requesting for get_cipher_suites standalone method of SSLSmartLib")
267
+ version = :SSLv23
268
+ begin
269
+ # RUBY 1.8.6 OpenSSL api is not compatible with 1.9 for purpose required here
270
+ # Using the existing implementation to get ciphers suites
271
+ if(RUBY_VERSION =~ /^1\.8/)
272
+ rq = Net::HTTP.new('dummy')
273
+ rq.set_ssl_config(version, filter) # Either this line of the three commented lines do the JOB
274
+ return rq.ciphers
275
+ elsif(RUBY_VERSION =~ /^1\.9/)
276
+ # RUBY 1.9 library call does not return cipher array when ciphers function is called on an HTTP object.
277
+ # The code below talks directly to OpenSSL Library and extracts the information.
278
+ ncontext = OpenSSL::SSL::SSLContext.new
279
+ ohash = {:ciphers => filter, :ssl_version => version}
280
+ ncontext.set_params(ohash)
281
+ return ncontext.ciphers
282
+ end
283
+ rescue => ex
284
+ $log.error(ex.message)
285
+ raise ex
286
+ end
287
+ end
@@ -0,0 +1,35 @@
1
+ # Gursev Singh Kalra @ Foundstone(McAfee)
2
+ # Please see LICENSE.txt for licensing information
3
+
4
+ require 'logger'
5
+ require 'singleton'
6
+
7
+ class SSLSmartLog
8
+ include Singleton
9
+ def initialize
10
+ @logfile = Logger.new('SSLSmart.log', 'daily')
11
+ @logfile.level = Logger::INFO
12
+ #@logfile.level = Logger::DEBUG
13
+ end
14
+
15
+ def fatal(message)
16
+ @logfile.fatal(message)
17
+ end
18
+
19
+ def error(message)
20
+ @logfile.error(message)
21
+ end
22
+
23
+ def warn(message)
24
+ @logfile.warn(message)
25
+ end
26
+
27
+ def info(message)
28
+ @logfile.info(message)
29
+ end
30
+
31
+ def debug(message)
32
+ @logfile.debug(message)
33
+ end
34
+
35
+ end
@@ -0,0 +1,50 @@
1
+ # Gursev Singh Kalra @ Foundstone(McAfee)
2
+ # Please see LICENSE.txt for licensing information
3
+
4
+ require 'uri'
5
+ require 'sslsmartlog'
6
+
7
+ $log = SSLSmartLog.instance
8
+
9
+ module SSLSmartMisc
10
+
11
+ def convert_to_url(line)
12
+ line.strip!
13
+ if(line == "")
14
+ $log.warn("No hostname provided")
15
+ return nil
16
+ end
17
+
18
+ line.sub!(/^https?:\/\//,"") if(line =~ /^https?:\/\//)
19
+ line[0,0] = 'https://'
20
+ begin
21
+ uri = URI.parse(line)
22
+ rescue URI::Error => e
23
+ $log.warn(e.message)
24
+ return nil
25
+ end
26
+
27
+ uri.path = "/" if(uri.path == "")
28
+ uri.to_s
29
+
30
+ end
31
+
32
+
33
+ def get_urls_from_file(filename)
34
+
35
+ unless(File.file?(filename))
36
+ $log.error("Invalid Filename #{filename}")
37
+ return nil
38
+ end
39
+
40
+ urls = []
41
+ File.open(filename) do |f|
42
+ f.each do |line|
43
+ url = convert_to_url(line) if(line.strip != "")
44
+ urls << url if(url)
45
+ end
46
+ end
47
+ urls
48
+ end
49
+
50
+ end
@@ -0,0 +1,19 @@
1
+ # Gursev Singh Kalra @ Foundstone
2
+ require 'bin/sslsmartlib'
3
+
4
+ host = ARGV[0]
5
+ port = ARGV[1]
6
+
7
+ suites = OpenSSL::SSL.get_mod_cipher_suites("SSLv3:!NULL:!aNULL")
8
+ q = Net::HTTP.new(host,port)
9
+
10
+ suites.each do |suite, version, bits, klen|
11
+ a = q.verify_ssl_config(version, suite, Net::HTTP::CONTENT, "/")
12
+ case a.status
13
+ when true
14
+ print "\n[+] Accepted%7s %-25s%5s bits %-s" % [version, suite, bits, a.data.body]
15
+ when false
16
+ print "\n[-] Rejected%7s %-25s%5s bits %-s" % [version, suite, bits, a.data]
17
+ end
18
+ end
19
+
@@ -0,0 +1,20 @@
1
+ # Gursev Singh Kalra @ Foundstone(McAfee)
2
+ # Please see LICENSE.txt for licensing information
3
+
4
+ require 'rubygems'
5
+ SPEC = Gem::Specification.new do |s|
6
+ s.name = 'sslsmart'
7
+ s.version = '1.0'
8
+ s.authors = ['Gursev Singh Kalra']
9
+ s.email = %q{gursev.kalra@foundstone.com}
10
+ s.files = ['bin/fs_icon_32.ico', 'bin/resultcontainer.rb', 'bin/rootcerts.pem', 'bin/sslsmartconfig.rb', 'bin/sslsmartcontroller.rb', 'bin/sslsmartdb.rb', 'bin/sslsmartgui.rb', 'bin/sslsmartlib.rb', 'bin/sslsmartlog.rb', 'bin/sslsmartmisc.rb', 'sslsmart.gemspec', 'README', 'sample.rb', 'LICENSE.txt']
11
+ s.homepage = 'http://www.foundstone.com'
12
+ s.platform = Gem::Platform::RUBY
13
+ s.require_paths = ["bin"]
14
+ s.summary = 'SSLSmart is a smart SSL cipher enumeration tool'
15
+ s.description = 'SSLSmart is an advanced and highly flexible Ruby based smart SSL cipher enumeration tool'
16
+ s.rubyforge_project = 'none'
17
+ s.add_dependency("wxruby", ">=2.0.0")
18
+ s.add_dependency("builder", ">=2.1.2")
19
+
20
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sslsmart
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ version: "1.0"
10
+ platform: ruby
11
+ authors:
12
+ - Gursev Singh Kalra
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-16 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: wxruby
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 15
29
+ segments:
30
+ - 2
31
+ - 0
32
+ - 0
33
+ version: 2.0.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: builder
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 15
45
+ segments:
46
+ - 2
47
+ - 1
48
+ - 2
49
+ version: 2.1.2
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: SSLSmart is an advanced and highly flexible Ruby based smart SSL cipher enumeration tool
53
+ email: gursev.kalra@foundstone.com
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ extra_rdoc_files: []
59
+
60
+ files:
61
+ - bin/fs_icon_32.ico
62
+ - bin/resultcontainer.rb
63
+ - bin/rootcerts.pem
64
+ - bin/sslsmartconfig.rb
65
+ - bin/sslsmartcontroller.rb
66
+ - bin/sslsmartdb.rb
67
+ - bin/sslsmartgui.rb
68
+ - bin/sslsmartlib.rb
69
+ - bin/sslsmartlog.rb
70
+ - bin/sslsmartmisc.rb
71
+ - sslsmart.gemspec
72
+ - README
73
+ - sample.rb
74
+ - LICENSE.txt
75
+ has_rdoc: true
76
+ homepage: http://www.foundstone.com
77
+ licenses: []
78
+
79
+ post_install_message:
80
+ rdoc_options: []
81
+
82
+ require_paths:
83
+ - bin
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project: none
105
+ rubygems_version: 1.3.7
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: SSLSmart is a smart SSL cipher enumeration tool
109
+ test_files: []
110
+