commit
1869977921
|
@ -67,17 +67,7 @@ external/source/exploits/**/Release
|
|||
|
||||
# Avoid checking in Meterpreter binaries. These are supplied upstream by
|
||||
# the meterpreter_bins gem.
|
||||
data/meterpreter/elevator.*.dll
|
||||
data/meterpreter/ext_server_espia.*.dll
|
||||
data/meterpreter/ext_server_extapi.*.dll
|
||||
data/meterpreter/ext_server_incognito.*.dll
|
||||
data/meterpreter/ext_server_kiwi.*.dll
|
||||
data/meterpreter/ext_server_lanattacks.*.dll
|
||||
data/meterpreter/ext_server_mimikatz.*.dll
|
||||
data/meterpreter/ext_server_priv.*.dll
|
||||
data/meterpreter/ext_server_stdapi.*.dll
|
||||
data/meterpreter/metsrv.*.dll
|
||||
data/meterpreter/screenshot.*.dll
|
||||
data/meterpreter/*.dll
|
||||
|
||||
# Avoid checking in Meterpreter libs that are built from
|
||||
# private source. If you're interested in this functionality,
|
||||
|
|
|
@ -9,7 +9,7 @@ PATH
|
|||
json
|
||||
metasploit-concern (~> 0.3.0)
|
||||
metasploit-model (~> 0.29.0)
|
||||
meterpreter_bins (= 0.0.16)
|
||||
meterpreter_bins (= 0.0.17)
|
||||
msgpack
|
||||
nokogiri
|
||||
packetfu (= 1.1.9)
|
||||
|
@ -132,7 +132,7 @@ GEM
|
|||
pg
|
||||
railties (< 4.0.0)
|
||||
recog (~> 1.0)
|
||||
meterpreter_bins (0.0.16)
|
||||
meterpreter_bins (0.0.17)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.6.1)
|
||||
|
|
|
@ -3,6 +3,8 @@ 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'
|
||||
require 'msf/core/payload/windows/verify_ssl'
|
||||
|
||||
module Msf
|
||||
module Handler
|
||||
|
@ -16,6 +18,7 @@ module ReverseHttp
|
|||
|
||||
include Msf::Handler
|
||||
include Msf::Handler::ReverseHttp::UriChecksum
|
||||
include Msf::Payload::Windows::VerifySsl
|
||||
|
||||
#
|
||||
# Returns the string representation of the handler type
|
||||
|
@ -291,12 +294,15 @@ protected
|
|||
|
||||
blob = obj.stage_payload
|
||||
|
||||
verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
|
||||
datastore['HandlerSSLCert'])
|
||||
#
|
||||
# Patch options into the payload
|
||||
#
|
||||
Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob,
|
||||
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
|
||||
:ssl => ssl?,
|
||||
:url => url,
|
||||
:ssl_cert_hash => verify_cert_hash,
|
||||
:expiration => datastore['SessionExpirationTimeout'],
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'],
|
||||
:ua => datastore['MeterpreterUserAgent'],
|
||||
|
@ -304,7 +310,7 @@ protected
|
|||
:proxy_port => datastore['PayloadProxyPort'],
|
||||
:proxy_type => datastore['PayloadProxyType'],
|
||||
:proxy_user => datastore['PayloadProxyUser'],
|
||||
:proxy_pass => datastore['PayloadProxyPass']
|
||||
:proxy_pass => datastore['PayloadProxyPass'])
|
||||
|
||||
resp.body = encode_stage(blob)
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ module ReverseHttps
|
|||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format"])
|
||||
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format"]),
|
||||
OptBool.new('StagerVerifySSLCert', [false, "Whether to verify the SSL certificate in Meterpreter"])
|
||||
], Msf::Handler::ReverseHttps)
|
||||
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/windows/reverse_winhttp'
|
||||
require 'rex/parser/x509_certificate'
|
||||
require 'msf/core/payload/windows/verify_ssl'
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -17,6 +17,7 @@ module Msf
|
|||
module Payload::Windows::ReverseWinHttps
|
||||
|
||||
include Msf::Payload::Windows::ReverseWinHttp
|
||||
include Msf::Payload::Windows::VerifySsl
|
||||
|
||||
#
|
||||
# Register reverse_winhttps specific options
|
||||
|
@ -49,27 +50,13 @@ 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(datastore['StagerVerifySSLCert'],
|
||||
datastore['HandlerSSLCert'])
|
||||
|
||||
# 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 +65,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 +75,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']
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#-*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/payloads/meterpreter/patch'
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -75,11 +76,8 @@ module Payload::Windows::StagelessMeterpreter
|
|||
|
||||
# the URL might not be given, as it might be patched in some other way
|
||||
if url
|
||||
url = "s#{url}\x00"
|
||||
location = dll.index("https://#{'X' * 256}")
|
||||
if location
|
||||
dll[location, url.length] = url
|
||||
end
|
||||
# Patch the URL using the patcher as this upports both ASCII and WCHAR.
|
||||
Rex::Payloads::Meterpreter::Patch.patch_string!(dll, "https://#{'X' * 512}", "s#{url}\x00")
|
||||
end
|
||||
|
||||
# if a block is given then call that with the meterpreter dll
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/parser/x509_certificate'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Implements SSL validation check options
|
||||
#
|
||||
###
|
||||
|
||||
module Payload::Windows::VerifySsl
|
||||
|
||||
#
|
||||
# Get the SSL hash from the certificate, if required.
|
||||
#
|
||||
def get_ssl_cert_hash(verify_cert, handler_cert)
|
||||
unless verify_cert.to_s =~ /^(t|y|1)/i
|
||||
return nil
|
||||
end
|
||||
|
||||
unless handler_cert
|
||||
raise ArgumentError, "Verifying SSL cert is enabled but no handler cert is configured"
|
||||
end
|
||||
|
||||
hash = Rex::Parser::X509Certificate.get_cert_file_hash(handler_cert)
|
||||
print_status("Meterpreter will verify SSL Certificate with SHA1 hash #{hash.unpack("H*").first}")
|
||||
hash
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -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
|
||||
|
|
|
@ -11,29 +11,18 @@ module Rex
|
|||
module Patch
|
||||
|
||||
# Replace the transport string
|
||||
def self.patch_transport! blob, ssl
|
||||
|
||||
i = blob.index("METERPRETER_TRANSPORT_SSL")
|
||||
if i
|
||||
str = ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00"
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
def self.patch_transport!(blob, ssl)
|
||||
str = ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00"
|
||||
patch_string!(blob, "METERPRETER_TRANSPORT_SSL", str)
|
||||
end
|
||||
|
||||
# Replace the URL
|
||||
def self.patch_url! blob, url
|
||||
|
||||
i = blob.index("https://" + ("X" * 256))
|
||||
if i
|
||||
str = url
|
||||
blob[i, str.length] = str
|
||||
end
|
||||
|
||||
def self.patch_url!(blob, url)
|
||||
patch_string!(blob, "https://#{'X' * 512}", url)
|
||||
end
|
||||
|
||||
# Replace the session expiration timeout
|
||||
def self.patch_expiration! blob, expiration
|
||||
def self.patch_expiration!(blob, expiration)
|
||||
|
||||
i = blob.index([0xb64be661].pack("V"))
|
||||
if i
|
||||
|
@ -44,7 +33,7 @@ module Rex
|
|||
end
|
||||
|
||||
# Replace the session communication timeout
|
||||
def self.patch_comm_timeout! blob, comm_timeout
|
||||
def self.patch_comm_timeout!(blob, comm_timeout)
|
||||
|
||||
i = blob.index([0xaf79257f].pack("V"))
|
||||
if i
|
||||
|
@ -55,68 +44,67 @@ module Rex
|
|||
end
|
||||
|
||||
# Replace the user agent string with our option
|
||||
def self.patch_ua! blob, ua
|
||||
|
||||
ua = ua[0,255] + "\x00"
|
||||
i = blob.index("METERPRETER_UA\x00")
|
||||
if i
|
||||
blob[i, ua.length] = ua
|
||||
end
|
||||
|
||||
def self.patch_ua!(blob, ua)
|
||||
patch_string!(blob, "METERPRETER_UA\x00", ua[0,255] + "\x00")
|
||||
end
|
||||
|
||||
# Activate a custom proxy
|
||||
def self.patch_proxy! blob, proxyhost, proxyport, proxy_type
|
||||
def self.patch_proxy!(blob, proxyhost, proxyport, proxy_type)
|
||||
|
||||
i = blob.index("METERPRETER_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
if i
|
||||
if proxyhost
|
||||
if proxyhost.to_s != ""
|
||||
proxyhost = proxyhost.to_s
|
||||
proxyport = proxyport.to_s || "8080"
|
||||
proxyinfo = proxyhost + ":" + proxyport
|
||||
if proxyport == "80"
|
||||
proxyinfo = proxyhost
|
||||
end
|
||||
if proxy_type.to_s == 'HTTP'
|
||||
proxyinfo = 'http://' + proxyinfo
|
||||
else #socks
|
||||
proxyinfo = 'socks=' + proxyinfo
|
||||
end
|
||||
proxyinfo << "\x00"
|
||||
blob[i, proxyinfo.length] = proxyinfo
|
||||
end
|
||||
if proxyhost && proxyhost.to_s != ""
|
||||
proxyhost = proxyhost.to_s
|
||||
proxyport = proxyport.to_s || "8080"
|
||||
proxyinfo = proxyhost + ":" + proxyport
|
||||
if proxyport == "80"
|
||||
proxyinfo = proxyhost
|
||||
end
|
||||
if proxy_type.to_s == 'HTTP'
|
||||
proxyinfo = 'http://' + proxyinfo
|
||||
else #socks
|
||||
proxyinfo = 'socks=' + proxyinfo
|
||||
end
|
||||
proxyinfo << "\x00"
|
||||
patch_string!(blob, "METERPRETER_PROXY#{"\x00" * 10}", proxyinfo)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Proxy authentification
|
||||
def self.patch_proxy_auth! blob, proxy_username, proxy_password, proxy_type
|
||||
def self.patch_proxy_auth!(blob, proxy_username, proxy_password, proxy_type)
|
||||
|
||||
unless (proxy_username.nil? or proxy_username.empty?) or
|
||||
(proxy_password.nil? or proxy_password.empty?) or
|
||||
proxy_type == 'SOCKS'
|
||||
|
||||
proxy_username_loc = blob.index("METERPRETER_USERNAME_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
proxy_username = proxy_username << "\x00"
|
||||
blob[proxy_username_loc, proxy_username.length] = proxy_username
|
||||
patch_string!(blob, "METERPRETER_USERNAME_PROXY#{"\x00" * 10}",
|
||||
proxy_username + "\x00")
|
||||
|
||||
proxy_password_loc = blob.index("METERPRETER_PASSWORD_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
proxy_password = proxy_password << "\x00"
|
||||
blob[proxy_password_loc, proxy_password.length] = proxy_password
|
||||
patch_string!(blob, "METERPRETER_PASSWORD_PROXY#{"\x00" * 10}",
|
||||
proxy_password + "\x00")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Patch options into metsrv for reverse HTTP payloads
|
||||
def self.patch_passive_service! blob, options
|
||||
# Patch the ssl cert hash
|
||||
def self.patch_ssl_check!(blob, ssl_cert_hash)
|
||||
# SSL cert location is an ASCII string, so no need for
|
||||
# WCHAR support
|
||||
if ssl_cert_hash
|
||||
i = blob.index("METERPRETER_SSL_CERT_HASH\x00")
|
||||
if i
|
||||
blob[i, ssl_cert_hash.length] = ssl_cert_hash
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
patch_transport! blob, options[:ssl]
|
||||
patch_url! blob, options[:url]
|
||||
patch_expiration! blob, options[:expiration]
|
||||
patch_comm_timeout! blob, options[:comm_timeout]
|
||||
patch_ua! blob, options[:ua]
|
||||
# Patch options into metsrv for reverse HTTP payloads
|
||||
def self.patch_passive_service!(blob, options)
|
||||
|
||||
patch_transport!(blob, options[:ssl])
|
||||
patch_url!(blob, options[:url])
|
||||
patch_expiration!(blob, options[:expiration])
|
||||
patch_comm_timeout!(blob, options[:comm_timeout])
|
||||
patch_ua!(blob, options[:ua])
|
||||
patch_ssl_check!(blob, options[:ssl_cert_hash])
|
||||
patch_proxy!(blob,
|
||||
options[:proxy_host],
|
||||
options[:proxy_port],
|
||||
|
@ -130,6 +118,30 @@ module Rex
|
|||
|
||||
end
|
||||
|
||||
#
|
||||
# Patch an ASCII value in the given payload. If not found, try WCHAR instead.
|
||||
#
|
||||
def self.patch_string!(blob, search, replacement)
|
||||
i = blob.index(search)
|
||||
if i
|
||||
blob[i, replacement.length] = replacement
|
||||
else
|
||||
i = blob.index(wchar(search))
|
||||
if i
|
||||
r = wchar(replacement)
|
||||
blob[i, r.length] = r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#
|
||||
# Convert the given ASCII string into a WCHAR string (dumb, but works)
|
||||
#
|
||||
def self.wchar(str)
|
||||
str.to_s.unpack("C*").pack("v*")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -178,7 +178,6 @@ module PacketDispatcher
|
|||
# Sends a packet and waits for a timeout for the given time interval.
|
||||
#
|
||||
def send_request(packet, t = self.response_timeout)
|
||||
|
||||
if not t
|
||||
send_packet(packet)
|
||||
return nil
|
||||
|
|
|
@ -64,7 +64,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model', '~> 0.29.0'
|
||||
# Needed for Meterpreter on Windows, soon others.
|
||||
spec.add_runtime_dependency 'meterpreter_bins', '0.0.16'
|
||||
spec.add_runtime_dependency 'meterpreter_bins', '0.0.17'
|
||||
# Needed by msfgui and other rpc components
|
||||
spec.add_runtime_dependency 'msgpack'
|
||||
# Needed by anemone crawler
|
||||
|
|
|
@ -8,6 +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'
|
||||
require 'rex/parser/x509_certificate'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
|
@ -15,6 +16,7 @@ module Metasploit3
|
|||
|
||||
include Msf::Payload::Windows::StagelessMeterpreter
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Payload::Windows::VerifySsl
|
||||
|
||||
def initialize(info = {})
|
||||
|
||||
|
@ -30,7 +32,7 @@ module Metasploit3
|
|||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('EXTENSIONS', [false, "Comma-separate list of extensions to load"]),
|
||||
OptString.new('EXTENSIONS', [false, "Comma-separated list of extensions to load"]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -54,9 +56,13 @@ module Metasploit3
|
|||
# end
|
||||
#end
|
||||
|
||||
Rex::Payloads::Meterpreter::Patch.patch_passive_service! dll,
|
||||
verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
|
||||
datastore['HandlerSSLCert'])
|
||||
|
||||
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(dll,
|
||||
:url => url,
|
||||
:ssl => true,
|
||||
:ssl_cert_hash => verify_cert_hash,
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ua => datastore['MeterpreterUserAgent'],
|
||||
|
@ -64,8 +70,9 @@ module Metasploit3
|
|||
:proxyport => datastore['PROXYPORT'],
|
||||
:proxy_type => datastore['PROXY_TYPE'],
|
||||
:proxy_username => datastore['PROXY_USERNAME'],
|
||||
:proxy_password => datastore['PROXY_PASSWORD']
|
||||
:proxy_password => datastore['PROXY_PASSWORD'])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue