From fd4ad9bd2eb8a72d7d5893a70141afc0dfd37d26 Mon Sep 17 00:00:00 2001 From: "oj@buffered.io" Date: Wed, 18 Mar 2015 11:10:31 +1000 Subject: [PATCH] Rework changes on top of HD's PR This commit removes duplication, tidies up a couple of things and puts some common code into the x509 module. --- lib/msf/core/handler/reverse_http.rb | 45 ++----------------- .../core/payload/windows/reverse_winhttp.rb | 5 +-- .../core/payload/windows/reverse_winhttps.rb | 38 ++++++++-------- lib/rex/parser/x509_certificate.rb | 30 +++++++++++++ .../windows/meterpreter_reverse_https.rb | 42 +---------------- 5 files changed, 57 insertions(+), 103 deletions(-) diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index de4f6b770d..b55725d87a 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -3,6 +3,7 @@ require 'rex/io/stream_abstraction' require 'rex/sync/ref' require 'msf/core/handler/reverse_http/uri_checksum' require 'rex/payloads/meterpreter/patch' +require 'rex/parser/x509_certificate' module Msf module Handler @@ -356,56 +357,18 @@ protected port > 0 ? port : datastore['LPORT'].to_i end - # TODO: remove all that is below this when HD's PR has been landed def get_ssl_cert_hash - unless datastore['StagerVerifySslCert'].to_s =~ /^(t|y|1)/i + unless datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i return nil end unless datastore['HandlerSSLCert'] - raise ArgumentError, "StagerVerifySslCert is enabled but no HandlerSSLCert is configured" + raise ArgumentError, "StagerVerifySSLCert is enabled but no HandlerSSLCert is configured" end - # TODO: fix this up when HD's PR has landed. - #hcert = Rex::Parser::X509Certificate.parse_pem_file(datastore['HandlerSSLCert']) - hcert = parse_pem_file(datastore['HandlerSSLCert']) - unless hcert and hcert[0] and hcert[1] - raise ArgumentError, "Could not parse a private key and certificate from #{datastore['HandlerSSLCert']}" - end - - hash = Rex::Text.sha1_raw(hcert[1].to_der) + hash = Rex::Parser::X509Certificate.get_cert_file_hash(datastore['HandlerSSLCert']) print_status("Meterpreter will verify SSL Certificate with SHA1 hash #{hash.unpack("H*").first}") hash - nil - end - - def parse_pem(ssl_cert) - cert = nil - key = nil - chain = nil - - certs = [] - ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem| - if pem =~ /PRIVATE KEY/ - key = OpenSSL::PKey::RSA.new(pem) - elsif pem =~ /CERTIFICATE/ - certs << OpenSSL::X509::Certificate.new(pem) - end - end - - cert = certs.shift - if certs.length > 0 - chain = certs - end - - [key, cert, chain] - end - def parse_pem_file(ssl_cert_file) - data = '' - ::File.open(ssl_cert_file, 'rb') do |fd| - data << fd.read(fd.stat.size) - end - parse_pem(data) end end diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index 8a5be39790..2745ece351 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -108,8 +108,7 @@ module Payload::Windows::ReverseWinHttp # @option opts [String] :url The URI to request during staging # @option opts [String] :host The host to connect to # @option opts [Fixnum] :port The port to connect to - # @option opts [Bool] :verify_ssl Whether or not to do SSL certificate validation - # @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify + # @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh # @option opts [Fixnum] :retry_count The number of times to retry a failed request before giving up # @@ -121,7 +120,7 @@ module Payload::Windows::ReverseWinHttp encoded_url = asm_generate_wchar_array(opts[:url]) encoded_host = asm_generate_wchar_array(opts[:host]) - if opts[:ssl] && opts[:verify_cert] && opts[:verify_cert_hash] + if opts[:ssl] && opts[:verify_cert_hash] verify_ssl = true encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",") end diff --git a/lib/msf/core/payload/windows/reverse_winhttps.rb b/lib/msf/core/payload/windows/reverse_winhttps.rb index 993347db35..e53aa6ae2d 100644 --- a/lib/msf/core/payload/windows/reverse_winhttps.rb +++ b/lib/msf/core/payload/windows/reverse_winhttps.rb @@ -49,27 +49,12 @@ module Payload::Windows::ReverseWinHttps # def generate - verify_cert = false - verify_cert_hash = nil - - if datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i - unless datastore['HandlerSSLCert'] - raise ArgumentError, "StagerVerifySSLCert is enabled but no HandlerSSLCert is configured" - else - verify_cert = true - hcert = Rex::Parser::X509Certificate.parse_pem_file(datastore['HandlerSSLCert']) - unless hcert and hcert[0] and hcert[1] - raise ArgumentError, "Could not parse a private key and certificate from #{datastore['HandlerSSLCert']}" - end - verify_cert_hash = Rex::Text.sha1_raw(hcert[1].to_der) - print_status("Stager will verify SSL Certificate with SHA1 hash #{verify_cert_hash.unpack("H*").first}") - end - end + verify_cert_hash = get_ssl_cert_hash # Generate the simple version of this stager if we don't have enough space if self.available_space.nil? || required_space > self.available_space - if datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i + if verify_cert_hash raise ArgumentError, "StagerVerifySSLCert is enabled but not enough payload space is available" end @@ -78,7 +63,6 @@ module Payload::Windows::ReverseWinHttps host: datastore['LHOST'], port: datastore['LPORT'], url: generate_small_uri, - verify_cert: verify_cert, verify_cert_hash: verify_cert_hash, retry_count: datastore['StagerRetryCount']) end @@ -89,7 +73,6 @@ module Payload::Windows::ReverseWinHttps port: datastore['LPORT'], url: generate_uri, exitfunk: datastore['EXITFUNC'], - verify_cert: verify_cert, verify_cert_hash: verify_cert_hash, retry_count: datastore['StagerRetryCount'] } @@ -114,6 +97,23 @@ module Payload::Windows::ReverseWinHttps space end + # + # Get the SSL hash from the certificate, if required. + # + def get_ssl_cert_hash + unless datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i + return nil + end + + unless datastore['HandlerSSLCert'] + raise ArgumentError, "StagerVerifySSLCert is enabled but no HandlerSSLCert is configured" + end + + hash = Rex::Parser::X509Certificate.get_cert_file_hash(datastore['HandlerSSLCert']) + print_status("Meterpreter will verify SSL Certificate with SHA1 hash #{hash.unpack("H*").first}") + hash + end + end end diff --git a/lib/rex/parser/x509_certificate.rb b/lib/rex/parser/x509_certificate.rb index f46500bf5c..a1fad8a968 100644 --- a/lib/rex/parser/x509_certificate.rb +++ b/lib/rex/parser/x509_certificate.rb @@ -56,6 +56,36 @@ class X509Certificate parse_pem(data) end + # + # Parse a certificate in unified PEM format and retrieve + # the SHA1 hash. + # + # @param [String] ssl_cert + # @return [String] + def self.get_cert_hash(ssl_cert) + hcert = parse_pem(ssl_cert) + + unless hcert and hcert[0] and hcert[1] + raise ArgumentError, "Could not parse a private key and certificate" + end + + Rex::Text.sha1_raw(hcert[1].to_der) + end + + # + # Parse a file that contains a certificate in unified PEM + # format and retrieve the SHA1 hash. + # + # @param [String] ssl_cert_file + # @return [String] + def self.get_cert_file_hash(ssl_cert_file) + data = '' + ::File.open(ssl_cert_file, 'rb') do |fd| + data << fd.read(fd.stat.size) + end + get_cert_hash(data) + end + end end diff --git a/modules/payloads/singles/windows/meterpreter_reverse_https.rb b/modules/payloads/singles/windows/meterpreter_reverse_https.rb index bb6e59f80c..e82262b61b 100644 --- a/modules/payloads/singles/windows/meterpreter_reverse_https.rb +++ b/modules/payloads/singles/windows/meterpreter_reverse_https.rb @@ -8,8 +8,7 @@ require 'msf/core/handler/reverse_https' require 'msf/core/payload/windows/stageless_meterpreter' require 'msf/base/sessions/meterpreter_x86_win' require 'msf/base/sessions/meterpreter_options' -# TODO: put this in when HD's PR has been landed. -#require 'rex/parser/x509_certificate' +require 'rex/parser/x509_certificate' module Metasploit3 @@ -72,7 +71,6 @@ module Metasploit3 end - # TODO: remove all that is below this when HD's PR has been landed def get_ssl_cert_hash unless datastore['StagerVerifySSLCert'].to_s =~ /^(t|y|1)/i return nil @@ -82,46 +80,10 @@ module Metasploit3 raise ArgumentError, "StagerVerifySSLCert is enabled but no HandlerSSLCert is configured" end - # TODO: fix this up when HD's PR has landed. - #hcert = Rex::Parser::X509Certificate.parse_pem_file(datastore['HandlerSSLCert']) - hcert = parse_pem_file(datastore['HandlerSSLCert']) - unless hcert and hcert[0] and hcert[1] - raise ArgumentError, "Could not parse a private key and certificate from #{datastore['HandlerSSLCert']}" - end - - hash = Rex::Text.sha1_raw(hcert[1].to_der) + hash = Rex::Parser::X509Certificate.get_cert_file_hash(datastore['HandlerSSLCert']) print_status("Meterpreter will verify SSL Certificate with SHA1 hash #{hash.unpack("H*").first}") hash end - def parse_pem(ssl_cert) - cert = nil - key = nil - chain = nil - - certs = [] - ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem| - if pem =~ /PRIVATE KEY/ - key = OpenSSL::PKey::RSA.new(pem) - elsif pem =~ /CERTIFICATE/ - certs << OpenSSL::X509::Certificate.new(pem) - end - end - - cert = certs.shift - if certs.length > 0 - chain = certs - end - - [key, cert, chain] - end - def parse_pem_file(ssl_cert_file) - data = '' - ::File.open(ssl_cert_file, 'rb') do |fd| - data << fd.read(fd.stat.size) - end - parse_pem(data) - end - end