diff --git a/Gemfile.lock b/Gemfile.lock index 08e8a895c6..d2c02e6c6a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,6 +10,7 @@ PATH bcrypt_pbkdf bit-struct dnsruby + faker filesize jsobfu json @@ -122,6 +123,8 @@ GEM factory_girl_rails (4.9.0) factory_girl (~> 4.9.0) railties (>= 3.0.0) + faker (1.8.7) + i18n (>= 0.7) faraday (0.13.1) multipart-post (>= 1.2, < 3) ffi (1.9.18) @@ -287,7 +290,7 @@ GEM metasm rex-core rex-text - rex-socket (0.1.9) + rex-socket (0.1.10) rex-core rex-sslscan (0.1.5) rex-core diff --git a/lib/msf/core/cert_provider.rb b/lib/msf/core/cert_provider.rb new file mode 100644 index 0000000000..69775ad507 --- /dev/null +++ b/lib/msf/core/cert_provider.rb @@ -0,0 +1,100 @@ +require 'rex/socket/ssl' +require 'faker' + +module Msf +module Ssl + module CertProvider + + def self.rand_vars(opts = {}) + opts ||= {} + opts[:cc] ||= 'US' + opts[:st] ||= Faker::Address.state_abbr + opts[:loc] ||= Faker::Address.city + opts[:org] ||= Faker::Company.name + opts[:ou] ||= Faker::Hacker.send(%w{noun verb adjective}.sample.to_sym).gsub(/\W+/,'.') + opts[:cn] ||= opts[:org].downcase.gsub(/and/,'').gsub(/\W+/,'.') + '.' + Faker::Internet.domain_suffix + opts[:email] ||= "#{opts[:ou]}@#{opts[:cn]}" + opts + end + + def self.ssl_generate_subject(opts = {}) + opts = self.rand_vars(opts) + subject = "" + subject << "/C=#{opts[:cc]}" if opts[:cc] + subject << "/ST=#{opts[:st]}" if opts[:st] + subject << "/O=#{opts[:org]}" if opts[:org] + subject << "/OU=#{opts[:ou]}" if opts[:ou] + subject << "/CN=#{opts[:cn]}" if opts[:cn] + subject << "/emailAddress=#{opts[:email]}" if opts[:email] + subject + end + + # Not used, for API compatibility + def self.ssl_generate_issuer( + cc: 'US', + org: Faker::Company.name, + cn: Faker::Internet.domain_name + ) + "#{cc}/O=#{org}/CN=#{cn}" + end + + # + # Generate a realistic-looking but obstensibly fake SSL + # certificate. Use Faker gem to mimic other self-signed + # certificates on the web to reduce the chance of sig + # identification by NIDS and the like. + # + # @return [String, String, Array] + def self.ssl_generate_certificate(opts = {}, ksize = 2048) + yr = 24*3600*365 + vf = opts[:not_before] || Time.at(Time.now.to_i - rand(yr * 3) - yr) + vt = opts[:not_after] || Time.at(vf.to_i + (rand(9)+1) * yr) + cvars = opts[:cert_vars] || self.rand_vars + subject = opts[:subject] || ssl_generate_subject(cvars) + ctype = opts[:cert_type] || opts[:ca_cert].nil? ? :ca : :server + key = opts[:key] || OpenSSL::PKey::RSA.new(ksize){ } + cert = OpenSSL::X509::Certificate.new + + cert.version = opts[:version] || 2 + cert.serial = opts[:serial] || (rand(0xFFFFFFFF) << 32) + rand(0xFFFFFFFF) + cert.subject = OpenSSL::X509::Name.new([["C", subject]]) + cert.issuer = opts[:ca_cert] || cert.subject + cert.not_before = vf + cert.not_after = vt + cert.public_key = key.public_key + + bconst, kuse, ekuse = case ctype + when :ca + ['CA:TRUE', 'cRLSign,keyCertSign'] + when :server + ['CA:FALSE', 'digitalSignature,keyEncipherment', 'serverAuth'] + when :client + ['CA:FALSE', 'nonRepudiation,digitalSignature,keyEncipherment', 'clientAuth,emailProtection'] + when :ocsp + ['CA:FALSE', 'nonRepudiation,digitalSignature', 'serverAuth,OCSPSigning'] + when :tsca + ['CA:TRUE,pathlen:0', 'cRLSign,keyCertSign'] + end + + ef = OpenSSL::X509::ExtensionFactory.new + ef.subject_certificate = cert + ef.issuer_certificate = cert + cert.extensions = [ + ef.create_extension("basicConstraints", bconst, true), + ef.create_extension("subjectKeyIdentifier", "hash") + ] + if kuse and !kuse.empty? + cert.extensions << ef.create_extension("keyUsage", kuse) + end + + if ekuse and !ekuse.empty? + cert.extensions << ef.create_extension("extendedKeyUsage", ekuse) + end + + cert.sign(key, OpenSSL::Digest::SHA256.new) + + [key, cert, nil] + end + end +end +end diff --git a/lib/msf/core/framework.rb b/lib/msf/core/framework.rb index 9fbefb234e..c8e1c5480d 100644 --- a/lib/msf/core/framework.rb +++ b/lib/msf/core/framework.rb @@ -61,6 +61,7 @@ class Framework require 'msf/core/db_manager' require 'msf/core/event_dispatcher' require 'rex/json_hash_file' + require 'msf/core/cert_provider' # # Creates an instance of the framework context. @@ -84,6 +85,9 @@ class Framework # Configure the thread factory Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: self) + # Configure the SSL certificate generator + Rex::Socket::Ssl.cert_provider = Msf::Ssl::CertProvider + subscriber = FrameworkEventSubscriber.new(self) events.add_exploit_subscriber(subscriber) events.add_session_subscriber(subscriber) diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index c436d0a1e9..5dbbb00547 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -187,4 +187,6 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'nexpose' # Needed for NDMP sockets spec.add_runtime_dependency 'xdr' + # Needed for ::Msf...CertProvider + spec.add_runtime_dependency 'faker' end