Module: Msf::Session::Provider::SingleCommandShell
- Included in:
- MultiCommandShell, Msf::Sessions::AwsInstanceConnectCommandShellBind, Msf::Sessions::AwsSsmCommandShellBind, Msf::Sessions::CommandShell, Msf::Sessions::EncryptedShell, Msf::Sessions::MainframeShell, Msf::Sessions::Meterpreter, Msf::Sessions::SshCommandShellReverse, Msf::Sessions::TTY
- Defined in:
- lib/msf/core/session/provider/single_command_shell.rb
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
- #command_termination ⇒ Object
-
#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.
-
#shell_close ⇒ Object
Closes the command shell.
- #shell_command_token(cmd, timeout = 10) ⇒ Object
-
#shell_command_token_base(cmd, timeout = 10, command_separator = "\n") ⇒ Object
Explicitly run a single command and return the output.
- #shell_command_token_unix(cmd, timeout = 10) ⇒ Object
- #shell_command_token_win32(cmd, timeout = 10) ⇒ Object
-
#shell_init ⇒ Object
Initializes the command shell.
-
#shell_read(length = nil) ⇒ Object
Reads data from the command shell.
-
#shell_read_until_token(token, wanted_idx = 0, timeout = 10) ⇒ Object
Read data until we find the token.
-
#shell_write(buf) ⇒ Object
Writes data to the command shell.
- #to_cmd(cmd, args) ⇒ Object
Instance Method Details
#command_termination ⇒ Object
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.include?(cmd) end |
#shell_close ⇒ Object
Closes the command shell.
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).
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_init ⇒ Object
Initializes the command shell.
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.
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.
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, 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, args) if platform == 'windows' result = Msf::Sessions::CommandShellWindows.to_cmd(cmd, args) else result = Msf::Sessions::CommandShellUnix.to_cmd(cmd, args) end end |