Land #10389, PTY support for Meterpreter "shell"
commit
7c79bbd998
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue