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

[View source]

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.

[View source]

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)
[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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)
[View source]

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)
[View source]

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

[View source]

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)
[View source]

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

[View source]

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