Skip to content

Commit d5a0b81

Browse files
author
Tod Beardsley
committed
Land rapid7#4024, auto-negotiate SSL versions
Thanks @hmoore-r7!
2 parents 8ec0aa0 + 5a350b2 commit d5a0b81

File tree

5 files changed

+53
-13
lines changed

5 files changed

+53
-13
lines changed

lib/msf/core/auxiliary/crawler.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def initialize(info = {})
4444
OptString.new('HTTPAdditionalHeaders', [false, "A list of additional headers to send (separated by \\x01)"]),
4545
OptString.new('HTTPCookie', [false, "A HTTP cookie header to send with each request"]),
4646
OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]),
47-
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL23', 'SSL3', 'TLS1']]),
47+
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'Auto', ['Auto', 'SSL2', 'SSL23', 'SSL3', 'TLS1']]),
4848
], self.class
4949
)
5050

lib/msf/core/exploit/http/client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def initialize(info = {})
5050
OptString.new('PASSWORD', [false, 'The HTTP password to specify for authentication', '']),
5151
OptBool.new('DigestAuthIIS', [false, 'Conform to IIS, should work for most servers. Only set to false for non-IIS servers', true]),
5252
OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]),
53-
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]),
53+
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'Auto', ['Auto', 'SSL2', 'SSL3', 'TLS1']]),
5454
OptBool.new('FingerprintCheck', [ false, 'Conduct a pre-exploit fingerprint verification', true]),
5555
OptString.new('DOMAIN', [ true, 'The domain to use for windows authentification', 'WORKSTATION'])
5656
], self.class

lib/msf/core/exploit/tcp.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def initialize(info = {})
6262
register_advanced_options(
6363
[
6464
OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]),
65-
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]),
65+
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'Auto', ['Auto', 'SSL2', 'SSL3', 'TLS1']]),
6666
OptEnum.new('SSLVerifyMode', [ false, 'SSL verification method', 'PEER', %W{CLIENT_ONCE FAIL_IF_NO_PEER_CERT NONE PEER}]),
6767
OptString.new('SSLCipher', [ false, 'String for SSL cipher - "DHE-RSA-AES256-SHA" or "ADH"']),
6868
Opt::Proxies,
@@ -290,7 +290,7 @@ def initialize(info = {})
290290
register_options(
291291
[
292292
OptBool.new('SSL', [ false, 'Negotiate SSL for incoming connections', false]),
293-
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]),
293+
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'Auto', ['Auto', 'SSL2', 'SSL3', 'TLS1']]),
294294
OptPath.new('SSLCert', [ false, 'Path to a custom SSL certificate (default is randomly generated)']),
295295
OptAddress.new('SRVHOST', [ true, "The local host to listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]),
296296
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 8080 ]),

lib/rex/socket/parameters.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def self.from_hash(hash)
5656
# @option hash [Bool] 'Bool' Create a bare socket
5757
# @option hash [Bool] 'Server' Whether or not this should be a server
5858
# @option hash [Bool] 'SSL' Whether or not SSL should be used
59-
# @option hash [String] 'SSLVersion' Specify SSL2, SSL3, or TLS1 (SSL3 is
59+
# @option hash [String] 'SSLVersion' Specify Auto, SSL2, SSL3, or TLS1 (Auto is
6060
# default)
6161
# @option hash [String] 'SSLCert' A file containing an SSL certificate (for
6262
# server sockets)
@@ -117,7 +117,7 @@ def initialize(hash)
117117
self.ssl = false
118118
end
119119

120-
supported_ssl_versions = ['SSL2', 'SSL23', 'TLS1', 'SSL3', :SSLv2, :SSLv3, :SSLv23, :TLSv1]
120+
supported_ssl_versions = ['Auto', 'SSL2', 'SSL23', 'TLS1', 'SSL3', :Auto, :SSLv2, :SSLv3, :SSLv23, :TLSv1]
121121
if (hash['SSLVersion'] and supported_ssl_versions.include? hash['SSLVersion'])
122122
self.ssl_version = hash['SSLVersion']
123123
end
@@ -324,7 +324,7 @@ def v6?
324324
# @return [Bool]
325325
attr_accessor :ssl
326326

327-
# What version of SSL to use (SSL2, SSL3, SSL23, TLS1)
327+
# What version of SSL to use (Auto, SSL2, SSL3, SSL23, TLS1)
328328
# @return [String,Symbol]
329329
attr_accessor :ssl_version
330330

lib/rex/socket/ssl_tcp.rb

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,55 @@ def self.create_param(param)
5656
def initsock(params = nil)
5757
super
5858

59-
version = :SSLv3
60-
if(params)
59+
# The autonegotiation preference for SSL/TLS versions
60+
versions = [:TLSv1, :SSLv3, :SSLv23, :SSLv2]
61+
62+
# Limit this to a specific SSL/TLS version if specified
63+
if params
6164
case params.ssl_version
6265
when 'SSL2', :SSLv2
63-
version = :SSLv2
66+
versions = [:SSLv2]
6467
when 'SSL23', :SSLv23
65-
version = :SSLv23
68+
versions = [:SSLv23]
69+
when 'SSL3', :SSLv3
70+
versions = [:SSLv3]
6671
when 'TLS1', :TLSv1
67-
version = :TLSv1
72+
versions = [:TLSv1]
73+
else
74+
# Leave the version list as-is (Auto)
6875
end
6976
end
7077

78+
# Limit our versions to those supported by the linked OpenSSL library
79+
versions = versions.select {|v| OpenSSL::SSL::SSLContext::METHODS.include? v }
80+
81+
# Raise an error if no selected versions are supported
82+
if versions.length == 0
83+
raise ArgumentError, 'The system OpenSSL does not support the requested SSL/TLS version'
84+
end
85+
86+
last_error = nil
87+
88+
# Iterate through SSL/TLS versions until we successfully negotiate
89+
versions.each do |version|
90+
begin
91+
# Try intializing the socket with this SSL/TLS version
92+
# This will throw an exception if it fails
93+
initsock_with_ssl_version(params, version)
94+
95+
# Success! Record what method was used and return
96+
self.ssl_negotiated_version = version
97+
return
98+
rescue OpenSSL::SSL::SSLError => e
99+
last_error = e
100+
end
101+
end
102+
103+
# No SSL/TLS versions succeeded, raise the last error
104+
raise last_error
105+
end
106+
107+
def initsock_with_ssl_version(params, version)
71108
# Build the SSL connection
72109
self.sslctx = OpenSSL::SSL::SSLContext.new(version)
73110

@@ -84,7 +121,9 @@ def initsock(params = nil)
84121
# Could also do this as graceful faildown in case a passed verify_mode is not supported
85122
self.sslctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
86123
end
124+
87125
self.sslctx.options = OpenSSL::SSL::OP_ALL
126+
88127
if params.ssl_cipher
89128
self.sslctx.ciphers = params.ssl_cipher
90129
end
@@ -101,7 +140,6 @@ def initsock(params = nil)
101140
# XXX - enabling this causes infinite recursion, so disable for now
102141
# self.sslsock.sync_close = true
103142

104-
105143
# Force a negotiation timeout
106144
begin
107145
Timeout.timeout(params.timeout) do
@@ -327,11 +365,13 @@ def allow_nonblock?
327365
end
328366

329367
attr_reader :peer_verified # :nodoc:
368+
attr_reader :ssl_negotiated_version # :nodoc:
330369
attr_accessor :sslsock, :sslctx # :nodoc:
331370

332371
protected
333372

334373
attr_writer :peer_verified # :nodoc:
374+
attr_writer :ssl_negotiated_version # :nodoc:
335375

336376

337377
rescue LoadError

0 commit comments

Comments
 (0)