move shell token stuff up to a mixin so meterpreter can use it, too
git-svn-id: file:///home/svn/framework3/trunk@11682 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
9163a7f0dd
commit
5e9fdc28e7
|
@ -99,75 +99,6 @@ class CommandShell
|
|||
buff
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Read data until we find the token
|
||||
#
|
||||
def shell_read_until_token(token, wanted_idx = 0, timeout = 10)
|
||||
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, 2))
|
||||
buf << tmp
|
||||
|
||||
# see if we have the wanted idx
|
||||
parts = buf.split(token, -1)
|
||||
if (parts.length == parts_needed)
|
||||
# cause another prompt to appear (just in case)
|
||||
shell_write("\n")
|
||||
return parts[wanted_idx]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
# nothing, just continue
|
||||
end
|
||||
|
||||
# failed to get any data or find the token!
|
||||
nil
|
||||
end
|
||||
|
||||
#
|
||||
# Explicitly run a single command and return the output.
|
||||
# This version uses a marker to denote the end of data (instead of a timeout).
|
||||
#
|
||||
def shell_command_token_unix(cmd)
|
||||
# read any pending data
|
||||
buf = shell_read(-1, 0.01)
|
||||
token = ::Rex::Text.rand_text_alpha(32)
|
||||
|
||||
# Send the command to the session's stdin.
|
||||
# NOTE: if the session echoes input we don't need to echo the token twice.
|
||||
shell_write(cmd + ";echo #{token}\n")
|
||||
shell_read_until_token(token)
|
||||
end
|
||||
|
||||
#
|
||||
# Explicitly run a single command and return the output.
|
||||
# This version uses a marker to denote the end of data (instead of a timeout).
|
||||
#
|
||||
def shell_command_token_win32(cmd)
|
||||
# read any pending data
|
||||
buf = shell_read(-1, 0.01)
|
||||
token = ::Rex::Text.rand_text_alpha(32)
|
||||
|
||||
# Send the command to the session's stdin.
|
||||
# NOTE: if the session echoes input we don't need to echo the token twice.
|
||||
shell_write(cmd + "&echo #{token}\n")
|
||||
shell_read_until_token(token, 1)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Read from the command shell.
|
||||
#
|
||||
|
|
|
@ -25,6 +25,11 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
include Msf::Session::Interactive
|
||||
include Msf::Session::Comm
|
||||
|
||||
#
|
||||
# This interface supports interacting with a single command shell.
|
||||
#
|
||||
include Msf::Session::Provider::SingleCommandShell
|
||||
|
||||
include Msf::Session::Scriptable
|
||||
|
||||
# Override for server implementations that can't do ssl
|
||||
|
@ -76,6 +81,79 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
self.class.type
|
||||
end
|
||||
|
||||
def shell_init
|
||||
return true if @shell
|
||||
|
||||
# COMSPEC is special-cased on all meterpreters to return a viable
|
||||
# shell.
|
||||
sh = fs.file.expand_path("%COMSPEC%")
|
||||
@shell = sys.process.execute(sh, nil, { "Hidden" => true, "Channelized" => true })
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Read from the command shell.
|
||||
#
|
||||
def shell_read(length=nil, timeout=1)
|
||||
shell_init
|
||||
|
||||
length = nil if length < 0
|
||||
begin
|
||||
rv = nil
|
||||
# Meterpreter doesn't offer a way to timeout on the victim side, so
|
||||
# we have to do it here. I'm concerned that this will cause loss
|
||||
# of data.
|
||||
Timeout.timeout(timeout) {
|
||||
rv = @shell.channel.read(length)
|
||||
}
|
||||
framework.events.on_session_output(self, rv) if rv
|
||||
return rv
|
||||
rescue ::Timeout::Error
|
||||
return nil
|
||||
rescue ::Exception => e
|
||||
shell_close
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Write to the command shell.
|
||||
#
|
||||
def shell_write(buf)
|
||||
shell_init
|
||||
|
||||
begin
|
||||
framework.events.on_session_command(self, buf.strip)
|
||||
len = @shell.channel.write(buf + "\r\n")
|
||||
rescue ::Exception => e
|
||||
shell_close
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
def shell_close
|
||||
@shell.close
|
||||
@shell = nil
|
||||
end
|
||||
|
||||
def shell_command(cmd)
|
||||
# Send the shell channel's stdin.
|
||||
shell_write(cmd + "\n")
|
||||
|
||||
timeout = 5
|
||||
etime = ::Time.now.to_f + timeout
|
||||
buff = ""
|
||||
|
||||
# Keep reading data until no more data is available or the timeout is
|
||||
# reached.
|
||||
while (::Time.now.to_f < etime)
|
||||
res = shell_read(-1, 0.1)
|
||||
buff << res if res
|
||||
end
|
||||
|
||||
buff
|
||||
end
|
||||
|
||||
#
|
||||
# Called by PacketDispatcher to resolve error codes to names.
|
||||
# This is the default version (return the number itself)
|
||||
|
@ -168,7 +246,7 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
end
|
||||
|
||||
#
|
||||
# Explicitly runs a command.
|
||||
# Explicitly runs a command in the meterpreter console.
|
||||
#
|
||||
def run_cmd(cmd)
|
||||
console.run_single(cmd)
|
||||
|
|
|
@ -38,6 +38,83 @@ module SingleCommandShell
|
|||
raise NotImplementedError
|
||||
end
|
||||
|
||||
#
|
||||
# Read data until we find the token
|
||||
#
|
||||
def shell_read_until_token(token, wanted_idx = 0, timeout = 10)
|
||||
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, 2))
|
||||
buf << tmp
|
||||
|
||||
# see if we have the wanted idx
|
||||
parts = buf.split(token, -1)
|
||||
if (parts.length == parts_needed)
|
||||
# cause another prompt to appear (just in case)
|
||||
shell_write("\n")
|
||||
return parts[wanted_idx]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
# nothing, just continue
|
||||
end
|
||||
|
||||
# failed to get any data or find the token!
|
||||
nil
|
||||
end
|
||||
|
||||
def shell_command_token(cmd)
|
||||
if platform =~ /win/
|
||||
output = shell_command_token_win32(cmd)
|
||||
else
|
||||
output = shell_command_token_unix(cmd)
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
#
|
||||
# Explicitly run a single command and return the output.
|
||||
# This version uses a marker to denote the end of data (instead of a timeout).
|
||||
#
|
||||
def shell_command_token_unix(cmd)
|
||||
# read any pending data
|
||||
buf = shell_read(-1, 0.01)
|
||||
token = ::Rex::Text.rand_text_alpha(32)
|
||||
|
||||
# Send the command to the session's stdin.
|
||||
# NOTE: if the session echoes input we don't need to echo the token twice.
|
||||
shell_write(cmd + ";echo #{token}\n")
|
||||
shell_read_until_token(token)
|
||||
end
|
||||
|
||||
#
|
||||
# Explicitly run a single command and return the output.
|
||||
# This version uses a marker to denote the end of data (instead of a timeout).
|
||||
#
|
||||
def shell_command_token_win32(cmd)
|
||||
# read any pending data
|
||||
buf = shell_read(-1, 0.01)
|
||||
token = ::Rex::Text.rand_text_alpha(32)
|
||||
|
||||
# Send the command to the session's stdin.
|
||||
# NOTE: if the session echoes input we don't need to echo the token twice.
|
||||
shell_write(cmd + "&echo #{token}\n")
|
||||
shell_read_until_token(token, 1)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue