Class: Msf::Sessions::WinrmCommandShell::WinRMStreamAdapter

Inherits:
Object
  • Object
show all
Includes:
Msf::Sessions::WinrmStreamAdapterCommon
Defined in:
lib/msf/base/sessions/winrm_command_shell.rb

Overview

Abstract WinRM to look like a stream so CommandShell can be happy

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Msf::Sessions::WinrmStreamAdapterCommon

#_get_once, #init_stream_buffer, #localinfo, #peerinfo

Constructor Details

#initialize(shell, interactive_command_id, on_shell_ended) ⇒ WinRMStreamAdapter

Returns a new instance of WinRMStreamAdapter.

Parameters:

  • shell (Net::MsfWinRM::StdinShell)

    Shell for talking to the WinRM service

  • on_shell_ended (Method)

    Callback for when the background thread notices the shell has ended



19
20
21
22
23
24
25
26
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 19

def initialize(shell, interactive_command_id, on_shell_ended)
  # To buffer input received while a session is backgrounded, we stick responses in a list
  init_stream_buffer
  @check_stdin_event = Rex::Sync::Event.new(false, true)
  self.interactive_command_id = interactive_command_id
  self.shell = shell
  self.on_shell_ended = on_shell_ended
end

Instance Attribute Details

#interactive_command_idObject

rubocop:enable Lint/SuppressedException



111
112
113
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 111

def interactive_command_id
  @interactive_command_id
end

#keep_alive_threadObject

rubocop:enable Lint/SuppressedException



111
112
113
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 111

def keep_alive_thread
  @keep_alive_thread
end

#on_shell_endedObject

rubocop:enable Lint/SuppressedException



111
112
113
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 111

def on_shell_ended
  @on_shell_ended
end

#shellObject

rubocop:enable Lint/SuppressedException



111
112
113
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 111

def shell
  @shell
end

Instance Method Details

#closeObject

Close the shell; cleanly terminating it on the server if possible

The shell may already be dead, or unreachable at this point, so do a best effort, and capture exceptions rubocop:disable Lint/SuppressedException



104
105
106
107
108
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 104

def close
  stop_keep_alive_loop
  shell.cleanup_command(interactive_command_id)
rescue WinRM::WinRMWSManFault
end

#get_once(length = -1,, timeout = 1) ⇒ Object

Read from the command shell, hurrying the keep-alive background thread along each time we loop back without a result.



40
41
42
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 40

def get_once(length = -1, timeout = 1)
  super(length, timeout) { refresh_stdout }
end

#refresh_stdoutObject

Trigger the background thread to go get more stdout



29
30
31
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 29

def refresh_stdout
  @check_stdin_event.set
end

#start_keep_alive_loop(framework) ⇒ Object

Start a background thread for regularly checking for stdout



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 45

def start_keep_alive_loop(framework)
  self.keep_alive_thread = framework.threads.spawn('WinRM-shell-keepalive', false, shell) do |_thr_shell|
    loop_delay = 0.5
    loop do
      tmp_buffer = []
      output_seen = false
      shell.read_stdout(interactive_command_id) do |stdout, stderr|
        if stdout || stderr
          output_seen = true
        end
        tmp_buffer << stdout if stdout
        tmp_buffer << stderr if stderr
      end
      @buffer_mutex.synchronize do
        @buffer.concat(tmp_buffer)
      end

      # If our last request received stdout, let's be ready for some more
      if output_seen
        @received_stdout_event.set
        loop_delay = 0.5
      else
        # Gradual backoff
        loop_delay *= 4
        loop_delay = [loop_delay, 30].min
      end

      # Wait loop_delay seconds, or until an interactive thread wakes us up
      begin
        @check_stdin_event.wait(loop_delay)
        # rubocop:disable Lint/SuppressedException
      rescue ::Timeout::Error
      end
      # rubocop:enable Lint/SuppressedException
      Thread.pass
    rescue WinRM::WinRMWSManFault => e
      print_error(e.fault_description)
      on_shell_ended.call
    rescue EOFError
      # Shell has been terminated
      on_shell_ended.call
    rescue Rex::HostUnreachable => e
      on_shell_ended.call(e.message)
    rescue StandardError => e
      on_shell_ended.call(e.message)
    end
  end
end

#stop_keep_alive_loopObject

Stop the background thread



95
96
97
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 95

def stop_keep_alive_loop
  keep_alive_thread.kill
end

#write(buf) ⇒ Object



33
34
35
36
# File 'lib/msf/base/sessions/winrm_command_shell.rb', line 33

def write(buf)
  shell.send_stdin(buf, interactive_command_id)
  refresh_stdout
end