From 87d1e925d0c1936f13d8909c352c9eaf678b6867 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Mon, 18 Oct 2021 16:46:40 -0400 Subject: [PATCH 1/4] Add an interactive SSH payload --- lib/msf/core/handler/generic.rb | 33 +++++++++++++++++++++++++ modules/exploits/multi/ssh/sshexec.rb | 20 +++++++++++++++ modules/payloads/singles/generic/ssh.rb | 32 ++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 lib/msf/core/handler/generic.rb create mode 100644 modules/payloads/singles/generic/ssh.rb diff --git a/lib/msf/core/handler/generic.rb b/lib/msf/core/handler/generic.rb new file mode 100644 index 0000000000..6991f8bf98 --- /dev/null +++ b/lib/msf/core/handler/generic.rb @@ -0,0 +1,33 @@ +# -*- coding: binary -*- +module Msf +module Handler + +module Generic + + include Msf::Handler + + # + # Returns the handler type of none since payloads that use this handler + # have no connection. + # + def self.handler_type + 'none' + end + + # + # Returns none to indicate no connection. + # + def self.general_handler_type + 'none' + end + + def handler(sock) + create_session(sock) + Claimed + end + +end + +end +end + diff --git a/modules/exploits/multi/ssh/sshexec.rb b/modules/exploits/multi/ssh/sshexec.rb index 0f2f8ca563..4cdb03552b 100644 --- a/modules/exploits/multi/ssh/sshexec.rb +++ b/modules/exploits/multi/ssh/sshexec.rb @@ -130,6 +130,20 @@ class MetasploitModule < Msf::Exploit::Remote 'Arch' => ARCH_CMD, 'Platform' => 'unix' } + ], + [ + 'Interactive SSH', + { + 'DefaultOptions' => { + 'PAYLOAD' => 'generic/ssh' + }, + 'Payload' => { + 'Compat' => { + 'PayloadType' => 'ssh_interact', + #'ConnectionType' => 'find' + } + } + } ] ], 'DefaultTarget' => 0, @@ -214,6 +228,12 @@ class MetasploitModule < Msf::Exploit::Remote def exploit do_login(datastore['RHOST'], datastore['USERNAME'], datastore['PASSWORD'], datastore['RPORT']) + + if target.name == 'Interactive SSH' + handler(ssh_socket) + return + end + print_status("#{datastore['RHOST']}:#{datastore['RPORT']} - Sending stager...") case target['Platform'] diff --git a/modules/payloads/singles/generic/ssh.rb b/modules/payloads/singles/generic/ssh.rb new file mode 100644 index 0000000000..fbe2ef69fe --- /dev/null +++ b/modules/payloads/singles/generic/ssh.rb @@ -0,0 +1,32 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +module MetasploitModule + CachedSize = 0 + + include Msf::Payload::Single + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super( + merge_info( + info, + 'Name' => 'Interact with Established SSH Connection', + 'Description' => 'Interacts with a shell on an established SSH connection', + 'Author' => 'Spencer McIntyre', + 'License' => MSF_LICENSE, + 'Platform' => '', + 'Arch' => ARCH_ALL, + 'Handler' => Msf::Handler::Generic, + 'Session' => Msf::Sessions::SshCommandShellBind, + 'PayloadType' => 'ssh_interact', + 'Payload' => { + 'Offsets' => {}, + 'Payload' => '' + } + ) + ) + end +end From 726c5f26e329834b83716553809907659f74e347 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 22 Oct 2021 17:24:26 -0400 Subject: [PATCH 2/4] SSH session consistency with auto-platform IDing --- lib/metasploit/framework/login_scanner/ssh.rb | 108 +--------------- lib/metasploit/framework/ssh/platform.rb | 121 ++++++++++++++++++ lib/msf/base/sessions/command_shell.rb | 7 +- .../base/sessions/command_shell_options.rb | 11 +- .../base/sessions/ssh_command_shell_bind.rb | 20 +++ lib/msf/core/auxiliary/command_shell.rb | 8 +- lib/msf/core/handler/generic.rb | 13 +- modules/auxiliary/scanner/ssh/ssh_login.rb | 3 +- .../auxiliary/scanner/ssh/ssh_login_pubkey.rb | 3 +- modules/exploits/multi/ssh/sshexec.rb | 4 +- modules/payloads/singles/generic/ssh.rb | 6 + 11 files changed, 185 insertions(+), 119 deletions(-) create mode 100644 lib/metasploit/framework/ssh/platform.rb diff --git a/lib/metasploit/framework/login_scanner/ssh.rb b/lib/metasploit/framework/login_scanner/ssh.rb index 39ba8fe304..d9d6208b2e 100644 --- a/lib/metasploit/framework/login_scanner/ssh.rb +++ b/lib/metasploit/framework/login_scanner/ssh.rb @@ -1,5 +1,6 @@ require 'net/ssh' require 'metasploit/framework/login_scanner/base' +require 'metasploit/framework/ssh/platform' require 'rex/socket/ssh_factory' module Metasploit @@ -110,80 +111,10 @@ module Metasploit private - # This method attempts to gather proof that we successfuly logged in. + # This method attempts to gather proof that we successfully logged in. # @return [String] The proof of a connection, May be empty. def gather_proof - proof = '' - begin - Timeout.timeout(10) do - proof = ssh_socket.exec!("id\n").to_s - if (proof =~ /id=/) - proof << ssh_socket.exec!("uname -a\n").to_s - if (proof =~ /JUNOS /) - # We're in the SSH shell for a Juniper JunOS, we can pull the version from the cli - # line 2 is hostname, 3 is model, 4 is the Base OS version - proof = ssh_socket.exec!("cli show version\n").split("\n")[2..4].join(", ").to_s - elsif (proof =~ /Linux USG /) - # Ubiquiti Unifi USG - proof << ssh_socket.exec!("cat /etc/version\n").to_s.rstrip - end - temp_proof = ssh_socket.exec!("grep unifi.version /tmp/system.cfg\n").to_s.rstrip - if (temp_proof =~ /unifi\.version/) - proof << temp_proof - # Ubiquiti Unifi device (non-USG), possibly a switch. Tested on US-24, UAP-nanoHD - # The /tmp/*.cfg files don't give us device info, however the info command does - # we dont call it originally since it doesnt say unifi/ubiquiti in it and info - # is a linux command as well - proof << ssh_socket.exec!("grep board.name /etc/board.info\n").to_s.rstrip - end - else - # Cisco IOS - if proof =~ /Unknown command or computer name/ - proof = ssh_socket.exec!("ver\n").to_s - # Juniper ScreenOS - elsif proof =~ /unknown keyword/ - proof = ssh_socket.exec!("get chassis\n").to_s - # Juniper JunOS CLI - elsif proof =~ /unknown command: id/ - proof = ssh_socket.exec!("show version\n").split("\n")[2..4].join(", ").to_s - # Brocade CLI - elsif proof =~ /Invalid input -> id/ || proof =~ /Protocol error, doesn't start with scp\!/ - proof = ssh_socket.exec!("show version\n").to_s - if proof =~ /Version:(?.+).+HW: (?)/mi - proof = "Model: #{hardware}, OS: #{os_version}" - end - # Arista - elsif proof =~ /% Invalid input at line 1/ - proof = ssh_socket.exec!("show version\n").split("\n")[0..1] - proof = proof.map {|item| item.strip} - proof = proof.join(", ").to_s - # Windows - elsif proof =~ /command not found|is not recognized as an internal or external command/ - proof = ssh_socket.exec!("systeminfo\n").to_s - /OS Name:\s+(?.+)$/ =~ proof - /OS Version:\s+(?.+)$/ =~ proof - if os_num.present? && os_name.present? - proof = "#{os_name.strip} #{os_num.strip}" - else - proof = ssh_socket.exec!("ver\n").to_s.strip - end - # mikrotik - elsif proof =~ /bad command name id \(line 1 column 1\)/ - proof = ssh_socket.exec!("/ system resource print\n").to_s - /platform:\s+(?.+)$/ =~ proof - /board-name:\s+(?.+)$/ =~ proof - /version:\s+(?.+)$/ =~ proof - if version && platform && board - proof = "#{platform.strip} #{board.strip} #{version.strip}" - end - else - proof << ssh_socket.exec!("help\n?\n\n\n").to_s - end - end - end - rescue ::Exception - end - proof + Metasploit::Framework::Ssh::Platform.get_platform_info(ssh_socket) end def set_sane_defaults @@ -195,40 +126,9 @@ module Metasploit public def get_platform(proof) - case proof - when /unifi\.version|UniFiSecurityGateway/ #Ubiquiti Unifi. uname -a is left in, so we got to pull before Linux - 'unifi' - when /Linux/ - 'linux' - when /Darwin/ - 'osx' - when /SunOS/ - 'solaris' - when /BSD/ - 'bsd' - when /HP-UX/ - 'hpux' - when /AIX/ - 'aix' - when /cygwin|Win32|Windows|Microsoft/ - 'windows' - when /Unknown command or computer name|Line has invalid autocommand/ - 'cisco-ios' - when /unknown keyword/ # ScreenOS - 'juniper' - when /JUNOS Base OS/ # JunOS - 'juniper' - when /MikroTik/ - 'mikrotik' - when /Arista/ - 'arista' - else - 'unknown' - end + Metasploit::Framework::Ssh::Platform.get_platform(proof) end - end - end end end diff --git a/lib/metasploit/framework/ssh/platform.rb b/lib/metasploit/framework/ssh/platform.rb new file mode 100644 index 0000000000..f91dd1e5eb --- /dev/null +++ b/lib/metasploit/framework/ssh/platform.rb @@ -0,0 +1,121 @@ +module Metasploit + module Framework + module Ssh + module Platform + def self.get_platform(ssh_socket) + info = get_platform_info(ssh_socket, timeout: 10) + get_platform_from_info(info) + end + + def self.get_platform_info(ssh_socket, timeout: 10) + info = '' + begin + Timeout.timeout(timeout) do + info = ssh_socket.exec!("id\n").to_s + if (info =~ /id=/) + info << ssh_socket.exec!("uname -a\n").to_s + if (info =~ /JUNOS /) + # We're in the SSH shell for a Juniper JunOS, we can pull the version from the cli + # line 2 is hostname, 3 is model, 4 is the Base OS version + info = ssh_socket.exec!("cli show version\n").split("\n")[2..4].join(", ").to_s + elsif (info =~ /Linux USG /) + # Ubiquiti Unifi USG + info << ssh_socket.exec!("cat /etc/version\n").to_s.rstrip + end + temp_proof = ssh_socket.exec!("grep unifi.version /tmp/system.cfg\n").to_s.rstrip + if (temp_proof =~ /unifi\.version/) + info << temp_proof + # Ubiquiti Unifi device (non-USG), possibly a switch. Tested on US-24, UAP-nanoHD + # The /tmp/*.cfg files don't give us device info, however the info command does + # we dont call it originally since it doesnt say unifi/ubiquiti in it and info + # is a linux command as well + info << ssh_socket.exec!("grep board.name /etc/board.info\n").to_s.rstrip + end + else + # Cisco IOS + if info =~ /Unknown command or computer name/ + info = ssh_socket.exec!("ver\n").to_s + # Juniper ScreenOS + elsif info =~ /unknown keyword/ + info = ssh_socket.exec!("get chassis\n").to_s + # Juniper JunOS CLI + elsif info =~ /unknown command: id/ + info = ssh_socket.exec!("show version\n").split("\n")[2..4].join(", ").to_s + # Brocade CLI + elsif info =~ /Invalid input -> id/ || info =~ /Protocol error, doesn't start with scp\!/ + info = ssh_socket.exec!("show version\n").to_s + if info =~ /Version:(?.+).+HW: (?)/mi + info = "Model: #{hardware}, OS: #{os_version}" + end + # Arista + elsif info =~ /% Invalid input at line 1/ + info = ssh_socket.exec!("show version\n").split("\n")[0..1] + info = info.map {|item| item.strip} + info = info.join(", ").to_s + # Windows + elsif info =~ /command not found|is not recognized as an internal or external command/ + info = ssh_socket.exec!("systeminfo\n").to_s + /OS Name:\s+(?.+)$/ =~ info + /OS Version:\s+(?.+)$/ =~ info + if os_num.present? && os_name.present? + info = "#{os_name.strip} #{os_num.strip}" + else + info = ssh_socket.exec!("ver\n").to_s.strip + end + # mikrotik + elsif info =~ /bad command name id \(line 1 column 1\)/ + info = ssh_socket.exec!("/ system resource print\n").to_s + /platform:\s+(?.+)$/ =~ info + /board-name:\s+(?.+)$/ =~ info + /version:\s+(?.+)$/ =~ info + if version && platform && board + info = "#{platform.strip} #{board.strip} #{version.strip}" + end + else + info << ssh_socket.exec!("help\n?\n\n\n").to_s + end + end + end + rescue Timeout::Error + end + + info + end + + def self.get_platform_from_info(info) + case info + when /unifi\.version|UniFiSecurityGateway/ #Ubiquiti Unifi. uname -a is left in, so we got to pull before Linux + 'unifi' + when /Linux/ + 'linux' + when /Darwin/ + 'osx' + when /SunOS/ + 'solaris' + when /BSD/ + 'bsd' + when /HP-UX/ + 'hpux' + when /AIX/ + 'aix' + when /cygwin|Win32|Windows|Microsoft/ + 'windows' + when /Unknown command or computer name|Line has invalid autocommand/ + 'cisco-ios' + when /unknown keyword/ # ScreenOS + 'juniper' + when /JUNOS Base OS/ # JunOS + 'juniper' + when /MikroTik/ + 'mikrotik' + when /Arista/ + 'arista' + else + 'unknown' + end + end + end + end + end +end + diff --git a/lib/msf/base/sessions/command_shell.rb b/lib/msf/base/sessions/command_shell.rb index 081f36fe08..cab7a409d9 100644 --- a/lib/msf/base/sessions/command_shell.rb +++ b/lib/msf/base/sessions/command_shell.rb @@ -107,13 +107,11 @@ class CommandShell banner.gsub!(/[^[:print:][:space:]]+/n, "_") banner.strip! - banner = %Q{ + session_info = @banner = %Q{ Shell Banner: #{banner} ----- } - - session_info = banner end end @@ -781,6 +779,7 @@ Shell Banner: attr_accessor :arch attr_accessor :platform attr_accessor :max_threads + attr_reader :banner protected @@ -804,7 +803,7 @@ protected # Displays +info+ on all session startups # +info+ is set to the shell banner and initial prompt in the +bootstrap+ method - user_output.print("#{self.info}\n") if (self.info && !self.info.empty?) && self.interacting + user_output.print("#{@banner}\n") if !@banner.blank? && self.interacting run_single('') diff --git a/lib/msf/base/sessions/command_shell_options.rb b/lib/msf/base/sessions/command_shell_options.rb index c12644b76e..440f938ce8 100644 --- a/lib/msf/base/sessions/command_shell_options.rb +++ b/lib/msf/base/sessions/command_shell_options.rb @@ -32,13 +32,20 @@ module CommandShellOptions # Configure input/output to match the payload session.user_input = self.user_input if self.user_input session.user_output = self.user_output if self.user_output + + platform = nil if self.platform and self.platform.kind_of? Msf::Module::PlatformList - session.platform = self.platform.platforms.first.realname.downcase + platform = self.platform.platforms.first.realname.downcase end if self.platform and self.platform.kind_of? Msf::Module::Platform - session.platform = self.platform.realname.downcase + platform = self.platform.realname.downcase end + + # a blank platform is *all* platforms and used by the generic modules, in that case only set this instance if it was + # not previously set to a more specific value through some means + session.platform = platform unless platform.blank? && !session.platform.blank? + if self.arch if self.arch.kind_of?(Array) session.arch = self.arch.join('') diff --git a/lib/msf/base/sessions/ssh_command_shell_bind.rb b/lib/msf/base/sessions/ssh_command_shell_bind.rb index 072a4fce6c..00f68e6cf4 100644 --- a/lib/msf/base/sessions/ssh_command_shell_bind.rb +++ b/lib/msf/base/sessions/ssh_command_shell_bind.rb @@ -1,5 +1,6 @@ # -*- coding: binary -*- +require 'metasploit/framework/ssh/platform' require 'rex/post/channel' require 'rex/post/meterpreter/channels/socket_abstraction' @@ -234,6 +235,21 @@ module Msf::Sessions super(rstream, opts) end + def bootstrap(datastore = {}, handler = nil) + @platform = Metasploit::Framework::Ssh::Platform.get_platform(self) + + # if the platform is known, it was recovered by communicating with the device, so skip verification, also not all + # shells accessed through SSH may respond to the echo command issued for verification as expected + datastore['AutoVerifySession'] &= @platform.blank? + super + + @info = "SSH #{username} @ #{@peer_info}" + end + + def desc + "SSH" + end + # # Create a network socket using this session. At this time, only TCP client # connections can be made (like SSH port forwarding) while TCP server sockets @@ -403,5 +419,9 @@ module Msf::Sessions attr_reader :sock, :ssh_connection + # Define #exec! as shell_command. This allows invocations of exec!(...).to_s to either use this session object or + # the ssh_connection. Once this objects #initialize method is called and the Net::SSH::CommandStream instance is + # created, the @ssh_connection's #exec! method can not be used. + alias exec! shell_command end end diff --git a/lib/msf/core/auxiliary/command_shell.rb b/lib/msf/core/auxiliary/command_shell.rb index e4cfe5bfad..7c3f2c8f71 100644 --- a/lib/msf/core/auxiliary/command_shell.rb +++ b/lib/msf/core/auxiliary/command_shell.rb @@ -39,7 +39,6 @@ module Auxiliary::CommandShell sock ||= obj.sock sess ||= Msf::Sessions::CommandShell.new(sock) sess.set_from_exploit(obj) - sess.info = info # Clean up the stored data sess.exploit_datastore.merge!(ds_merge) @@ -49,7 +48,14 @@ module Auxiliary::CommandShell obj.sock = nil if obj.respond_to? :sock framework.sessions.register(sess) + + if sess.respond_to?(:bootstrap) + sess.bootstrap(datastore) + + return unless sess.alive + end sess.process_autoruns(datastore) + sess.info = info unless info.blank? # Notify the framework that we have a new session opening up... # Don't let errant event handlers kill our session diff --git a/lib/msf/core/handler/generic.rb b/lib/msf/core/handler/generic.rb index 6991f8bf98..80d60cd8cd 100644 --- a/lib/msf/core/handler/generic.rb +++ b/lib/msf/core/handler/generic.rb @@ -21,11 +21,20 @@ module Generic 'none' end - def handler(sock) - create_session(sock) + # This is necessary for find-sock style payloads. + # + def handler(*args) + create_session(*args) + Claimed end + # + # Always wait at least 5 seconds for this payload (due to channel delays) + # + def wfs_delay + datastore['WfsDelay'] > 4 ? datastore['WfsDelay'] : 5 + end end end diff --git a/modules/auxiliary/scanner/ssh/ssh_login.rb b/modules/auxiliary/scanner/ssh/ssh_login.rb index 50715b20aa..22293c2715 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login.rb @@ -70,8 +70,7 @@ class MetasploitModule < Msf::Auxiliary 'USERNAME' => result.credential.public, 'PASSWORD' => result.credential.private } - info = "#{proto_from_fullname} #{result.credential} (#{ Rex::Socket.is_ipv6?(@ip) ? '[' + @ip + ']' : @ip }:#{rport})" - s = start_session(self, info, merge_me, false, sess.rstream, sess) + s = start_session(self, nil, merge_me, false, sess.rstream, sess) self.sockets.delete(scanner.ssh_socket.transport.socket) # Set the session platform diff --git a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb index efdc7a1e26..df860d58c7 100644 --- a/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb +++ b/modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb @@ -91,8 +91,7 @@ class MetasploitModule < Msf::Auxiliary 'KEY_PATH' => nil } - info = "#{proto_from_fullname} #{result.credential.public}:#{fingerprint} (#{ Rex::Socket.is_ipv6?(ip) ? '[' + ip + ']' : ip }:#{rport})" - s = start_session(self, info, merge_me, false, sess.rstream, sess) + s = start_session(self, nil, merge_me, false, sess.rstream, sess) self.sockets.delete(scanner.ssh_socket.transport.socket) # Set the session platform diff --git a/modules/exploits/multi/ssh/sshexec.rb b/modules/exploits/multi/ssh/sshexec.rb index 4cdb03552b..2e003bd403 100644 --- a/modules/exploits/multi/ssh/sshexec.rb +++ b/modules/exploits/multi/ssh/sshexec.rb @@ -135,12 +135,12 @@ class MetasploitModule < Msf::Exploit::Remote 'Interactive SSH', { 'DefaultOptions' => { - 'PAYLOAD' => 'generic/ssh' + 'PAYLOAD' => 'generic/ssh', + 'WfsDelay' => 5 }, 'Payload' => { 'Compat' => { 'PayloadType' => 'ssh_interact', - #'ConnectionType' => 'find' } } } diff --git a/modules/payloads/singles/generic/ssh.rb b/modules/payloads/singles/generic/ssh.rb index fbe2ef69fe..42155409e8 100644 --- a/modules/payloads/singles/generic/ssh.rb +++ b/modules/payloads/singles/generic/ssh.rb @@ -29,4 +29,10 @@ module MetasploitModule ) ) end + + def on_session(session) + super + + session.arch.clear # undo the ARCH_ALL amalgamation + end end From d5e024ae4c236d3d4d2708bad4182510d5f56057 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 5 Nov 2021 09:43:48 -0400 Subject: [PATCH 3/4] Refactor to generic/ssh/interact --- modules/exploits/multi/ssh/sshexec.rb | 2 +- modules/payloads/singles/generic/{ssh.rb => ssh/interact.rb} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename modules/payloads/singles/generic/{ssh.rb => ssh/interact.rb} (100%) diff --git a/modules/exploits/multi/ssh/sshexec.rb b/modules/exploits/multi/ssh/sshexec.rb index 2e003bd403..63123aab56 100644 --- a/modules/exploits/multi/ssh/sshexec.rb +++ b/modules/exploits/multi/ssh/sshexec.rb @@ -135,7 +135,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Interactive SSH', { 'DefaultOptions' => { - 'PAYLOAD' => 'generic/ssh', + 'PAYLOAD' => 'generic/ssh/interact', 'WfsDelay' => 5 }, 'Payload' => { diff --git a/modules/payloads/singles/generic/ssh.rb b/modules/payloads/singles/generic/ssh/interact.rb similarity index 100% rename from modules/payloads/singles/generic/ssh.rb rename to modules/payloads/singles/generic/ssh/interact.rb From 5fdcdcaaa59cb90c7fa0f71c0443f89ddbfe9fe4 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Wed, 10 Nov 2021 16:52:10 -0500 Subject: [PATCH 4/4] Delay rstream creation until the platform is known --- lib/metasploit/framework/login_scanner/ssh.rb | 2 +- lib/msf/base/sessions/ssh_command_shell_bind.rb | 14 +++++--------- lib/msf/core/auxiliary/command_shell.rb | 6 +++--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/metasploit/framework/login_scanner/ssh.rb b/lib/metasploit/framework/login_scanner/ssh.rb index d9d6208b2e..bdaa46c881 100644 --- a/lib/metasploit/framework/login_scanner/ssh.rb +++ b/lib/metasploit/framework/login_scanner/ssh.rb @@ -126,7 +126,7 @@ module Metasploit public def get_platform(proof) - Metasploit::Framework::Ssh::Platform.get_platform(proof) + Metasploit::Framework::Ssh::Platform.get_platform_from_info(proof) end end end diff --git a/lib/msf/base/sessions/ssh_command_shell_bind.rb b/lib/msf/base/sessions/ssh_command_shell_bind.rb index 00f68e6cf4..52c5f16203 100644 --- a/lib/msf/base/sessions/ssh_command_shell_bind.rb +++ b/lib/msf/base/sessions/ssh_command_shell_bind.rb @@ -228,19 +228,20 @@ module Msf::Sessions initialize_channels @channel_ticker = 0 - rstream = Net::SSH::CommandStream.new(ssh_connection).lsock - # Be alerted to reverse port forward connections (once we start listening on a port) ssh_connection.on_open_channel('forwarded-tcpip', &method(:on_got_remote_connection)) - super(rstream, opts) + super(nil, opts) end def bootstrap(datastore = {}, handler = nil) - @platform = Metasploit::Framework::Ssh::Platform.get_platform(self) + # this won't work after the rstream is initialized, so do it first + @platform = Metasploit::Framework::Ssh::Platform.get_platform(ssh_connection) # if the platform is known, it was recovered by communicating with the device, so skip verification, also not all # shells accessed through SSH may respond to the echo command issued for verification as expected datastore['AutoVerifySession'] &= @platform.blank? + + @rstream = Net::SSH::CommandStream.new(ssh_connection).lsock super @info = "SSH #{username} @ #{@peer_info}" @@ -418,10 +419,5 @@ module Msf::Sessions end attr_reader :sock, :ssh_connection - - # Define #exec! as shell_command. This allows invocations of exec!(...).to_s to either use this session object or - # the ssh_connection. Once this objects #initialize method is called and the Net::SSH::CommandStream instance is - # created, the @ssh_connection's #exec! method can not be used. - alias exec! shell_command end end diff --git a/lib/msf/core/auxiliary/command_shell.rb b/lib/msf/core/auxiliary/command_shell.rb index 7c3f2c8f71..c47cd449ea 100644 --- a/lib/msf/core/auxiliary/command_shell.rb +++ b/lib/msf/core/auxiliary/command_shell.rb @@ -36,7 +36,7 @@ module Auxiliary::CommandShell obj.sock.extend(CRLFLineEndings) end - sock ||= obj.sock + sock ||= obj.respond_to?(:sock) ? obj.sock : nil sess ||= Msf::Sessions::CommandShell.new(sock) sess.set_from_exploit(obj) @@ -44,8 +44,8 @@ module Auxiliary::CommandShell sess.exploit_datastore.merge!(ds_merge) # Prevent the socket from being closed - obj.sockets.delete(sock) - obj.sock = nil if obj.respond_to? :sock + obj.sockets.delete(sock) if sock + obj.sock = nil if obj.respond_to?(:sock) framework.sessions.register(sess)