diff --git a/data/meterpreter/ext_server_stdapi.jar b/data/meterpreter/ext_server_stdapi.jar index bfa2a9d4b5..fa4c781431 100755 Binary files a/data/meterpreter/ext_server_stdapi.jar and b/data/meterpreter/ext_server_stdapi.jar differ diff --git a/external/source/meterpreter/java/src/stdapi/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java b/external/source/meterpreter/java/src/stdapi/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java index 8d5de4aa91..e1bc6877cc 100644 --- a/external/source/meterpreter/java/src/stdapi/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java +++ b/external/source/meterpreter/java/src/stdapi/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java @@ -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; } } diff --git a/lib/msf/core/post/common.rb b/lib/msf/core/post/common.rb index a7a7a8c7cd..d7ea9635de 100644 --- a/lib/msf/core/post/common.rb +++ b/lib/msf/core/post/common.rb @@ -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?