From aa04d88b873b9654ec20d71907deeecf5a3c6d95 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Mon, 15 Apr 2024 14:51:07 +1000 Subject: [PATCH 01/11] Handle new cmd_exec TLV format --- .../stdapi/server/sys/process/process.c | 197 +++++++++++++++--- .../source/extensions/stdapi/stdapi.h | 3 + 2 files changed, 171 insertions(+), 29 deletions(-) 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..5f77cfdc3 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,164 @@ DWORD request_sys_process_close(Remote *remote, Packet *packet) return ERROR_SUCCESS; } +BOOL needs_quoting(PCHAR str) +{ + BOOL bNeedsQuoting = FALSE; + char* pArgIndex = str; + // Check whether we'll need to quote the argument + while (*pArgIndex != '\0') + { + if (*pArgIndex == '\v' || *pArgIndex == ' ' || *pArgIndex == '\t') + { + bNeedsQuoting = TRUE; + break; + } + ++pArgIndex; + } + + return bNeedsQuoting; +} + +DWORD get_arguments(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; + + path = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH); + + if (flags & PROCESS_EXECUTE_FLAG_ARG_ARRAY) + { + // 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 + { + dprintf("[PROCESS] Using legacy command line: %s", *commandLine); + 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; + } + } + return ERROR_SUCCESS; +} + /* * Executes a process using the supplied parameters, optionally creating a * channel through which output is filtered. @@ -122,7 +280,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 +316,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 arguments + result = get_arguments(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 +362,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 +749,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..eea75e649 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,8 @@ #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 ) // Driver enum stuff #define TLV_TYPE_DRIVER_ENTRY MAKE_CUSTOM_TLV( TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_STDAPI, 2320 ) From e68225d4b10473452a9c824b9fd3df0fdeb60358 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Mon, 15 Apr 2024 15:50:04 +1000 Subject: [PATCH 02/11] Python meterp to support new cmd_exec --- python/meterpreter/ext_server_stdapi.py | 44 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py index d7abbcd89..b84562b2e 100644 --- a/python/meterpreter/ext_server_stdapi.py +++ b/python/meterpreter/ext_server_stdapi.py @@ -655,6 +655,7 @@ class RTMSG(ctypes.Structure): 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_IMAGE_FILE = TLV_META_TYPE_STRING | 2400 TLV_TYPE_IMAGE_FILE_PATH = TLV_META_TYPE_STRING | 2401 @@ -724,6 +725,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 @@ -1409,21 +1411,33 @@ 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] + + flags = packet_get_tlv(request, TLV_TYPE_PROCESS_FLAGS)['value'] + if flags & PROCESS_EXECUTE_FLAG_ARG_ARRAY: + cmd_array = [cmd] + for arg in packet_enum_tlvs(request, TLV_TYPE_PROCESS_ARGUMENT): + cmd_array.append(arg['value']) + + # In case it's using a subshell: + cmd_string = shlex.join(cmd_array) else: - args = [cmd] - args.extend(shlex.split(raw_args)) + # 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 + + # In case we're not using a subshell: + cmd_array = [cmd] + cmd_array.extend(shlex.split(arg_string)) + + if os.path.isfile('/bin/sh') and (flags & PROCESS_EXECUTE_FLAG_SUBSHELL): + cmd_array = ['/bin/sh', '-c', cmd_string] if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): if has_pty and (flags & PROCESS_EXECUTE_FLAG_PTY): @@ -1434,17 +1448,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(cmd_array, 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(cmd_array, 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(cmd_array, 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) From 6631a198e7a3448f98e3c9281096d8bd99c94852 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Mon, 15 Apr 2024 17:33:44 +1000 Subject: [PATCH 03/11] PHP implementation of new_cmd --- php/meterpreter/ext_server_stdapi.php | 31 +++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/php/meterpreter/ext_server_stdapi.php b/php/meterpreter/ext_server_stdapi.php index f6a5d6c6a..6fdab6184 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); @@ -113,6 +114,7 @@ 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_IMAGE_FILE", TLV_META_TYPE_STRING | 2400); define("TLV_TYPE_IMAGE_FILE_PATH", TLV_META_TYPE_STRING | 2401); @@ -898,20 +900,35 @@ function stdapi_sys_process_execute($req, &$pkt) { 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']; # 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; } - $real_cmd = $cmd ." ". $args; + + $flags_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_FLAGS); + $flags = $flags_tlv['value']; + + # 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) { + $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); + } + } else { + $args_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_ARGUMENTS); + $args = $args_tlv['value']; + $real_cmd = $cmd ." ". $args; + my_print("Cmd: $real_cmd"); + } + $pipe_desc = array(array('pipe','r'), array('pipe','w')); if (is_windows()) { From 133e8f0b0177b5bbd356451ecd292bdca37ad74b Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Tue, 16 Apr 2024 09:19:27 +1000 Subject: [PATCH 04/11] Implement new cmd_exec for Java --- .../com/metasploit/meterpreter/TLVType.java | 1 + .../stdapi/stdapi_sys_process_execute.java | 43 +++++++++++++------ 2 files changed, 32 insertions(+), 12 deletions(-) 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..2d5ba15f3 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 @@ -160,6 +160,7 @@ public interface TLVType { 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_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..6a62f915d 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,42 @@ 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); - - cmdbuf.append(cmd); - if (argsString.length() > 0) { - cmdbuf.append(" "); - cmdbuf.append(argsString); - } - - if (cmd.length() == 0) { return ERROR_FAILURE; } - Process proc = execute(cmdbuf.toString()); + int flags = request.getIntValue(TLVType.TLV_TYPE_PROCESS_FLAGS); + Process proc; + if ((flags & PROCESS_EXECUTE_FLAG_ARG_ARRAY) != 0) { + 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 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()); + } + if ((flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) != 0) { ProcessChannel channel = new ProcessChannel(meterpreter, proc); @@ -49,6 +60,14 @@ 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); + return builder.start(); + } + protected Process execute(String cmdstr) throws IOException { Process proc = Runtime.getRuntime().exec(cmdstr); return proc; From 92e9de46aea3e1525264750264cad70d99f16b42 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Thu, 18 Apr 2024 13:01:48 +1000 Subject: [PATCH 05/11] Don't use subshell with array arguments --- .../source/extensions/stdapi/server/sys/process/process.c | 6 +++--- php/meterpreter/ext_server_stdapi.php | 2 +- python/meterpreter/ext_server_stdapi.py | 7 ++----- 3 files changed, 6 insertions(+), 9 deletions(-) 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 5f77cfdc3..6e664d1fe 100644 --- a/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c +++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c @@ -123,7 +123,7 @@ BOOL needs_quoting(PCHAR str) return bNeedsQuoting; } -DWORD get_arguments(Packet *packet, DWORD flags, PCHAR* commandLine) +DWORD get_commandline(Packet *packet, DWORD flags, PCHAR* commandLine) { // Check new-style arguments first DWORD dwTlvIndex = 0; @@ -319,8 +319,8 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet) flags = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS); - // Get the execution arguments - result = get_arguments(packet, flags, &commandLine); + // Get the execution command line + result = get_commandline(packet, flags, &commandLine); if (result != ERROR_SUCCESS) { break; diff --git a/php/meterpreter/ext_server_stdapi.php b/php/meterpreter/ext_server_stdapi.php index 6fdab6184..e372dcead 100755 --- a/php/meterpreter/ext_server_stdapi.php +++ b/php/meterpreter/ext_server_stdapi.php @@ -114,7 +114,7 @@ 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_ARGUMENT", TLV_META_TYPE_STRING | 2310); define("TLV_TYPE_IMAGE_FILE", TLV_META_TYPE_STRING | 2400); define("TLV_TYPE_IMAGE_FILE_PATH", TLV_META_TYPE_STRING | 2401); diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py index b84562b2e..3e1fe4096 100644 --- a/python/meterpreter/ext_server_stdapi.py +++ b/python/meterpreter/ext_server_stdapi.py @@ -1420,9 +1420,6 @@ def stdapi_sys_process_execute(request, response): cmd_array = [cmd] for arg in packet_enum_tlvs(request, TLV_TYPE_PROCESS_ARGUMENT): cmd_array.append(arg['value']) - - # In case it's using a subshell: - cmd_string = shlex.join(cmd_array) else: # Legacy argument style arg_string = packet_get_tlv(request, TLV_TYPE_PROCESS_ARGUMENTS) @@ -1436,8 +1433,8 @@ def stdapi_sys_process_execute(request, response): cmd_array = [cmd] cmd_array.extend(shlex.split(arg_string)) - if os.path.isfile('/bin/sh') and (flags & PROCESS_EXECUTE_FLAG_SUBSHELL): - cmd_array = ['/bin/sh', '-c', cmd_string] + if os.path.isfile('/bin/sh') and (flags & PROCESS_EXECUTE_FLAG_SUBSHELL): + cmd_array = ['/bin/sh', '-c', cmd_string] if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): if has_pty and (flags & PROCESS_EXECUTE_FLAG_PTY): From 5c2486c0039c1c50d2a67fbcc7ffc67f7b207731 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Thu, 18 Apr 2024 14:39:04 +1000 Subject: [PATCH 06/11] Treat old-style path separately to new (unescaped) path --- .../stdapi/server/sys/process/process.c | 6 +- .../source/extensions/stdapi/stdapi.h | 1 + .../com/metasploit/meterpreter/TLVType.java | 33 +++++----- .../stdapi/stdapi_sys_process_execute.java | 16 +++-- php/meterpreter/ext_server_stdapi.php | 60 +++++++++++-------- python/meterpreter/ext_server_stdapi.py | 52 +++++++++------- 6 files changed, 96 insertions(+), 72 deletions(-) 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 6e664d1fe..0a56faba3 100644 --- a/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c +++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c @@ -136,10 +136,9 @@ DWORD get_commandline(Packet *packet, DWORD flags, PCHAR* commandLine) PCHAR path, arguments = NULL; DWORD backslashCount; - path = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH); - 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; @@ -236,7 +235,7 @@ DWORD get_commandline(Packet *packet, DWORD flags, PCHAR* commandLine) } else { - dprintf("[PROCESS] Using legacy command line: %s", *commandLine); + 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 @@ -259,6 +258,7 @@ DWORD get_commandline(Packet *packet, DWORD flags, PCHAR* commandLine) { return ERROR_INVALID_PARAMETER; } + dprintf("[PROCESS] Using legacy command line: %s", *commandLine); } return ERROR_SUCCESS; } diff --git a/c/meterpreter/source/extensions/stdapi/stdapi.h b/c/meterpreter/source/extensions/stdapi/stdapi.h index eea75e649..d874a4af4 100644 --- a/c/meterpreter/source/extensions/stdapi/stdapi.h +++ b/c/meterpreter/source/extensions/stdapi/stdapi.h @@ -75,6 +75,7 @@ #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/TLVType.java b/java/meterpreter/meterpreter/src/main/java/com/metasploit/meterpreter/TLVType.java index 2d5ba15f3..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,22 +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_PROCESS_ARGUMENT = TLVPacket.TLV_META_TYPE_STRING | 2310; + 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 6a62f915d..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 @@ -18,14 +18,14 @@ public class stdapi_sys_process_execute implements Command { private static int pid = 0; public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket response) throws Exception { - String cmd = request.getStringValue(TLVType.TLV_TYPE_PROCESS_PATH); - if (cmd.length() == 0) { - return ERROR_FAILURE; - } - 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; + } + List rawArgs = request.getValues(TLVType.TLV_TYPE_PROCESS_ARGUMENT); ArrayList args = new ArrayList(); for (int i = 0; i < rawArgs.size(); ++i) { @@ -33,6 +33,11 @@ public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket respons } proc = execute(cmd, args); } else { + String cmd = request.getStringValue(TLVType.TLV_TYPE_PROCESS_PATH); + if (cmd.length() == 0) { + return ERROR_FAILURE; + } + String argsString = request.getStringValue(TLVType.TLV_TYPE_PROCESS_ARGUMENTS, ""); StringBuilder cmdbuf = new StringBuilder(); cmdbuf.append(cmd); @@ -65,6 +70,7 @@ protected Process execute(String cmd, ArrayList args) throws IOException cmdAndArgs.add(cmd); cmdAndArgs.addAll(args); ProcessBuilder builder = new ProcessBuilder(cmdAndArgs); + builder.directory(Loader.getCWD()); return builder.start(); } diff --git a/php/meterpreter/ext_server_stdapi.php b/php/meterpreter/ext_server_stdapi.php index e372dcead..7592e4947 100755 --- a/php/meterpreter/ext_server_stdapi.php +++ b/php/meterpreter/ext_server_stdapi.php @@ -99,22 +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_PROCESS_ARGUMENT", TLV_META_TYPE_STRING | 2310); +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); @@ -899,15 +900,6 @@ 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); - $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; - } - $flags_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_FLAGS); $flags = $flags_tlv['value']; @@ -915,6 +907,15 @@ function stdapi_sys_process_execute($req, &$pkt) { # 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; + } + $args_tlv = packet_get_all_tlvs($req, TLV_TYPE_PROCESS_ARGUMENT); $real_cmd = array(); array_push($real_cmd, $cmd); @@ -923,6 +924,15 @@ function stdapi_sys_process_execute($req, &$pkt) { array_push($real_cmd, $arg); } } 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; diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py index 3e1fe4096..a10ab64e1 100644 --- a/python/meterpreter/ext_server_stdapi.py +++ b/python/meterpreter/ext_server_stdapi.py @@ -638,24 +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_PROCESS_ARGUMENT = TLV_META_TYPE_STRING | 2310 +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 @@ -1410,17 +1411,22 @@ 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'] - - if len(cmd) == 0: - return ERROR_FAILURE, response - flags = packet_get_tlv(request, TLV_TYPE_PROCESS_FLAGS)['value'] 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 + cmd_array = [cmd] for arg in packet_enum_tlvs(request, TLV_TYPE_PROCESS_ARGUMENT): cmd_array.append(arg['value']) else: + 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: From 5422a15c7956c47fa2fab6528951e4b0deb23f10 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Thu, 18 Apr 2024 19:44:40 +1000 Subject: [PATCH 07/11] Allow collection-style TLVs to have zero elements --- .../src/main/java/com/metasploit/meterpreter/TLVPacket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { From a470c4dc3a8bda4cd9911dfd1cb3651daf98c51c Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Wed, 25 Sep 2024 16:18:21 +1000 Subject: [PATCH 08/11] Fix edge cases in Python and C meterpreters --- .../extensions/stdapi/server/sys/process/process.c | 5 ++++- python/meterpreter/ext_server_stdapi.py | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) 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 0a56faba3..7475ba3fe 100644 --- a/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c +++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/process.c @@ -107,11 +107,14 @@ DWORD request_sys_process_close(Remote *remote, Packet *packet) BOOL needs_quoting(PCHAR str) { - BOOL bNeedsQuoting = FALSE; + // 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; diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py index a10ab64e1..158baca1e 100644 --- a/python/meterpreter/ext_server_stdapi.py +++ b/python/meterpreter/ext_server_stdapi.py @@ -1435,11 +1435,15 @@ def stdapi_sys_process_execute(request, response): arg_string = "" cmd_string = cmd + ' ' + arg_string - # In case we're not using a subshell: - cmd_array = [cmd] - cmd_array.extend(shlex.split(arg_string)) + if arg_string == '': + # Everything was just provided in a single argument. Need to split it out. + cmd_array = shlex.split(cmd) + else: + # In case we're not using a subshell: + cmd_array = [cmd] + cmd_array.extend(shlex.split(arg_string)) - if os.path.isfile('/bin/sh') and (flags & PROCESS_EXECUTE_FLAG_SUBSHELL): + if (flags & PROCESS_EXECUTE_FLAG_SUBSHELL) and os.path.isfile('/bin/sh'): cmd_array = ['/bin/sh', '-c', cmd_string] if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): From af7f639bbc8c9cf9d845a01bf5acc165399cb4d6 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Mon, 30 Sep 2024 20:47:10 +1000 Subject: [PATCH 09/11] Skip shell in old versions of PHP --- php/meterpreter/ext_server_stdapi.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/php/meterpreter/ext_server_stdapi.php b/php/meterpreter/ext_server_stdapi.php index 7592e4947..c17bedce8 100755 --- a/php/meterpreter/ext_server_stdapi.php +++ b/php/meterpreter/ext_server_stdapi.php @@ -902,6 +902,7 @@ function stdapi_sys_process_execute($req, &$pkt) { my_print("doing execute"); $flags_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_FLAGS); $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. @@ -944,13 +945,14 @@ function stdapi_sys_process_execute($req, &$pkt) { if (is_windows()) { # see http://us2.php.net/manual/en/function.proc-open.php#97012 array_push($pipe_desc, array('pipe','a')); + $options['bypass_shell'] = true; } else { array_push($pipe_desc, array('pipe','w')); } # 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; } From 0b9ff3fea97cdf8bc1cda1a00808e87b88d55c9e Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Tue, 1 Oct 2024 15:48:08 +1000 Subject: [PATCH 10/11] On Windows with basic shell, just submit a single string, to match other Meterp/shell behaviours --- python/meterpreter/ext_server_stdapi.py | 29 ++++++++++++++----------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py index 158baca1e..8edbb74ec 100644 --- a/python/meterpreter/ext_server_stdapi.py +++ b/python/meterpreter/ext_server_stdapi.py @@ -1418,9 +1418,9 @@ def stdapi_sys_process_execute(request, response): if len(cmd) == 0: return ERROR_FAILURE, response - cmd_array = [cmd] + command = [cmd] for arg in packet_enum_tlvs(request, TLV_TYPE_PROCESS_ARGUMENT): - cmd_array.append(arg['value']) + command.append(arg['value']) else: cmd = packet_get_tlv(request, TLV_TYPE_PROCESS_PATH)['value'] @@ -1435,16 +1435,19 @@ def stdapi_sys_process_execute(request, response): arg_string = "" cmd_string = cmd + ' ' + arg_string - if arg_string == '': - # Everything was just provided in a single argument. Need to split it out. - cmd_array = shlex.split(cmd) + if sys.platform == 'win32': + command = cmd_string else: - # In case we're not using a subshell: - cmd_array = [cmd] - cmd_array.extend(shlex.split(arg_string)) + 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'): - cmd_array = ['/bin/sh', '-c', cmd_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): @@ -1455,17 +1458,17 @@ def stdapi_sys_process_execute(request, response): termios.tcsetattr(master, termios.TCSADRAIN, settings) except: pass - proc_h = STDProcess(cmd_array, 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(cmd_array, 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(cmd_array, 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) From 64bd2f16a23386a5d2b1a78d6e67adf63852ff3c Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Thu, 3 Oct 2024 21:56:31 +1000 Subject: [PATCH 11/11] Only bypass for new style, for backwards compatibility --- php/meterpreter/ext_server_stdapi.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/php/meterpreter/ext_server_stdapi.php b/php/meterpreter/ext_server_stdapi.php index c17bedce8..5e7628ee7 100755 --- a/php/meterpreter/ext_server_stdapi.php +++ b/php/meterpreter/ext_server_stdapi.php @@ -924,6 +924,10 @@ function stdapi_sys_process_execute($req, &$pkt) { $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']; @@ -945,7 +949,6 @@ function stdapi_sys_process_execute($req, &$pkt) { if (is_windows()) { # see http://us2.php.net/manual/en/function.proc-open.php#97012 array_push($pipe_desc, array('pipe','a')); - $options['bypass_shell'] = true; } else { array_push($pipe_desc, array('pipe','w')); }