Rework meterpreter SSL & pass datastore to handle_connection()
This allows HandlerSSLCert to be used to pass a SSL certificate into the Meterpreter handler. The datastore has to be passed into handle_connection() for this to work, as SSL needs to be initialized on Session.new. This still doesn't pass the datastore into Meterpreter directly, but allows the Session::Meterpreter code to extract and pass down the :ssl_cert option if it was specified. This also fixes SSL certificate caching by expiring the cached cert from the class variables if the configuration has changed. A final change is to create a new SSL SessionID for each connection versus reusing the SSL context, which is incorrect and may lead to problems in the future (if not already).bug/bundler_fix
parent
b34ddbdfff
commit
673e21cfaf
|
@ -30,10 +30,12 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
|
||||
include Msf::Session::Scriptable
|
||||
|
||||
# Override for server implementations that can't do ssl
|
||||
# Override for server implementations that can't do SSL
|
||||
def supports_ssl?
|
||||
true
|
||||
end
|
||||
|
||||
# Override for server implementations that can't do zlib
|
||||
def supports_zlib?
|
||||
true
|
||||
end
|
||||
|
@ -49,11 +51,24 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
:ssl => supports_ssl?,
|
||||
:zlib => supports_zlib?
|
||||
}
|
||||
|
||||
# The caller didn't request to skip ssl, so make sure we support it
|
||||
if not opts[:skip_ssl]
|
||||
# the caller didn't request to skip ssl, so make sure we support it
|
||||
opts.merge!(:skip_ssl => (not supports_ssl?))
|
||||
end
|
||||
|
||||
#
|
||||
# Parse options passed in via the datastore
|
||||
#
|
||||
|
||||
# Extract the HandlerSSLCert option if specified by the user
|
||||
if opts[:datastore] and opts[:datastore]['HandlerSSLCert']
|
||||
opts[:ssl_cert] = opts[:datastore]['HandlerSSLCert']
|
||||
end
|
||||
|
||||
# Don't pass the datastore into the init_meterpreter method
|
||||
opts.delete(:datastore)
|
||||
|
||||
#
|
||||
# Initialize the meterpreter client
|
||||
#
|
||||
|
|
|
@ -15,7 +15,8 @@ module MeterpreterOptions
|
|||
OptString.new('InitialAutoRunScript', [false, "An initial script to run on session creation (before AutoRunScript)", '']),
|
||||
OptString.new('AutoRunScript', [false, "A script to run automatically on session creation.", '']),
|
||||
OptBool.new('AutoSystemInfo', [true, "Automatically capture system information on initialization.", true]),
|
||||
OptBool.new('EnableUnicodeEncoding', [true, "Automatically encode UTF-8 strings as hexadecimal", true])
|
||||
OptBool.new('EnableUnicodeEncoding', [true, "Automatically encode UTF-8 strings as hexadecimal", true]),
|
||||
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ module BindTcp
|
|||
# to implement the Stream interface.
|
||||
conn_threads << framework.threads.spawn("BindTcpHandlerSession", false, client) { |client_copy|
|
||||
begin
|
||||
handle_connection(wrap_aes_socket(client_copy))
|
||||
handle_connection(wrap_aes_socket(client_copy), { datastore: datastore })
|
||||
rescue
|
||||
elog("Exception raised from BindTcp.handle_connection: #{$!}")
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ module FindPort
|
|||
# If this is a multi-stage payload, then we just need to blindly
|
||||
# transmit the stage and create the session, hoping that it works.
|
||||
if (self.payload_type != Msf::Payload::Type::Single)
|
||||
handle_connection(sock)
|
||||
handle_connection(sock, { datastore: datastore })
|
||||
# Otherwise, check to see if we found a session. We really need
|
||||
# to improve this, as we could create a session when the exploit
|
||||
# really didn't succeed.
|
||||
|
|
|
@ -163,10 +163,10 @@ module ReverseTcp
|
|||
begin
|
||||
if datastore['ReverseListenerThreaded']
|
||||
self.conn_threads << framework.threads.spawn("ReverseTcpHandlerSession-#{local_port}-#{client.peerhost}", false, client) { | client_copy|
|
||||
handle_connection(wrap_aes_socket(client_copy))
|
||||
handle_connection(wrap_aes_socket(client_copy), { datastore: datastore })
|
||||
}
|
||||
else
|
||||
handle_connection(wrap_aes_socket(client))
|
||||
handle_connection(wrap_aes_socket(client), { datastore: datastore })
|
||||
end
|
||||
rescue ::Exception
|
||||
elog("Exception raised from handle_connection: #{$!.class}: #{$!}\n\n#{$@.join("\n")}")
|
||||
|
|
|
@ -120,7 +120,7 @@ module ReverseTcpDouble
|
|||
begin
|
||||
sock_inp, sock_out = detect_input_output(client_a_copy, client_b_copy)
|
||||
chan = TcpReverseDoubleSessionChannel.new(framework, sock_inp, sock_out)
|
||||
handle_connection(chan.lsock)
|
||||
handle_connection(chan.lsock, { datastore: datastore })
|
||||
rescue
|
||||
elog("Exception raised from handle_connection: #{$!}\n\n#{$@.join("\n")}")
|
||||
end
|
||||
|
|
|
@ -121,7 +121,7 @@ module ReverseTcpDoubleSSL
|
|||
begin
|
||||
sock_inp, sock_out = detect_input_output(client_a_copy, client_b_copy)
|
||||
chan = TcpReverseDoubleSSLSessionChannel.new(framework, sock_inp, sock_out)
|
||||
handle_connection(chan.lsock)
|
||||
handle_connection(chan.lsock, { datastore: datastore })
|
||||
rescue
|
||||
elog("Exception raised from handle_connection: #{$!}\n\n#{$@.join("\n")}")
|
||||
end
|
||||
|
|
|
@ -42,9 +42,14 @@ class Client
|
|||
@@ext_hash = {}
|
||||
|
||||
#
|
||||
# Cached SSL certificate (required to scale)
|
||||
# Cached SSL context (required to scale)
|
||||
#
|
||||
@@ssl_ctx = nil
|
||||
@@ssl_cert_info = nil
|
||||
|
||||
#
|
||||
# Cached SSL certificate
|
||||
#
|
||||
@@ssl_cached_cert = nil
|
||||
|
||||
#
|
||||
# Mutex to synchronize class-wide operations
|
||||
|
@ -116,9 +121,21 @@ class Client
|
|||
|
||||
self.response_timeout = opts[:timeout] || self.class.default_timeout
|
||||
self.send_keepalives = true
|
||||
|
||||
# TODO: Clarify why we don't allow unicode to be set in initial options
|
||||
# self.encode_unicode = opts.has_key?(:encode_unicode) ? opts[:encode_unicode] : true
|
||||
self.encode_unicode = false
|
||||
|
||||
# The SSL certificate is being passed down as a file path
|
||||
if opts[:ssl_cert]
|
||||
if ! File.exists? opts[:ssl_cert]
|
||||
elog("SSL certificate at #{opts[:ssl_cert]} does not exist and will be ignored")
|
||||
else
|
||||
# Load the certificate the same way that SslTcpServer does it
|
||||
self.ssl_cert = ::File.read(opts[:ssl_cert])
|
||||
end
|
||||
end
|
||||
|
||||
if opts[:passive_dispatcher]
|
||||
initialize_passive_dispatcher
|
||||
|
||||
|
@ -200,68 +217,48 @@ class Client
|
|||
end
|
||||
|
||||
def generate_ssl_context
|
||||
|
||||
# Initialize a null context
|
||||
ctx = nil
|
||||
|
||||
# Synchronize to prevent race conditions
|
||||
@@ssl_mutex.synchronize do
|
||||
if not @@ssl_ctx
|
||||
|
||||
# If the user specified a certificate and its not the cached one, delete the cached info
|
||||
if self.ssl_cert && self.ssl_cert != @@ssl_cached_cert
|
||||
@ssl_ctx = nil
|
||||
end
|
||||
|
||||
# If the user did not specify a certificate and we have cached one, delete the cached info
|
||||
if ! self.ssl_cert && @@ssl_cached_cert
|
||||
@@ssl_cert_info = nil
|
||||
end
|
||||
|
||||
unless @@ssl_cert_info
|
||||
# If no certificate was specified, generate one
|
||||
unless self.ssl_cert
|
||||
wlog("Generating SSL certificate for Meterpreter sessions")
|
||||
|
||||
key = OpenSSL::PKey::RSA.new(1024){ }
|
||||
cert = OpenSSL::X509::Certificate.new
|
||||
cert.version = 2
|
||||
cert.serial = rand(0xFFFFFFFF)
|
||||
|
||||
# Depending on how the socket was created, getsockname will
|
||||
# return either a struct sockaddr as a String (the default ruby
|
||||
# Socket behavior) or an Array (the extend'd Rex::Socket::Tcp
|
||||
# behavior). Avoid the ambiguity by always picking a random
|
||||
# hostname. See #7350.
|
||||
subject_cn = Rex::Text.rand_hostname
|
||||
|
||||
subject = OpenSSL::X509::Name.new([
|
||||
["C","US"],
|
||||
['ST', Rex::Text.rand_state()],
|
||||
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["CN", subject_cn],
|
||||
])
|
||||
issuer = OpenSSL::X509::Name.new([
|
||||
["C","US"],
|
||||
['ST', Rex::Text.rand_state()],
|
||||
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["CN", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
])
|
||||
|
||||
cert.subject = subject
|
||||
cert.issuer = issuer
|
||||
cert.not_before = Time.now - (3600 * 365) + rand(3600 * 14)
|
||||
cert.not_after = Time.now + (3600 * 365) + rand(3600 * 14)
|
||||
cert.public_key = key.public_key
|
||||
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
|
||||
cert.extensions = [
|
||||
ef.create_extension("basicConstraints","CA:FALSE"),
|
||||
ef.create_extension("subjectKeyIdentifier","hash"),
|
||||
ef.create_extension("extendedKeyUsage","serverAuth"),
|
||||
ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
|
||||
]
|
||||
ef.issuer_certificate = cert
|
||||
cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
|
||||
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
||||
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.key = key
|
||||
ctx.cert = cert
|
||||
|
||||
ctx.session_id_context = Rex::Text.rand_text(16)
|
||||
|
||||
@@ssl_cert_info = Rex::Socket::SslTcpServer.ssl_generate_certificate
|
||||
wlog("Generated SSL certificate for Meterpreter sessions")
|
||||
# Load the user's specified certificate
|
||||
else
|
||||
wlog("Loading custom SSL certificate for Meterpreter sessions")
|
||||
@@ssl_cert_info = Rex::Socket::SslTcpServer.ssl_parse_pem(self.ssl_cert)
|
||||
wlog("Loaded custom SSL certificate for Meterpreter sessions")
|
||||
@@ssl_cached_cert = self.ssl_cert
|
||||
end
|
||||
end
|
||||
|
||||
@@ssl_ctx = ctx
|
||||
|
||||
end # End of if not @ssl_ctx
|
||||
# Create a new context for each session
|
||||
ctx = OpenSSL::SSL::SSLContext.new()
|
||||
ctx.key = @@ssl_cert_info[0]
|
||||
ctx.cert = @@ssl_cert_info[1]
|
||||
ctx.extra_chain_cert = @@ssl_cert_info[2]
|
||||
ctx.options = 0
|
||||
ctx.session_id_context = Rex::Text.rand_text(16)
|
||||
end # End of mutex.synchronize
|
||||
|
||||
@@ssl_ctx
|
||||
ctx
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -453,6 +450,10 @@ class Client
|
|||
#
|
||||
attr_accessor :ssl
|
||||
#
|
||||
# Use this SSL Certificate (unified PEM)
|
||||
#
|
||||
attr_accessor :ssl_cert
|
||||
#
|
||||
# The Session Expiration Timeout
|
||||
#
|
||||
attr_accessor :expiration
|
||||
|
|
Loading…
Reference in New Issue