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.
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.
107 108 109 110 111 112 113 114 115 |
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 107 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).
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 132 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
121 122 123 |
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 121 def shell_command_token_unix(cmd, timeout=10) shell_command_token_base(cmd, timeout, ';') end |
#shell_command_token_win32(cmd, timeout = 10) ⇒ Object
117 118 119 |
# File 'lib/msf/core/session/provider/single_command_shell.rb', line 117 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 |