Refactor cmd_shell and add cmd_shell_{help,tabs}

GSoC/Meterpreter_Web_Console
William Vu 2018-09-13 15:08:31 -05:00
parent cfbd259e80
commit 4a759fd048
1 changed files with 112 additions and 76 deletions

View File

@ -22,7 +22,7 @@ class Console::CommandDispatcher::Stdapi::Sys
#
@@execute_opts = Rex::Parser::Arguments.new(
"-a" => [ true, "The arguments to pass to the command." ],
"-c" => [ false, "Channelized I/O (required for interaction)." ],
"-c" => [ false, "Channelized I/O (required for interaction)." ], # -i sets -c
"-f" => [ true, "The executable command to run." ],
"-h" => [ false, "Help menu." ],
"-H" => [ false, "Create the process hidden from view." ],
@ -33,6 +33,14 @@ class Console::CommandDispatcher::Stdapi::Sys
"-k" => [ false, "Execute process on the meterpreters current desktop" ],
"-s" => [ true, "Execute process in a given session as the session user" ])
#
# Options used by the 'shell' command.
#
@@shell_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help menu." ],
"-l" => [ false, "List available shells (/etc/shells)." ],
"-t" => [ true, "Spawn a PTY shell (/bin/bash if no argument given)." ]) # ssh(1) -t
#
# Options used by the 'reboot' command.
#
@ -40,15 +48,6 @@ class Console::CommandDispatcher::Stdapi::Sys
"-h" => [ false, "Help menu." ],
"-f" => [ true, "Force a reboot, valid values [1|2]" ])
#
# Options used by the 'shell' command.
#
@@shell_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help menu." ],
"-b" => [ false, "Spawn bash PTY shell. (/bin/bash)" ],
"-l" => [ false, "List available shells. (Linux, OSX, BSD)" ],
"-s" => [ true, "Spawn alternative PTY shell. (E.g. /bin/zsh)" ])
#
# Options used by the 'shutdown' command.
#
@ -258,111 +257,148 @@ class Console::CommandDispatcher::Stdapi::Sys
[]
end
def cmd_shell_help
print_line 'Usage: shell [options]'
print_line
print_line 'Opens an interactive native shell.'
print_line @@shell_opts.usage
end
def cmd_shell_tabs(str, words)
return @@shell_opts.fmt.keys if words.length == 1
[]
end
#
# Drop into a system shell as specified by %COMSPEC% or
# as appropriate for the host.
#
def cmd_shell(*args)
use_alt = false
alt_path = ""
use_pty = false
sh_path = '/bin/bash'
@@shell_opts.parse(args) { |opt, idx, val|
@@shell_opts.parse(args) do |opt, idx, val|
case opt
when "-h"
print(
"Usage: shell [options]\n\n" +
"Opens an interactive native shell.\n" +
@@shell_opts.usage)
return true
when "-b"
use_alt = true
alt_path = "/bin/bash"
when "-s"
use_alt = true
alt_path = val
if (alt_path == "")
print_error("You must specify a shell path with -s")
return true
when '-h'
cmd_shell_help
return true
when '-l'
return false unless client.fs.file.exist?('/etc/shells')
begin
client.fs.file.open('/etc/shells') do |f|
print(f.read) until f.eof
end
when "-l"
if client.fs.file.exist?('/etc/shells')
fd = client.fs.file.new("/etc/shells", "rb")
begin
until fd.eof?
print(fd.read)
end
rescue EOFError
end
fd.close
end
return true
rescue
return false
end
return true
when '-t'
use_pty = true
# XXX: No other options must follow
sh_path = val if val
end
}
end
case client.platform
when 'windows'
path = client.fs.file.expand_path("%COMSPEC%")
path = (path and not path.empty?) ? path : "cmd.exe"
path = client.fs.file.expand_path('%COMSPEC%')
path = (path && !path.empty?) ? path : 'cmd.exe'
# attempt the shell with thread impersonation
begin
cmd_execute("-f", path, "-c", "-H", "-i", "-t")
cmd_execute('-f', path, '-c', '-i', '-H', '-t')
rescue
# if this fails, then we attempt without impersonation
print_error( "Failed to spawn shell with thread impersonation. Retrying without it." )
cmd_execute("-f", path, "-c", "-H", "-i")
print_error('Failed to spawn shell with thread impersonation. Retrying without it.')
cmd_execute('-f', path, '-c', '-i', '-H')
end
when 'linux', 'osx'
if use_pty && pty_shell(sh_path)
return true
end
# Don't expand_path() this because it's literal anyway
if use_alt
return true if alt_shell(alt_path)
end
path = "/bin/sh"
cmd_execute("-f", path, "-c", "-i")
cmd_execute('-f', '/bin/sh', '-c', '-i')
else
# Then this is a multi-platform meterpreter (php or java), which
# Then this is a multi-platform meterpreter (e.g., php or java), which
# must special-case COMSPEC to return the system-specific shell.
path = client.fs.file.expand_path("%COMSPEC%")
path = client.fs.file.expand_path('%COMSPEC%')
# If that failed for whatever reason, guess it's unix
path = (path and not path.empty?) ? path : "/bin/sh"
if use_alt && path == "/bin/sh"
return true if alt_shell(alt_path)
path = (path && !path.empty?) ? path : '/bin/sh'
if use_pty && path == '/bin/sh' && pty_shell(sh_path)
return true
end
cmd_execute("-f", path, "-c", "-i")
cmd_execute('-f', comspec, '-c', '-i')
end
end
#
# Spawn an alternative shell
# Spawn a PTY shell
#
def alt_shell(sh_path)
def pty_shell(sh_path)
sh_path = client.fs.file.exist?(sh_path) ? sh_path : '/bin/sh'
# Python Meterpreter calls pty.openpty() - No need for other methods
if client.session_type =~ /python/
cmd_execute('-if', sh_path)
if client.arch == 'python'
cmd_execute('-f', sh_path, '-c', '-i')
return true
end
# Check for the following in /usr{,/local}/bin:
# script
# python{,2,3}
# socat
# expect
paths = %w[
/usr/bin/script
/usr/bin/python
/usr/local/bin/python
/usr/bin/python2
/usr/local/bin/python2
/usr/bin/python3
/usr/local/bin/python3
/usr/bin/socat
/usr/local/bin/socat
/usr/bin/expect
/usr/local/bin/expect
]
# Select method for spawning PTY Shell based on availability on the target.
path = nil
path = '/usr/bin/socat' if client.fs.file.exist?('/usr/bin/socat')
path = '/usr/bin/script' if client.fs.file.exist?('/usr/bin/script') && !path
path = '/usr/bin/python' if client.fs.file.exist?('/usr/bin/python') && !path
path = '/usr/bin/python2' if client.fs.file.exist?('/usr/bin/python2') && !path
path = '/usr/bin/python3' if client.fs.file.exist?('/usr/bin/python3') && !path
path = '/usr/bin/expect' if client.fs.file.exist?('/usr/bin/expect') && !path
path = paths.find { |p| client.fs.file.exist?(p) }
return false unless path
# Commands for methods - "env TERM=xterm" provides colors, "clear" command, etc. as available on the target.
cmd = "env TERM=xterm #{path} - exec:'#{sh_path} -li',pty,stderr,setsid,sigint,sane" if path =~ /socat/
cmd = "env TERM=xterm #{path} -qc #{sh_path} /dev/null || #{path} -q /dev/null #{sh_path}" if path =~ /script/
cmd = "env TERM=xterm #{path} -c \'import pty;pty.spawn(\"#{sh_path}\")\'" if path =~ /python/
cmd = "env TERM=xterm #{path} -c 'spawn #{sh_path}; interact'" if path =~ /expect/
cmd_execute('-if', cmd)
# Commands for methods
cmd =
case path
when /script/
if client.platform == 'linux'
"#{path} -qc #{sh_path} /dev/null"
else
# script(1) invocation for BSD, OS X, etc.
"#{path} -q /dev/null #{sh_path}"
end
when /python/
"#{path} -c 'import pty; pty.spawn(\"#{sh_path}\")'"
when /socat/
# sigint isn't passed through yet
"#{path} - exec:#{sh_path},pty,sane,setsid,sigint,stderr"
when /expect/
"#{path} -c 'spawn #{sh_path}; interact'"
end
# "env TERM=xterm" provides colors, "clear" command, etc. as available on the target.
cmd.prepend('env TERM=xterm HISTFILE= ')
print_status(cmd)
cmd_execute('-f', cmd, '-c', '-i')
true
rescue
false
end
#