Class: Msf::Sessions::SshCommandShellBind
- Inherits:
-
CommandShell
- Object
- CommandShell
- Msf::Sessions::SshCommandShellBind
- Includes:
- Msf::Session::Comm, Rex::Post::Channel::Container
- Defined in:
- lib/msf/base/sessions/ssh_command_shell_bind.rb
Overview
This class provides a session for SSH client connections, where Metasploit has authenticated to a remote SSH server. It is compatible with the Net::SSH library.
Defined Under Namespace
Modules: ChannelFailureReason Classes: TcpClientChannel, TcpServerChannel
Instance Attribute Summary collapse
-
#sock ⇒ Object
readonly
Returns the value of attribute sock.
-
#ssh_connection ⇒ Object
readonly
Returns the value of attribute ssh_connection.
Attributes included from Rex::Post::Channel::Container
Attributes inherited from CommandShell
#arch, #banner, #max_threads, #platform
Attributes included from Msf::Session::Interactive
Attributes included from Rex::Ui::Interactive
#completed, #interacting, #next_session, #on_command_proc, #on_print_proc, #orig_suspend, #orig_usr1, #orig_winch
Attributes included from Rex::Ui::Subscriber::Input
Attributes included from Rex::Ui::Subscriber::Output
Attributes included from Msf::Session
#alive, #db_record, #exploit, #exploit_datastore, #exploit_task, #exploit_uuid, #framework, #info, #machine_id, #payload_uuid, #routes, #sid, #sname, #target_host, #target_port, #username, #uuid, #via, #workspace
Attributes included from Framework::Offspring
Instance Method Summary collapse
- #bootstrap(datastore = {}, handler = nil) ⇒ Object
- #cleanup ⇒ Object
-
#create(params) ⇒ TcpClientChannel
Create a network socket using this session.
- #create_client_channel(params) ⇒ Object
- #create_server_channel(params) ⇒ Object
- #desc ⇒ Object
-
#initialize(ssh_connection, opts = {}) ⇒ SshCommandShellBind
constructor
Create a sessions instance from an SshConnection.
-
#on_got_remote_connection(_session, channel, packet) ⇒ Object
The SSH server has told us that there’s a port forwarding request.
- #stop_server_channel(host, port) ⇒ Object
Methods included from Rex::Post::Channel::Container
#add_channel, #find_channel, #initialize_channels, #remove_channel
Methods inherited from CommandShell
#_file_transfer, #_interact, #_interact_stream, #abort_foreground_supported, #binary_exists, binary_exists, can_cleanup_files, #cmd_background, #cmd_background_help, #cmd_download, #cmd_download_help, #cmd_help, #cmd_help_help, #cmd_irb, #cmd_irb_help, #cmd_pry, #cmd_pry_help, #cmd_resource, #cmd_resource_help, #cmd_sessions, #cmd_sessions_help, #cmd_shell, #cmd_shell_help, #cmd_source, #cmd_source_help, #cmd_upload, #cmd_upload_help, #commands, #docs_dir, #execute_file, #process_autoruns, #run_builtin_cmd, #run_single, #shell_close, #shell_command, #shell_init, #shell_read, #shell_write, #type, type
Methods included from Rex::Ui::Text::Resource
Methods included from Scriptable
#execute_file, #execute_script, included, #legacy_script_to_post_module
Methods included from Msf::Session::Provider::SingleCommandShell
#command_termination, #set_is_echo_shell, #shell_close, #shell_command_token, #shell_command_token_base, #shell_command_token_unix, #shell_command_token_win32, #shell_init, #shell_read, #shell_read_until_token, #shell_write
Methods included from Msf::Session::Basic
Methods included from Msf::Session::Interactive
#_interact, #_interact_complete, #_interrupt, #_suspend, #_usr1, #abort_foreground, #abort_foreground_supported, #comm_channel, #interactive?, #kill, #run_cmd, #tunnel_local, #tunnel_peer, #user_want_abort?
Methods included from Rex::Ui::Interactive
#_interact, #_interact_complete, #_interrupt, #_local_fd, #_remote_fd, #_stream_read_local_write_remote, #_stream_read_remote_write_local, #_suspend, #_winch, #detach, #handle_suspend, #handle_usr1, #handle_winch, #interact, #interact_stream, #prompt, #prompt_yesno, #restore_suspend, #restore_usr1, #restore_winch
Methods included from Rex::Ui::Subscriber
Methods included from Rex::Ui::Subscriber::Input
Methods included from Rex::Ui::Subscriber::Output
#flush, #print, #print_blank_line, #print_error, #print_good, #print_line, #print_status, #print_warning
Methods included from Msf::Session
#alive?, #comm_channel, #dead?, #inspect, #interactive?, #kill, #log_file_name, #log_source, #name, #name=, #register?, #session_host, #session_host=, #session_port, #session_port=, #session_type, #set_from_exploit, #set_via, #tunnel_local, #tunnel_peer, #tunnel_to_s, type, #type, #via_exploit, #via_payload
Constructor Details
#initialize(ssh_connection, opts = {}) ⇒ SshCommandShellBind
Create a sessions instance from an SshConnection. This will handle creating a new command stream.
225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 225 def initialize(ssh_connection, opts = {}) @ssh_connection = ssh_connection @sock = ssh_connection.transport.socket @server_channels = {} initialize_channels @channel_ticker = 0 # 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(nil, opts) end |
Instance Attribute Details
#sock ⇒ Object (readonly)
Returns the value of attribute sock.
423 424 425 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 423 def sock @sock end |
#ssh_connection ⇒ Object (readonly)
Returns the value of attribute ssh_connection.
423 424 425 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 423 def ssh_connection @ssh_connection end |
Instance Method Details
#bootstrap(datastore = {}, handler = nil) ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 238 def bootstrap(datastore = {}, handler = nil) # 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}" end |
#cleanup ⇒ Object
416 417 418 419 420 421 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 416 def cleanup channels.each_value(&:close) @server_channels.each_value(&:close) super end |
#create(params) ⇒ TcpClientChannel
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 can not be opened (SSH reverse port forwarding). The SSH specification does not define a UDP channel, so that is not supported either.
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 268 def create(params) # Notify handlers before we create the socket notify_before_socket_create(self, params) if params.proto == 'tcp' if params.server sock = create_server_channel(params) else sock = create_client_channel(params) end elsif params.proto == 'udp' raise ::Rex::ConnectionError.new(params.peerhost, params.peerport, reason: 'UDP sockets are not supported by SSH sessions.') end raise ::Rex::ConnectionError unless sock # Notify now that we've created the socket notify_socket_created(self, sock, params) sock end |
#create_client_channel(params) ⇒ Object
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 350 def create_client_channel(params) msf_channel = nil mutex = Mutex.new condition = ConditionVariable.new opened = false ssh_channel = @ssh_connection.open_channel('direct-tcpip', :string, params.peerhost, :long, params.peerport, :string, params.localhost, :long, params.localport) do |_| dlog("new direct-tcpip channel opened to #{Rex::Socket.is_ipv6?(params.peerhost) ? '[' + params.peerhost + ']' : params.peerhost}:#{params.peerport}") opened = true mutex.synchronize do condition.signal end end failure_reason_code = nil ssh_channel.on_open_failed do |_ch, code, desc| failure_reason_code = code wlog("failed to open SSH channel (code: #{code.inspect}, description: #{desc.inspect})") mutex.synchronize do condition.signal end end mutex.synchronize do timeout = params.timeout.to_i <= 0 ? nil : params.timeout condition.wait(mutex, timeout) end unless opened ssh_channel.close raise ::Rex::ConnectionTimeout.new(params.peerhost, params.peerport) if failure_reason_code.nil? case failure_reason_code when ChannelFailureReason::SSH_OPEN_ADMINISTRATIVELY_PROHIBITED reason = 'The SSH channel request was administratively prohibited.' when ChannelFailureReason::SSH_OPEN_UNKNOWN_CHANNEL_TYPE reason = 'The SSH channel type is not supported.' when ChannelFailureReason::SSH_OPEN_RESOURCE_SHORTAGE reason = 'The SSH channel request was denied because of a resource shortage.' end raise ::Rex::ConnectionError.new(params.peerhost, params.peerport, reason: reason) end msf_channel = TcpClientChannel.new(self, @channel_ticker += 1, ssh_channel, params) sock = msf_channel.lsock # Notify now that we've created the socket notify_socket_created(self, sock, params) sock end |
#create_server_channel(params) ⇒ Object
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 290 def create_server_channel(params) msf_channel = nil mutex = Mutex.new condition = ConditionVariable.new timed_out = false @ssh_connection.send_global_request('tcpip-forward', :string, params.localhost, :long, params.localport) do |success, response| mutex.synchronize { remote_port = params.localport remote_port = response.read_long if remote_port == 0 if success if timed_out # We're not using the port; clean it up elog("Remote forwarding on #{params.localhost}:#{params.localport} succeeded after timeout. Stopping channel to clean up dangling port") stop_server_channel(params.localhost, remote_port) else dlog("Remote forwarding from #{params.localhost} established on port #{remote_port}") key = [params.localhost, remote_port] msf_channel = TcpServerChannel.new(params, self, params.localhost, remote_port) @server_channels[key] = msf_channel end else elog("Remote forwarding failed on #{params.localhost}:#{params.localport}") end condition.signal } end mutex.synchronize { condition.wait(mutex, params.timeout) unless msf_channel timed_out = true end } # Return the server channel itself msf_channel end |
#desc ⇒ Object
252 253 254 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 252 def desc "SSH" end |
#on_got_remote_connection(_session, channel, packet) ⇒ Object
The SSH server has told us that there’s a port forwarding request. Find the relevant server channel and inform it.
403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 403 def on_got_remote_connection(_session, channel, packet) connected_address = packet.read_string connected_port = packet.read_long originator_address = packet.read_string originator_port = packet.read_long ilog("Received connection: #{connected_address}:#{connected_port} <--> #{originator_address}:#{originator_port}") # Find the correct TcpServerChannel # key = [connected_address, connected_port] server_channel = @server_channels[key] server_channel.create(@channel_ticker += 1, channel, originator_address, originator_port) end |
#stop_server_channel(host, port) ⇒ Object
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'lib/msf/base/sessions/ssh_command_shell_bind.rb', line 328 def stop_server_channel(host, port) completed_event = Rex::Sync::Event.new dlog("Cancelling tcpip-forward to #{host}:#{port}") @ssh_connection.send_global_request('cancel-tcpip-forward', :string, host, :long, port) do |success, _response| if success key = [host, port] @server_channels.delete(key) ilog("Reverse SSH listener on #{host}:#{port} stopped") else elog("Could not stop reverse listener on #{host}:#{port}") end completed_event.set end timeout = 5 # seconds begin completed_event.wait(timeout) true rescue ::Timeout::Error false end end |