Module: Msf::Session::Provider::SingleCommandShell

Overview

This interface is to be implemented by a session that is only capable of providing an interface to a single command shell.

Instance Method Summary collapse

Instance Method Details

#command_terminationObject



42
43
44
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 42

def command_termination
  "\n"
end

#set_is_echo_shell(timeout, command_separator) ⇒ Object

We don’t know initially whether the shell we have is one that echos input back to the output stream. If it is, we need to take this into account when using tokens to extract the data corresponding to the command we run. For instance, if the input is not echoed, our output will receive the data corresponding to the command run, followed by the token. On the other hand, if it does echo, we will see the token (echoed from our input) followed by the data corresponding to the command that was run, followed again by the token (this time from actually being run).

This function determines which situation we’re in, and sets a variable accordingly (is_echo_shell) which will persist for the duration of the session.



115
116
117
118
119
120
121
122
123
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 115

def set_is_echo_shell(timeout, command_separator)
  return @is_echo_shell unless @is_echo_shell.nil?
  token = ::Rex::Text.rand_text_alpha(32)
  numeric_token = rand(0xffffffff) + 1
  cmd = "echo #{numeric_token}"
  shell_write(cmd + "#{command_separator}echo #{token}#{command_termination}")
  res = shell_read_until_token(token, 0, timeout)
  @is_echo_shell = res ? res.include?(cmd) : false
end

#shell_closeObject

Closes the command shell.

Raises:

  • (NotImplementedError)


38
39
40
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 38

def shell_close()
  raise NotImplementedError
end

#shell_command_token(cmd, timeout = 10) ⇒ Object



87
88
89
90
91
92
93
94
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 87

def shell_command_token(cmd, timeout=10)
  if platform == 'windows'
    output = shell_command_token_win32(cmd, timeout)
  else
    output = shell_command_token_unix(cmd, timeout)
  end
  output
end

#shell_command_token_base(cmd, timeout = 10, command_separator = "\n") ⇒ Object

Explicitly run a single command and return the output. This version uses a marker to denote the end of data (instead of a timeout).

Parameters:

  • cmd (String)

    The command to run (will have an echo statement appended to signify the end)

  • timeout (Integer) (defaults to: 10)

    The timeout in seconds for the command

  • command_separator (String) (defaults to: "\n")

    A string to separate commands, for the given platform



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 140

def shell_command_token_base(cmd, timeout=10, command_separator="\n")
  # read any pending data
  buf = shell_read(-1, 0.01)
  set_is_echo_shell(timeout, command_separator)
  token = ::Rex::Text.rand_text_alpha(32)

  # Send the command to the session's stdin.
  delimiter = "echo #{token}"
  if cmd.strip.end_with?(command_separator)
    # This command already ends with a delimiter - don't need to add another one
    shell_data = cmd + "#{delimiter}#{command_termination}"
  else
    shell_data = cmd + "#{command_separator}#{delimiter}#{command_termination}"
  end
  unless @is_echo_shell
    shell_data = "#{delimiter}#{command_separator}#{shell_data}"
  end
  shell_write(shell_data)
  res = shell_read_until_token(token, 1, timeout)
  res
end

#shell_command_token_unix(cmd, timeout = 10) ⇒ Object



129
130
131
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 129

def shell_command_token_unix(cmd, timeout=10)
  shell_command_token_base(cmd, timeout, ';')
end

#shell_command_token_win32(cmd, timeout = 10) ⇒ Object



125
126
127
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 125

def shell_command_token_win32(cmd, timeout=10)
  shell_command_token_base(cmd, timeout, '&')
end

#shell_initObject

Initializes the command shell.

Raises:

  • (NotImplementedError)


17
18
19
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 17

def shell_init()
  raise NotImplementedError
end

#shell_read(length = nil) ⇒ Object

Reads data from the command shell.

Raises:

  • (NotImplementedError)


24
25
26
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 24

def shell_read(length = nil)
  raise NotImplementedError
end

#shell_read_until_token(token, wanted_idx = 0, timeout = 10) ⇒ Object

Read data until we find the token



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
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 49

def shell_read_until_token(token, wanted_idx=0, timeout=10)
  return if timeout.to_i == 0

  if wanted_idx == 0
    parts_needed = 2
  else
    parts_needed = 1 + (wanted_idx * 2)
  end

  # Read until we get the data between two tokens or absolute timeout.
  begin
    ::Timeout.timeout(timeout) do
      buf = ''
      idx = nil
      loop do
        if (tmp = shell_read(-1))
          buf << tmp
          # see if we have the wanted idx
          unless buf.nil?
            # normalize the line endings following the token and parse them
            buf.gsub!("#{token}\n", "#{token}\r\n")
            parts = buf.split("#{token}\r\n", -1)
            if parts.length >= parts_needed
              # cause another prompt to appear (just in case)
              shell_write(command_termination)
              return parts[wanted_idx]
            end
          end
        end
      end
    end
  rescue Timeout::Error
    # This is expected in many cases
  end
  # failed to get any data or find the token!
  nil
end

#shell_write(buf) ⇒ Object

Writes data to the command shell.

Raises:

  • (NotImplementedError)


31
32
33
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 31

def shell_write(buf)
  raise NotImplementedError
end

#to_cmd(cmd_and_args) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 96

def to_cmd(cmd_and_args)
  if platform == 'windows'
    result = Msf::Sessions::CommandShellWindows.to_cmd(cmd_and_args)
  else
    result = Msf::Sessions::CommandShellUnix.to_cmd(cmd_and_args)
  end
end