Fixes command parsing in Post::Common

The meterpreter API wants arguments in a seperate string (not an array,
mind you) just so it can concatenate them on the server side.
Originally, I worked around that by using Shellwords.shellwords to pull
out the first token. But! Shellwords.shellwords inexplicably and
inexcusably removes backslashes in ways that make it impossible to quote
things on Windows. This commit works around both of those things.
unstable
James Lee 2012-06-07 22:24:59 -06:00
parent 83d21df9f6
commit 1be9ce8649
3 changed files with 65 additions and 18 deletions

View File

@ -16,20 +16,21 @@ public class stdapi_sys_process_execute implements Command {
private static int pid = 0;
public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket response) throws Exception {
StringBuffer cmdbuf = new StringBuffer();
String cmd = request.getStringValue(TLVType.TLV_TYPE_PROCESS_PATH);
String argsString = request.getStringValue(TLVType.TLV_TYPE_PROCESS_ARGUMENTS, "");
StringTokenizer st = new StringTokenizer(argsString);
String[] cmdarray = new String[st.countTokens() + 1];
cmdarray[0] = cmd;
for (int i = 0; i < cmdarray.length - 1; i++) {
cmdarray[i + 1] = st.nextToken();
}
int flags = request.getIntValue(TLVType.TLV_TYPE_PROCESS_FLAGS);
cmdbuf.append(cmd);
if (argsString.length() > 0) {
cmdbuf.append(argsString);
}
if (cmd.length() == 0)
return ERROR_FAILURE;
Process proc = execute(cmdarray);
Process proc = execute(cmdbuf.toString());
if ((flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) != 0) {
ProcessChannel channel = new ProcessChannel(meterpreter, proc);
@ -47,8 +48,8 @@ public class stdapi_sys_process_execute implements Command {
return ERROR_SUCCESS;
}
protected Process execute(String[] cmdarray) throws IOException {
Process proc = Runtime.getRuntime().exec(cmdarray);
protected Process execute(String cmdstr) throws IOException {
Process proc = Runtime.getRuntime().exec(cmdstr);
return proc;
}
}

View File

@ -3,18 +3,64 @@ class Post
module Common
# Execute given command as hidden and channelize, output of command given as a multiline string.
# For certain versions of Meterpreter options can not be included in the cmd var
def cmd_exec(cmd, opts=nil, time_out=15)
#
# Executes +cmd+ on the remote system
#
# On Windows meterpreter, this will go through CreateProcess as the
# "commandLine" parameter. This means it will follow the same rules as
# Windows' path disambiguation. For example, if you were to call this method
# thusly:
#
# cmd_exec("c:\\program files\\sub dir\\program name")
#
# Windows would look for these executables, in this order, passing the rest
# of the line as arguments:
#
# c:\program.exe
# c:\program files\sub.exe
# c:\program files\sub dir\program.exe
# c:\program files\sub dir\program name.exe
#
# On POSIX meterpreter, if +args+ is set or if +cmd+ contains shell
# metacharacters, the server will run the whole thing in /bin/sh. Otherwise,
# (cmd is a single path and there are no arguments), it will execve the given
# executable.
#
# On Java, it is passed through Runtime.getRuntime().exec(String) and PHP
# uses proc_open() both of which have similar semantics to POSIX.
#
# On shell sessions, this passes +cmd+ directly the session's
# +shell_command_token+ method.
#
# Returns a (possibly multi-line) String.
#
def cmd_exec(cmd, args=nil, time_out=15)
case session.type
when /meterpreter/
if opts.nil? and cmd =~ /\s*/
opts = Shellwords.shellwords(cmd)
cmd = opts.shift
opts = opts.join(" ")
#
# The meterpreter API requires arguments to come seperately from the
# executable path. This has no effect on Windows where the two are just
# blithely concatenated and passed to CreateProcess or its brethren. On
# POSIX, this allows the server to execve just the executable when a
# shell is not needed. Determining when a shell is not needed is not
# always easy, so it assumes anything with arguments needs to go through
# /bin/sh.
#
# This problem was originally solved by using Shellwords.shellwords but
# unfortunately, it is retarded. When a backslash occurs inside double
# quotes (as is often the case with Windows commands) it inexplicably
# removes them. So. Shellwords is out.
#
# By setting +args+ to an empty string, we can get POSIX to send it
# through /bin/sh, solving all the pesky parsing troubles, without
# affecting Windows.
#
if args.nil? and cmd =~ /[^a-zA-Z0-9\/._-]/
args = ""
end
session.response_timeout = time_out
process = session.sys.process.execute(cmd, opts, {'Hidden' => true, 'Channelized' => true})
process = session.sys.process.execute(cmd, args, {'Hidden' => true, 'Channelized' => true})
o = ""
while (d = process.channel.read)
break if d == ""
@ -23,7 +69,7 @@ module Common
process.channel.close
process.close
when /shell/
o = session.shell_command_token("#{cmd} #{opts}", time_out)
o = session.shell_command_token("#{cmd} #{args}", time_out)
o.chomp! if o
end
return "" if o.nil?