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-b9f4589650da
unstable
James Lee 2011-01-31 05:20:16 +00:00
parent 9163a7f0dd
commit 5e9fdc28e7
3 changed files with 156 additions and 70 deletions

View File

@ -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.
#

View File

@ -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)

View File

@ -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