Lots of changes to support moving timeouts to common spots

Session expiry, comms timeout, retry total/wait are all now part of all
of the meterpreter payloads as these are going to be used for
maintaining access with resiliency and will aim for consistency across
the payload types.
bug/bundler_fix
OJ 2015-04-09 17:57:43 +10:00
parent bc5fd4b813
commit 809409d8c4
18 changed files with 190 additions and 121 deletions

View File

@ -16,7 +16,12 @@ module MeterpreterOptions
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", Rex::Compat.is_windows]),
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"])
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"]),
OptBool.new('StagerCloseListenSocket', [false, "Close the listen socket in the stager", false]),
OptInt.new('SessionRetryTotal', [false, "Number of seconds try reconnecting for on network failure", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_TOTAL]),
OptInt.new('SessionRetryWait', [false, "Number of seconds to wait between reconnect attempts", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_WAIT]),
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcibly shut down', Rex::Post::Meterpreter::ClientCore::TIMEOUT_SESSION]),
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', Rex::Post::Meterpreter::ClientCore::TIMEOUT_COMMS])
], self.class)
end

View File

@ -250,7 +250,7 @@ module ReverseHopHttp
#
# 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,
:expiration => datastore['SessionExpirationTimeout'],
@ -260,7 +260,7 @@ module ReverseHopHttp
:proxy_port => datastore['PayloadProxyPort'],
:proxy_type => datastore['PayloadProxyType'],
:proxy_user => datastore['PayloadProxyUser'],
:proxy_pass => datastore['PayloadProxyPass']
:proxy_pass => datastore['PayloadProxyPass'])
blob = encode_stage(blob)

View File

@ -50,8 +50,6 @@ module ReverseHttp
register_advanced_options(
[
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcibly shut down', (24*3600*7)]),
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', 300]),
OptString.new('MeterpreterUserAgent', [ false, 'The user-agent that the payload should use for communication', 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)' ]),
OptString.new('MeterpreterServerName', [ false, 'The server header that the handler will send in response to requests', 'Apache' ]),
OptAddress.new('ReverseListenerBindAddress', [ false, 'The specific IP address to bind to on the local system']),
@ -263,6 +261,8 @@ protected
:url => url,
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i,
:ssl => ssl?,
:payload_uuid => uuid
})
@ -291,6 +291,8 @@ protected
:url => url,
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i,
:ssl => ssl?,
:payload_uuid => uuid
})
@ -312,8 +314,10 @@ protected
:ssl => ssl?,
:url => url,
:ssl_cert_hash => verify_cert_hash,
:expiration => datastore['SessionExpirationTimeout'],
:comm_timeout => datastore['SessionCommunicationTimeout'],
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i,
:ua => datastore['MeterpreterUserAgent'],
:proxy_host => datastore['PayloadProxyHost'],
:proxy_port => datastore['PayloadProxyPort'],
@ -330,6 +334,8 @@ protected
:url => url,
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i,
:ssl => ssl?,
:payload_uuid => uuid
})
@ -344,8 +350,13 @@ protected
:passive_dispatcher => obj.service,
:conn_id => conn_id,
:url => payload_uri(req) + conn_id + "/\x00",
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
# TODO ### Figure out what to do with these options given that the payload ###
# settings might not match the handler, should we instead read the remote? #
:expiration => datastore['SessionExpirationTimeout'].to_i, #
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i, #
:retry_total => datastore['SessionRetryTotal'].to_i, #
:retry_wait => datastore['SessionRetryWait'].to_i, #
##############################################################################
:ssl => ssl?,
:payload_uuid => uuid
})

View File

@ -53,6 +53,8 @@ module Handler::ReverseHttp::Stageless
:ssl_cert_hash => verify_cert_hash,
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i,
:ua => datastore['MeterpreterUserAgent'],
:proxy_host => datastore['PayloadProxyHost'],
:proxy_port => datastore['PayloadProxyPort'],

View File

@ -27,7 +27,7 @@ module Payload::Windows::BindTcp
#
# Generate the first stage
#
def generate_bind_tcp_from_datastore
def generate
# Generate the simple version of this stager if we don't have enough space
if self.available_space.nil? || required_space > self.available_space

View File

@ -29,16 +29,10 @@ module Payload::Windows::ReflectiveDllInject
],
'Platform' => 'win',
'Arch' => ARCH_X86,
'PayloadCompat' =>
{
'Convention' => 'sockedi -https',
},
'PayloadCompat' => { 'Convention' => 'sockedi -https', },
'Stage' =>
{
'Offsets' =>
{
'EXITFUNC' => [ 33, 'V' ]
},
'Offsets' => { 'EXITFUNC' => [ 33, 'V' ] },
'Payload' => ""
}
))
@ -85,6 +79,16 @@ module Payload::Windows::ReflectiveDllInject
# patch the bootstrap code into the dll's DOS header...
dll[ 0, bootstrap.length ] = bootstrap
# patch in the timeout options
timeout_opts = {
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i,
}
Rex::Payloads::Meterpreter::Patch.patch_timeouts!(dll, timeout_opts)
# patch the target ID into the URI if specified
if target_id
i = dll.index("/123456789 HTTP/1.0\r\n\r\n\x00")

View File

@ -85,6 +85,16 @@ module Payload::Windows::StagelessMeterpreter
end
end
# Patch in the timeout options
timeout_opts = {
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i
}
Rex::Payloads::Meterpreter::Patch.patch_timeouts!(dll, timeout_opts)
# if a block is given then call that with the meterpreter dll
# so that custom patching can happen if required
yield dll if block_given?

View File

@ -29,16 +29,9 @@ module Payload::Windows::ReflectiveDllInject_x64
],
'Platform' => 'win',
'Arch' => ARCH_X86_64,
'PayloadCompat' =>
{
'Convention' => 'sockrdi'
},
'Stage' =>
{
'Offsets' =>
{
'EXITFUNC' => [ 47, 'V' ]
},
'PayloadCompat' => { 'Convention' => 'sockrdi' },
'Stage' => {
'Offsets' => { 'EXITFUNC' => [ 47, 'V' ] },
'Payload' => ""
}
))
@ -81,6 +74,16 @@ module Payload::Windows::ReflectiveDllInject_x64
return
end
# patch in the timeout options
timeout_opts = {
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i,
}
Rex::Payloads::Meterpreter::Patch.patch_timeouts!(dll, timeout_opts)
# patch the bootstrap code into the dll's DOS header...
dll[ 0, bootstrap.length ] = bootstrap

View File

@ -85,6 +85,16 @@ module Payload::Windows::StagelessMeterpreter_x64
end
end
# Patch in the timeout options
timeout_opts = {
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:retry_total => datastore['SessionRetryTotal'].to_i,
:retry_wait => datastore['SessionRetryWait'].to_i
}
Rex::Payloads::Meterpreter::Patch.patch_timeouts!(dll, timeout_opts)
# if a block is given then call that with the meterpreter dll
# so that custom patching can happen if required
yield dll if block_given?

View File

@ -10,13 +10,17 @@ module Rex
###
module Patch
#
# Replace the transport string
#
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)
unless patch_string!(blob, "https://#{'X' * 512}", url)
# If the patching failed this could mean that we are somehow
@ -26,34 +30,28 @@ module Rex
end
end
# Replace the session expiration timeout
def self.patch_expiration!(blob, expiration)
i = blob.index([0xb64be661].pack("V"))
#
# Replace the timeout data with the actual timeout values.
#
def self.patch_timeouts!(blob, opts)
i = blob.index("METERP_TIMEOUTS\x00")
if i
str = [ expiration ].pack("V")
blob[i, str.length] = str
end
end
# Replace the session communication timeout
def self.patch_comm_timeout!(blob, comm_timeout)
i = blob.index([0xaf79257f].pack("V"))
if i
str = [ comm_timeout ].pack("V")
blob[i, str.length] = str
end
data = [opts[:expiration].to_i, opts[:comm_timeout].to_i,
opts[:retry_total].to_i, opts[:retry_wait].to_i].pack("VVVV")
blob[i, data.length] = data
end
end
#
# Replace the user agent string with our option
#
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)
if proxyhost && proxyhost.to_s != ""
@ -73,7 +71,9 @@ module Rex
end
end
#
# Proxy authentification
#
def self.patch_proxy_auth!(blob, proxy_username, proxy_password, proxy_type)
return if proxy_type.nil? || proxy_type.upcase == 'SOCKS'
@ -93,7 +93,9 @@ module Rex
end
end
#
# 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
@ -105,24 +107,25 @@ module Rex
end
end
#
# Patch options into metsrv for reverse HTTP payloads
def self.patch_passive_service!(blob, options)
#
def self.patch_passive_service!(blob, opts)
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_transport!(blob, opts[:ssl])
patch_url!(blob, opts[:url])
patch_timeouts!(blob, opts)
patch_ua!(blob, opts[:ua])
patch_ssl_check!(blob, opts[:ssl_cert_hash])
patch_proxy!(blob,
options[:proxy_host],
options[:proxy_port],
options[:proxy_type]
opts[:proxy_host],
opts[:proxy_port],
opts[:proxy_type]
)
patch_proxy_auth!(blob,
options[:proxy_user],
options[:proxy_pass],
options[:proxy_type]
opts[:proxy_user],
opts[:proxy_pass],
opts[:proxy_type]
)
end

View File

@ -111,6 +111,8 @@ class Client
self.ssl = opts[:ssl]
self.expiration = opts[:expiration]
self.comm_timeout = opts[:comm_timeout]
self.retry_total = opts[:retry_total]
self.retry_wait = opts[:retry_wait]
self.passive_dispatcher = opts[:passive_dispatcher]
self.response_timeout = opts[:timeout] || self.class.default_timeout
@ -451,6 +453,14 @@ class Client
#
attr_accessor :comm_timeout
#
# The total time for retrying connections
#
attr_accessor :retry_total
#
# The time to wait between retry attempts
#
attr_accessor :retry_wait
#
# The Passive Dispatcher
#
attr_accessor :passive_dispatcher

View File

@ -39,8 +39,10 @@ class ClientCore < Extension
METERPRETER_TRANSPORT_HTTP = 1
METERPRETER_TRANSPORT_HTTPS = 2
DEFAULT_SESSION_EXPIRATION = 24*3600*7
DEFAULT_COMMS_TIMEOUT = 300
TIMEOUT_SESSION = 24*3600*7 # 1 week
TIMEOUT_COMMS = 300 # 5 minutes
TIMEOUT_RETRY_TOTAL = 60*60 # 1 hour
TIMEOUT_RETRY_WAIT = 10 # 10 seconds
VALID_TRANSPORTS = {
'reverse_tcp' => METERPRETER_TRANSPORT_SSL,
@ -63,7 +65,7 @@ class ClientCore < Extension
# Core commands
#
##
#
#
# Get a list of loaded commands for the given extension.
#
@ -275,6 +277,22 @@ class ClientCore < Extension
scheme = opts[:transport].split('_')[1]
url = "#{scheme}://#{opts[:lhost]}:#{opts[:lport]}"
if opts[:comm_timeout]
request.add_tlv(TLV_TYPE_TRANS_COMM_TIMEOUT, opts[:comm_timeout])
end
if opts[:session_exp]
request.add_tlv(TLV_TYPE_TRANS_SESSION_EXP, opts[:session_exp])
end
if opts[:retry_total]
request.add_tlv(TLV_TYPE_TRANS_RETRY_TOTAL, opts[:retry_total])
end
if opts[:retry_wait]
request.add_tlv(TLV_TYPE_TRANS_RETRY_WAIT, opts[:retry_wait])
end
# do more magic work for http(s) payloads
unless opts[:transport].ends_with?('tcp')
sum = uri_checksum_lookup(:connect)
@ -288,12 +306,6 @@ class ClientCore < Extension
end
url << generate_uri_uuid(sum, uuid) + '/'
opts[:comms_timeout] ||= DEFAULT_COMMS_TIMEOUT
request.add_tlv(TLV_TYPE_TRANS_COMMS_TIMEOUT, opts[:comms_timeout])
opts[:session_exp] ||= DEFAULT_SESSION_EXPIRATION
request.add_tlv(TLV_TYPE_TRANS_SESSION_EXP, opts[:session_exp])
# TODO: randomise if not specified?
opts[:ua] ||= 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)'
request.add_tlv(TLV_TYPE_TRANS_UA, opts[:ua])
@ -557,22 +569,28 @@ class ClientCore < Extension
blob = migrate_stager.stage_payload
if client.passive_service
#
# Patch options into metsrv for reverse HTTP payloads
#
Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob,
# Patch options into metsrv for reverse HTTP payloads.
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
:ssl => client.ssl,
:url => self.client.url,
:expiration => self.client.expiration,
:comm_timeout => self.client.comm_timeout,
:retry_total => self.client.retry_total,
:retry_wait => self.client.retry_wait,
:ua => client.exploit_datastore['MeterpreterUserAgent'],
:proxy_host => client.exploit_datastore['PayloadProxyHost'],
:proxy_port => client.exploit_datastore['PayloadProxyPort'],
:proxy_type => client.exploit_datastore['PayloadProxyType'],
:proxy_user => client.exploit_datastore['PayloadProxyUser'],
:proxy_pass => client.exploit_datastore['PayloadProxyPass']
:proxy_pass => client.exploit_datastore['PayloadProxyPass'])
# This should be done by the reflective loader payloads
#else
# # Just patch the timeouts, which are consistent on each of the payloads.
# Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
# :expiration => self.client.expiration,
# :comm_timeout => self.client.comm_timeout,
# :retry_total => self.client.retry_total,
# :retry_wait => self.client.retry_wait)
end
blob

View File

@ -91,12 +91,14 @@ TLV_TYPE_MIGRATE_SOCKET_PATH = TLV_META_TYPE_STRING | 409
TLV_TYPE_TRANS_TYPE = TLV_META_TYPE_UINT | 430
TLV_TYPE_TRANS_URL = TLV_META_TYPE_STRING | 431
TLV_TYPE_TRANS_UA = TLV_META_TYPE_STRING | 432
TLV_TYPE_TRANS_COMMS_TIMEOUT = TLV_META_TYPE_UINT | 433
TLV_TYPE_TRANS_COMM_TIMEOUT = TLV_META_TYPE_UINT | 433
TLV_TYPE_TRANS_SESSION_EXP = TLV_META_TYPE_UINT | 434
TLV_TYPE_TRANS_CERT_HASH = TLV_META_TYPE_RAW | 435
TLV_TYPE_TRANS_PROXY_INFO = TLV_META_TYPE_STRING | 436
TLV_TYPE_TRANS_PROXY_USER = TLV_META_TYPE_STRING | 437
TLV_TYPE_TRANS_PROXY_PASS = TLV_META_TYPE_STRING | 438
TLV_TYPE_TRANS_RETRY_TOTAL = TLV_META_TYPE_UINT | 439
TLV_TYPE_TRANS_RETRY_WAIT = TLV_META_TYPE_UINT | 440
TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460
@ -194,13 +196,14 @@ class Tlv
when TLV_TYPE_MIGRATE_ARCH; "MIGRATE-ARCH"
when TLV_TYPE_TRANS_TYPE; "TRANS-TYPE"
when TLV_TYPE_TRANS_URL; "TRANS-URL"
when TLV_TYPE_TRANS_COMMS_TIMEOUT; "TRANS-COMMS-TIMEOUT"
when TLV_TYPE_TRANS_COMM_TIMEOUT; "TRANS-COMM-TIMEOUT"
when TLV_TYPE_TRANS_SESSION_EXP; "TRANS-SESSION-EXP"
when TLV_TYPE_TRANS_CERT_HASH; "TRANS-CERT-HASH"
when TLV_TYPE_TRANS_PROXY_INFO; "TRANS-PROXY-INFO"
when TLV_TYPE_TRANS_PROXY_USER; "TRANS-PROXY-USER"
when TLV_TYPE_TRANS_PROXY_PASS; "TRANS-PROXY-PASS"
when TLV_TYPE_TRANS_RETRY_TOTAL; "TRANS-RETRY-TOTAL"
when TLV_TYPE_TRANS_RETRY_WAIT; "TRANS-RETRY-WAIT"
when TLV_TYPE_MACHINE_ID; "MACHINE-ID"
#when Extensions::Stdapi::TLV_TYPE_NETWORK_INTERFACE; 'network-interface'

View File

@ -343,8 +343,10 @@ class Console::CommandDispatcher::Core
'-ps' => [ true, 'Proxy password for http(s) transports (optional)' ],
'-pt' => [ true, 'Proxy type for http(s) transports (optional: http, socks; default: http)' ],
'-c' => [ true, 'SSL certificate path for https transport verification (optional)' ],
'-to' => [ true, "Comms timeout (seconds) for http(s) transports (default: #{Rex::Post::Meterpreter::ClientCore::DEFAULT_COMMS_TIMEOUT})" ],
'-ex' => [ true, "Expiration timout (seconds) for http(s) transports (default: #{Rex::Post::Meterpreter::ClientCore::DEFAULT_SESSION_EXPIRATION})" ],
'-to' => [ true, 'Comms timeout (seconds) (default: same as current session)' ],
'-ex' => [ true, 'Expiration timout (seconds) (default: same as current session)' ],
'-rt' => [ true, 'Retry total time (seconds) (default: same as current session)' ],
'-rw' => [ true, 'Retry wait time (seconds) (default: same as current session)' ],
'-h' => [ false, 'Help menu' ])
def cmd_transport_help
@ -370,8 +372,10 @@ class Console::CommandDispatcher::Core
:proxy_type => nil,
:proxy_user => nil,
:proxy_pass => nil,
:comms_timeout => nil,
:comm_timeout => nil,
:session_exp => nil,
:retry_total => nil,
:retry_wait => nil,
:cert => nil
}
@ -392,9 +396,13 @@ class Console::CommandDispatcher::Core
when '-ua'
opts[:ua] = val
when '-to'
opts[:comms_timeout] = val.to_i if val
opts[:comm_timeout] = val.to_i if val
when '-ex'
opts[:session_exp] = val.to_i if val
when '-rt'
opts[:retry_total] = val.to_i if val
when '-rw'
opts[:retry_wait] = val.to_i if val
when '-p'
opts[:lport] = val.to_i if val
when '-l'

View File

@ -29,8 +29,4 @@ module Metasploit4
))
end
def generate
generate_bind_tcp_from_datastore
end
end

View File

@ -25,10 +25,7 @@ module Metasploit3
'Arch' => ARCH_X86,
'Handler' => Msf::Handler::ReverseTcp,
'Convention' => 'sockedi',
'Stager' =>
{
'RequiresMidstager' => false
}
'Stager' => { 'RequiresMidstager' => false }
))
end

View File

@ -32,12 +32,6 @@ module Metasploit3
# Don't let people set the library name option
options.remove_option('LibraryName')
options.remove_option('DLL')
# TODO: figure out of this is the best way to do it. We need the bind stager to not
# close the listen socket so that we can figure out transports from within metsrv.
register_advanced_options([
OptBool.new('StagerCloseListenSocket', [false, "Close the listen socket in the stager", false]),
], self.class)
end
def library_path

View File

@ -32,11 +32,6 @@ module Metasploit3
# Don't let people set the library name option
options.remove_option('LibraryName')
options.remove_option('DLL')
# TODO: figure out of this is the best way to do it.
register_advanced_options([
OptBool.new('StagerCloseListenSocket', [false, "Close the listen socket in the stager", false]),
], self.class)
end
def library_path