sslsmart 1.0

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