diff --git a/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c b/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c index 57378a211..7475ba3fe 100644 --- a/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c +++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c @@ -105,6 +105,167 @@ DWORD request_sys_process_close(Remote *remote, Packet *packet) return ERROR_SUCCESS; } +BOOL needs_quoting(PCHAR str) +{ + // Initial value is to need quoting, in case it's an empty arg + BOOL bNeedsQuoting = TRUE; + char* pArgIndex = str; + // Check whether we'll need to quote the argument + while (*pArgIndex != '\0') + { + // The arg is not empty + bNeedsQuoting = FALSE; + if (*pArgIndex == '\v' || *pArgIndex == ' ' || *pArgIndex == '\t') + { + bNeedsQuoting = TRUE; + break; + } + ++pArgIndex; + } + + return bNeedsQuoting; +} + +DWORD get_commandline(Packet *packet, DWORD flags, PCHAR* commandLine) +{ + // Check new-style arguments first + DWORD dwTlvIndex = 0; + Tlv argTlv; + char* pArgStart; + char* pArgIndex; + BOOL bNeedsQuoting; + size_t commandLineLength = 0; + size_t commandLineWriteIndex = 0; + PCHAR path, arguments = NULL; + DWORD backslashCount; + + if (flags & PROCESS_EXECUTE_FLAG_ARG_ARRAY) + { + path = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_PROCESS_UNESCAPED_PATH); + // Calculate the potential size of our command line. + // The path may need quoting, so two extra chars there, plus null terminator. + commandLineLength = strlen(path) + 3; + + // Now look at arguments + while (ERROR_SUCCESS == met_api->packet.enum_tlv(packet, dwTlvIndex++, TLV_TYPE_PROCESS_ARGUMENT, &argTlv)) + { + commandLineLength += strlen((char*)argTlv.buffer); + commandLineLength += 3; // Two quotes bookending it, plus a space between the arguments + } + + // In the worst case, we've got a lot of backslashes just before a quote character, which will need to double in size. + // So, allocate for the worst-case expansion. + commandLineLength *= 2; + + if (!(*commandLine = (PCHAR)malloc(commandLineLength))) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Append the path to the command line, possibly quoting, but no escaping + bNeedsQuoting = needs_quoting(path); + if (bNeedsQuoting) + { + (*commandLine)[commandLineWriteIndex++] = '"'; + } + + strncpy_s((*commandLine) + commandLineWriteIndex, commandLineLength - commandLineWriteIndex, path, strlen(path)); + commandLineWriteIndex += strlen(path); + if (bNeedsQuoting) + { + (*commandLine)[commandLineWriteIndex++] = '"'; + } + + dwTlvIndex = 0; + while (ERROR_SUCCESS == met_api->packet.enum_tlv(packet, dwTlvIndex++, TLV_TYPE_PROCESS_ARGUMENT, &argTlv)) + { + if (dwTlvIndex != 0) + { + // Add a space between arguments + (*commandLine)[commandLineWriteIndex++] = ' '; + } + + pArgStart = (char*)argTlv.buffer; + bNeedsQuoting = needs_quoting(pArgStart); + + // Now build up the command line + pArgIndex = pArgStart; + backslashCount = 0; + if (bNeedsQuoting) + { + (*commandLine)[commandLineWriteIndex++] = '"'; + } + + while (*pArgIndex != '\0') + { + if (*pArgIndex == '\\') + { + ++backslashCount; + } + else + { + if (*pArgIndex == '"') + { + // We've encountered a double quote - if there are any backslashes immediately preceding, double them + for (DWORD i = 0; i < backslashCount; ++i) + { + (*commandLine)[commandLineWriteIndex++] = '\\'; + } + // Now actually escape the double-quote + (*commandLine)[commandLineWriteIndex++] = '\\'; + } + + backslashCount = 0; + } + // Now write out whatever the character was + (*commandLine)[commandLineWriteIndex++] = *pArgIndex; + + ++pArgIndex; + } + if (bNeedsQuoting) + { + // We're about to add another quote - check for backslash doubling again + for (DWORD i = 0; i < backslashCount; ++i) + { + (*commandLine)[commandLineWriteIndex++] = '\\'; + } + (*commandLine)[commandLineWriteIndex++] = '"'; + + } + } + (*commandLine)[commandLineWriteIndex++] = '\0'; + dprintf("[PROCESS] Created command line: %s", *commandLine); + } + else + { + path = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH); + arguments = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS); + // If the remote endpoint provided arguments, combine them with the + // executable to produce a command line + if (path && arguments) + { + commandLineLength = strlen(path) + strlen(arguments) + 2; + + if (!(*commandLine = (PCHAR)malloc(commandLineLength))) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + _snprintf(*commandLine, commandLineLength, "%s %s", path, arguments); + } + else if (path) + { + *commandLine = path; + } + else + { + return ERROR_INVALID_PARAMETER; + } + dprintf("[PROCESS] Using legacy command line: %s", *commandLine); + } + return ERROR_SUCCESS; +} + /* * Executes a process using the supplied parameters, optionally creating a * channel through which output is filtered. @@ -122,7 +283,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet) PROCESS_INFORMATION pi; STARTUPINFOEXW si; HANDLE in[2], out[2]; - PCHAR path, arguments, commandLine = NULL; + PCHAR commandLine = NULL; wchar_t* commandLine_w = NULL; DWORD flags = 0, createFlags = 0, ppid = 0; BOOL inherit = FALSE; @@ -158,11 +319,16 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet) { break; } + - // Get the execution arguments - arguments = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS); - path = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH); flags = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS); + // Get the execution command line + result = get_commandline(packet, flags, &commandLine); + if (result != ERROR_SUCCESS) + { + break; + } + ppid = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_PARENT_PID); if (met_api->packet.get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS) @@ -199,30 +365,6 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet) } while (0); } - // If the remote endpoint provided arguments, combine them with the - // executable to produce a command line - if (path && arguments) - { - size_t commandLineLength = strlen(path) + strlen(arguments) + 2; - - if (!(commandLine = (PCHAR)malloc(commandLineLength))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - break; - } - - _snprintf(commandLine, commandLineLength, "%s %s", path, arguments); - } - else if (path) - { - commandLine = path; - } - else - { - result = ERROR_INVALID_PARAMETER; - break; - } - // If the channelized flag is set, create a pipe for stdin/stdout/stderr // such that input can be directed to and from the remote endpoint if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) @@ -610,7 +752,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet) } // Free the command line if necessary - if (path && arguments && commandLine) + if (commandLine) { free(commandLine); } diff --git a/c/meterpreter/source/extensions/stdapi/stdapi.h b/c/meterpreter/source/extensions/stdapi/stdapi.h index adc2b5b52..d874a4af4 100644 --- a/c/meterpreter/source/extensions/stdapi/stdapi.h +++ b/c/meterpreter/source/extensions/stdapi/stdapi.h @@ -53,6 +53,7 @@ #define PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN (1 << 3) #define PROCESS_EXECUTE_FLAG_DESKTOP (1 << 4) #define PROCESS_EXECUTE_FLAG_SESSION (1 << 5) +#define PROCESS_EXECUTE_FLAG_ARG_ARRAY (1 << 8) #define TLV_TYPE_BASE_ADDRESS MAKE_CUSTOM_TLV( TLV_META_TYPE_QWORD, TLV_TYPE_EXTENSION_STDAPI, 2000 ) #define TLV_TYPE_ALLOCATION_TYPE MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 2001 ) @@ -72,6 +73,9 @@ #define TLV_TYPE_PROCESS_ARCH MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 2306 ) #define TLV_TYPE_PARENT_PID MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 2307 ) #define TLV_TYPE_PROCESS_SESSION MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 2308 ) +#define TLV_TYPE_PROCESS_ARCH_NAME MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 2309 ) +#define TLV_TYPE_PROCESS_ARGUMENT MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 2310 ) +#define TLV_TYPE_PROCESS_UNESCAPED_PATH MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 2311 ) // Driver enum stuff #define TLV_TYPE_DRIVER_ENTRY MAKE_CUSTOM_TLV( TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_STDAPI, 2320 ) diff --git a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVPacket.java b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVPacket.java index 16796ac45..d60199ffc 100644 --- a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVPacket.java +++ b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVPacket.java @@ -199,7 +199,7 @@ public List getValues(int type) { ArrayList values = new ArrayList(); ArrayList indices = (ArrayList) valueMap.get(new Integer(type)); if (indices == null) { - throw new IllegalArgumentException("Cannot find type " + type); + return values; } for (int i = 0; i < indices.size(); ++i) { diff --git a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVType.java b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVType.java index 96d61f98c..9895adac3 100644 --- a/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVType.java +++ b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVType.java @@ -145,21 +145,23 @@ public interface TLVType { public static final int TLV_TYPE_ENV_GROUP = TLVPacket.TLV_META_TYPE_GROUP | 1102; // Process - public static final int TLV_TYPE_BASE_ADDRESS = TLVPacket.TLV_META_TYPE_QWORD | 2000; - public static final int TLV_TYPE_ALLOCATION_TYPE = TLVPacket.TLV_META_TYPE_UINT | 2001; - public static final int TLV_TYPE_PROTECTION = TLVPacket.TLV_META_TYPE_UINT | 2002; - public static final int TLV_TYPE_PROCESS_PERMS = TLVPacket.TLV_META_TYPE_UINT | 2003; - public static final int TLV_TYPE_PROCESS_MEMORY = TLVPacket.TLV_META_TYPE_RAW | 2004; - public static final int TLV_TYPE_ALLOC_BASE_ADDRESS = TLVPacket.TLV_META_TYPE_QWORD | 2005; - public static final int TLV_TYPE_MEMORY_STATE = TLVPacket.TLV_META_TYPE_UINT | 2006; - public static final int TLV_TYPE_MEMORY_TYPE = TLVPacket.TLV_META_TYPE_UINT | 2007; - public static final int TLV_TYPE_ALLOC_PROTECTION = TLVPacket.TLV_META_TYPE_UINT | 2008; - public static final int TLV_TYPE_PID = TLVPacket.TLV_META_TYPE_UINT | 2300; - public static final int TLV_TYPE_PROCESS_NAME = TLVPacket.TLV_META_TYPE_STRING | 2301; - public static final int TLV_TYPE_PROCESS_PATH = TLVPacket.TLV_META_TYPE_STRING | 2302; - public static final int TLV_TYPE_PROCESS_GROUP = TLVPacket.TLV_META_TYPE_GROUP | 2303; - public static final int TLV_TYPE_PROCESS_FLAGS = TLVPacket.TLV_META_TYPE_UINT | 2304; - public static final int TLV_TYPE_PROCESS_ARGUMENTS = TLVPacket.TLV_META_TYPE_STRING | 2305; + public static final int TLV_TYPE_BASE_ADDRESS = TLVPacket.TLV_META_TYPE_QWORD | 2000; + public static final int TLV_TYPE_ALLOCATION_TYPE = TLVPacket.TLV_META_TYPE_UINT | 2001; + public static final int TLV_TYPE_PROTECTION = TLVPacket.TLV_META_TYPE_UINT | 2002; + public static final int TLV_TYPE_PROCESS_PERMS = TLVPacket.TLV_META_TYPE_UINT | 2003; + public static final int TLV_TYPE_PROCESS_MEMORY = TLVPacket.TLV_META_TYPE_RAW | 2004; + public static final int TLV_TYPE_ALLOC_BASE_ADDRESS = TLVPacket.TLV_META_TYPE_QWORD | 2005; + public static final int TLV_TYPE_MEMORY_STATE = TLVPacket.TLV_META_TYPE_UINT | 2006; + public static final int TLV_TYPE_MEMORY_TYPE = TLVPacket.TLV_META_TYPE_UINT | 2007; + public static final int TLV_TYPE_ALLOC_PROTECTION = TLVPacket.TLV_META_TYPE_UINT | 2008; + public static final int TLV_TYPE_PID = TLVPacket.TLV_META_TYPE_UINT | 2300; + public static final int TLV_TYPE_PROCESS_NAME = TLVPacket.TLV_META_TYPE_STRING | 2301; + public static final int TLV_TYPE_PROCESS_PATH = TLVPacket.TLV_META_TYPE_STRING | 2302; + public static final int TLV_TYPE_PROCESS_GROUP = TLVPacket.TLV_META_TYPE_GROUP | 2303; + public static final int TLV_TYPE_PROCESS_FLAGS = TLVPacket.TLV_META_TYPE_UINT | 2304; + public static final int TLV_TYPE_PROCESS_ARGUMENTS = TLVPacket.TLV_META_TYPE_STRING | 2305; + public static final int TLV_TYPE_PROCESS_ARGUMENT = TLVPacket.TLV_META_TYPE_STRING | 2310; + public static final int TLV_TYPE_PROCESS_UNESCAPED_PATH = TLVPacket.TLV_META_TYPE_STRING | 2311; public static final int TLV_TYPE_IMAGE_FILE = TLVPacket.TLV_META_TYPE_STRING | 2400; public static final int TLV_TYPE_IMAGE_FILE_PATH = TLVPacket.TLV_META_TYPE_STRING | 2401; diff --git a/java/meterpreter/stdapi/src/main/java/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java b/java/meterpreter/stdapi/src/main/java/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java index 60e4e6a7d..7707c7db2 100644 --- a/java/meterpreter/stdapi/src/main/java/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java +++ b/java/meterpreter/stdapi/src/main/java/com/metasploit/meterpreter/stdapi/stdapi_sys_process_execute.java @@ -7,31 +7,47 @@ import com.metasploit.meterpreter.command.Command; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; public class stdapi_sys_process_execute implements Command { private static final int PROCESS_EXECUTE_FLAG_CHANNELIZED = (1 << 1); + private static final int PROCESS_EXECUTE_FLAG_ARG_ARRAY = (1 << 8); private static int pid = 0; public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket response) throws Exception { - StringBuilder cmdbuf = new StringBuilder(); - String cmd = request.getStringValue(TLVType.TLV_TYPE_PROCESS_PATH); - String argsString = request.getStringValue(TLVType.TLV_TYPE_PROCESS_ARGUMENTS, ""); int flags = request.getIntValue(TLVType.TLV_TYPE_PROCESS_FLAGS); + Process proc; + if ((flags & PROCESS_EXECUTE_FLAG_ARG_ARRAY) != 0) { + String cmd = request.getStringValue(TLVType.TLV_TYPE_PROCESS_UNESCAPED_PATH); + if (cmd.length() == 0) { + return ERROR_FAILURE; + } - cmdbuf.append(cmd); - if (argsString.length() > 0) { - cmdbuf.append(" "); - cmdbuf.append(argsString); - } - + List rawArgs = request.getValues(TLVType.TLV_TYPE_PROCESS_ARGUMENT); + ArrayList args = new ArrayList(); + for (int i = 0; i < rawArgs.size(); ++i) { + args.add((String) rawArgs.get(i)); + } + proc = execute(cmd, args); + } else { + String cmd = request.getStringValue(TLVType.TLV_TYPE_PROCESS_PATH); + if (cmd.length() == 0) { + return ERROR_FAILURE; + } - if (cmd.length() == 0) { - return ERROR_FAILURE; + String argsString = request.getStringValue(TLVType.TLV_TYPE_PROCESS_ARGUMENTS, ""); + StringBuilder cmdbuf = new StringBuilder(); + cmdbuf.append(cmd); + if (argsString.length() > 0) { + cmdbuf.append(" "); + cmdbuf.append(argsString); + } + proc = execute(cmdbuf.toString()); } - Process proc = execute(cmdbuf.toString()); if ((flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) != 0) { ProcessChannel channel = new ProcessChannel(meterpreter, proc); @@ -49,6 +65,15 @@ public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket respons return ERROR_SUCCESS; } + protected Process execute(String cmd, ArrayList args) throws IOException { + ArrayList cmdAndArgs = new ArrayList(); + cmdAndArgs.add(cmd); + cmdAndArgs.addAll(args); + ProcessBuilder builder = new ProcessBuilder(cmdAndArgs); + builder.directory(Loader.getCWD()); + return builder.start(); + } + protected Process execute(String cmdstr) throws IOException { Process proc = Runtime.getRuntime().exec(cmdstr); return proc; diff --git a/php/meterpreter/ext_server_stdapi.php b/php/meterpreter/ext_server_stdapi.php index f6a5d6c6a..5e7628ee7 100755 --- a/php/meterpreter/ext_server_stdapi.php +++ b/php/meterpreter/ext_server_stdapi.php @@ -70,6 +70,7 @@ define("PROCESS_EXECUTE_FLAG_CHANNELIZED", (1 << 1)); define("PROCESS_EXECUTE_FLAG_SUSPENDED", (1 << 2)); define("PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN", (1 << 3)); +define("PROCESS_EXECUTE_FLAG_ARG_ARRAY", (1 << 8)); # Registry define("TLV_TYPE_HKEY", TLV_META_TYPE_QWORD | 1000); @@ -98,21 +99,23 @@ define("DELETE_KEY_FLAG_RECURSIVE", (1 << 0)); # Process -define("TLV_TYPE_BASE_ADDRESS", TLV_META_TYPE_QWORD | 2000); -define("TLV_TYPE_ALLOCATION_TYPE", TLV_META_TYPE_UINT | 2001); -define("TLV_TYPE_PROTECTION", TLV_META_TYPE_UINT | 2002); -define("TLV_TYPE_PROCESS_PERMS", TLV_META_TYPE_UINT | 2003); -define("TLV_TYPE_PROCESS_MEMORY", TLV_META_TYPE_RAW | 2004); -define("TLV_TYPE_ALLOC_BASE_ADDRESS", TLV_META_TYPE_QWORD | 2005); -define("TLV_TYPE_MEMORY_STATE", TLV_META_TYPE_UINT | 2006); -define("TLV_TYPE_MEMORY_TYPE", TLV_META_TYPE_UINT | 2007); -define("TLV_TYPE_ALLOC_PROTECTION", TLV_META_TYPE_UINT | 2008); -define("TLV_TYPE_PID", TLV_META_TYPE_UINT | 2300); -define("TLV_TYPE_PROCESS_NAME", TLV_META_TYPE_STRING | 2301); -define("TLV_TYPE_PROCESS_PATH", TLV_META_TYPE_STRING | 2302); -define("TLV_TYPE_PROCESS_GROUP", TLV_META_TYPE_GROUP | 2303); -define("TLV_TYPE_PROCESS_FLAGS", TLV_META_TYPE_UINT | 2304); -define("TLV_TYPE_PROCESS_ARGUMENTS", TLV_META_TYPE_STRING | 2305); +define("TLV_TYPE_BASE_ADDRESS", TLV_META_TYPE_QWORD | 2000); +define("TLV_TYPE_ALLOCATION_TYPE", TLV_META_TYPE_UINT | 2001); +define("TLV_TYPE_PROTECTION", TLV_META_TYPE_UINT | 2002); +define("TLV_TYPE_PROCESS_PERMS", TLV_META_TYPE_UINT | 2003); +define("TLV_TYPE_PROCESS_MEMORY", TLV_META_TYPE_RAW | 2004); +define("TLV_TYPE_ALLOC_BASE_ADDRESS", TLV_META_TYPE_QWORD | 2005); +define("TLV_TYPE_MEMORY_STATE", TLV_META_TYPE_UINT | 2006); +define("TLV_TYPE_MEMORY_TYPE", TLV_META_TYPE_UINT | 2007); +define("TLV_TYPE_ALLOC_PROTECTION", TLV_META_TYPE_UINT | 2008); +define("TLV_TYPE_PID", TLV_META_TYPE_UINT | 2300); +define("TLV_TYPE_PROCESS_NAME", TLV_META_TYPE_STRING | 2301); +define("TLV_TYPE_PROCESS_PATH", TLV_META_TYPE_STRING | 2302); +define("TLV_TYPE_PROCESS_GROUP", TLV_META_TYPE_GROUP | 2303); +define("TLV_TYPE_PROCESS_FLAGS", TLV_META_TYPE_UINT | 2304); +define("TLV_TYPE_PROCESS_ARGUMENTS", TLV_META_TYPE_STRING | 2305); +define("TLV_TYPE_PROCESS_ARGUMENT", TLV_META_TYPE_STRING | 2310); +define("TLV_TYPE_PROCESS_UNESCAPED_PATH", TLV_META_TYPE_STRING | 2311); define("TLV_TYPE_IMAGE_FILE", TLV_META_TYPE_STRING | 2400); define("TLV_TYPE_IMAGE_FILE_PATH", TLV_META_TYPE_STRING | 2401); @@ -897,21 +900,50 @@ function stdapi_sys_process_execute($req, &$pkt) { global $channel_process_map, $processes; my_print("doing execute"); - $cmd_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_PATH); - $args_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_ARGUMENTS); $flags_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_FLAGS); - - $cmd = $cmd_tlv['value']; - $args = $args_tlv['value']; $flags = $flags_tlv['value']; + $options = array(); + + # proc_open can take either a string or an array of strings + # We support both, based on the presence of a flag. + # PHP pre-7.4 doesn't support args-as-arrays + if (($flags & PROCESS_EXECUTE_FLAG_ARG_ARRAY) && version_compare(PHP_VERSION, '7.4.0') >= 0) { + $cmd_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_UNESCAPED_PATH); + $cmd = $cmd_tlv['value']; + + # If there was no command specified, well, a user sending an empty command + # deserves failure. + if (0 > strlen($cmd)) { + return ERROR_FAILURE; + } - # If there was no command specified, well, a user sending an empty command - # deserves failure. - my_print("Cmd: $cmd $args"); - if (0 > strlen($cmd)) { - return ERROR_FAILURE; + $args_tlv = packet_get_all_tlvs($req, TLV_TYPE_PROCESS_ARGUMENT); + $real_cmd = array(); + array_push($real_cmd, $cmd); + foreach ($args_tlv as $arg_tlv) { + $arg = $arg_tlv['value']; + array_push($real_cmd, $arg); + } + + if (is_windows()) { + $options['bypass_shell'] = true; + } + } else { + $cmd_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_PATH); + $cmd = $cmd_tlv['value']; + + # If there was no command specified, well, a user sending an empty command + # deserves failure. + if (0 > strlen($cmd)) { + return ERROR_FAILURE; + } + + $args_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_ARGUMENTS); + $args = $args_tlv['value']; + $real_cmd = $cmd ." ". $args; + my_print("Cmd: $real_cmd"); } - $real_cmd = $cmd ." ". $args; + $pipe_desc = array(array('pipe','r'), array('pipe','w')); if (is_windows()) { @@ -923,7 +955,7 @@ function stdapi_sys_process_execute($req, &$pkt) { # Now that we've got the command built, run it. If it worked, we'll send # back a handle identifier. - $handle = proc_open($real_cmd, $pipe_desc, $pipes); + $handle = proc_open($real_cmd, $pipe_desc, $pipes, null, null, $options); if (!is_resource($handle)) { return ERROR_FAILURE; } diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py index d7abbcd89..8edbb74ec 100644 --- a/python/meterpreter/ext_server_stdapi.py +++ b/python/meterpreter/ext_server_stdapi.py @@ -638,23 +638,25 @@ class RTMSG(ctypes.Structure): ## # Process ## -TLV_TYPE_BASE_ADDRESS = TLV_META_TYPE_QWORD | 2000 -TLV_TYPE_ALLOCATION_TYPE = TLV_META_TYPE_UINT | 2001 -TLV_TYPE_PROTECTION = TLV_META_TYPE_UINT | 2002 -TLV_TYPE_PROCESS_PERMS = TLV_META_TYPE_UINT | 2003 -TLV_TYPE_PROCESS_MEMORY = TLV_META_TYPE_RAW | 2004 -TLV_TYPE_ALLOC_BASE_ADDRESS = TLV_META_TYPE_QWORD | 2005 -TLV_TYPE_MEMORY_STATE = TLV_META_TYPE_UINT | 2006 -TLV_TYPE_MEMORY_TYPE = TLV_META_TYPE_UINT | 2007 -TLV_TYPE_ALLOC_PROTECTION = TLV_META_TYPE_UINT | 2008 -TLV_TYPE_PID = TLV_META_TYPE_UINT | 2300 -TLV_TYPE_PROCESS_NAME = TLV_META_TYPE_STRING | 2301 -TLV_TYPE_PROCESS_PATH = TLV_META_TYPE_STRING | 2302 -TLV_TYPE_PROCESS_GROUP = TLV_META_TYPE_GROUP | 2303 -TLV_TYPE_PROCESS_FLAGS = TLV_META_TYPE_UINT | 2304 -TLV_TYPE_PROCESS_ARGUMENTS = TLV_META_TYPE_STRING | 2305 -TLV_TYPE_PROCESS_ARCH = TLV_META_TYPE_UINT | 2306 -TLV_TYPE_PARENT_PID = TLV_META_TYPE_UINT | 2307 +TLV_TYPE_BASE_ADDRESS = TLV_META_TYPE_QWORD | 2000 +TLV_TYPE_ALLOCATION_TYPE = TLV_META_TYPE_UINT | 2001 +TLV_TYPE_PROTECTION = TLV_META_TYPE_UINT | 2002 +TLV_TYPE_PROCESS_PERMS = TLV_META_TYPE_UINT | 2003 +TLV_TYPE_PROCESS_MEMORY = TLV_META_TYPE_RAW | 2004 +TLV_TYPE_ALLOC_BASE_ADDRESS = TLV_META_TYPE_QWORD | 2005 +TLV_TYPE_MEMORY_STATE = TLV_META_TYPE_UINT | 2006 +TLV_TYPE_MEMORY_TYPE = TLV_META_TYPE_UINT | 2007 +TLV_TYPE_ALLOC_PROTECTION = TLV_META_TYPE_UINT | 2008 +TLV_TYPE_PID = TLV_META_TYPE_UINT | 2300 +TLV_TYPE_PROCESS_NAME = TLV_META_TYPE_STRING | 2301 +TLV_TYPE_PROCESS_PATH = TLV_META_TYPE_STRING | 2302 +TLV_TYPE_PROCESS_GROUP = TLV_META_TYPE_GROUP | 2303 +TLV_TYPE_PROCESS_FLAGS = TLV_META_TYPE_UINT | 2304 +TLV_TYPE_PROCESS_ARGUMENTS = TLV_META_TYPE_STRING | 2305 +TLV_TYPE_PROCESS_ARCH = TLV_META_TYPE_UINT | 2306 +TLV_TYPE_PARENT_PID = TLV_META_TYPE_UINT | 2307 +TLV_TYPE_PROCESS_ARGUMENT = TLV_META_TYPE_STRING | 2310 +TLV_TYPE_PROCESS_UNESCAPED_PATH = TLV_META_TYPE_STRING | 2311 TLV_TYPE_IMAGE_FILE = TLV_META_TYPE_STRING | 2400 TLV_TYPE_IMAGE_FILE_PATH = TLV_META_TYPE_STRING | 2401 @@ -724,6 +726,7 @@ class RTMSG(ctypes.Structure): PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN = (1 << 3) PROCESS_EXECUTE_FLAG_SUBSHELL = (1 << 6) PROCESS_EXECUTE_FLAG_PTY = (1 << 7) +PROCESS_EXECUTE_FLAG_ARG_ARRAY = (1 << 8) PROCESS_ARCH_UNKNOWN = 0 PROCESS_ARCH_X86 = 1 @@ -1408,22 +1411,43 @@ def stdapi_sys_process_close(request, response): @register_function def stdapi_sys_process_execute(request, response): - cmd = packet_get_tlv(request, TLV_TYPE_PROCESS_PATH)['value'] - raw_args = packet_get_tlv(request, TLV_TYPE_PROCESS_ARGUMENTS) - if raw_args: - raw_args = raw_args['value'] - else: - raw_args = "" flags = packet_get_tlv(request, TLV_TYPE_PROCESS_FLAGS)['value'] - if len(cmd) == 0: - return ERROR_FAILURE, response - if os.path.isfile('/bin/sh') and (flags & PROCESS_EXECUTE_FLAG_SUBSHELL): - if raw_args: - cmd = cmd + ' ' + raw_args - args = ['/bin/sh', '-c', cmd] + if flags & PROCESS_EXECUTE_FLAG_ARG_ARRAY: + cmd = packet_get_tlv(request, TLV_TYPE_PROCESS_UNESCAPED_PATH)['value'] + + if len(cmd) == 0: + return ERROR_FAILURE, response + + command = [cmd] + for arg in packet_enum_tlvs(request, TLV_TYPE_PROCESS_ARGUMENT): + command.append(arg['value']) else: - args = [cmd] - args.extend(shlex.split(raw_args)) + cmd = packet_get_tlv(request, TLV_TYPE_PROCESS_PATH)['value'] + + if len(cmd) == 0: + return ERROR_FAILURE, response + + # Legacy argument style + arg_string = packet_get_tlv(request, TLV_TYPE_PROCESS_ARGUMENTS) + if arg_string: + arg_string = arg_string['value'] + else: + arg_string = "" + cmd_string = cmd + ' ' + arg_string + + if sys.platform == 'win32': + command = cmd_string + else: + if arg_string == '': + # Everything was just provided in a single argument. Need to split it out. + command = shlex.split(cmd) + else: + # In case we're not using a subshell: + command = [cmd] + command.extend(shlex.split(arg_string)) + + if (flags & PROCESS_EXECUTE_FLAG_SUBSHELL) and os.path.isfile('/bin/sh'): + command = ['/bin/sh', '-c', cmd_string] if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): if has_pty and (flags & PROCESS_EXECUTE_FLAG_PTY): @@ -1434,17 +1458,17 @@ def stdapi_sys_process_execute(request, response): termios.tcsetattr(master, termios.TCSADRAIN, settings) except: pass - proc_h = STDProcess(args, stdin=slave, stdout=slave, stderr=slave, bufsize=0, preexec_fn=os.setsid) + proc_h = STDProcess(command, stdin=slave, stdout=slave, stderr=slave, bufsize=0, preexec_fn=os.setsid) proc_h.stdin = os.fdopen(master, 'wb') proc_h.stdout = os.fdopen(master, 'rb') proc_h.stderr = open(os.devnull, 'rb') proc_h.ptyfd = slave else: - proc_h = STDProcess(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc_h = STDProcess(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc_h.echo_protection = True proc_h.start() else: - proc_h = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc_h = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc_h_id = meterpreter.add_process(proc_h) response += tlv_pack(TLV_TYPE_PID, proc_h.pid)