From f0eac5877ac7f6f5895fac69d196065cad49ddba Mon Sep 17 00:00:00 2001 From: Brent Cook <bcook@rapid7.com> Date: Mon, 9 Mar 2015 12:20:02 -0600 Subject: [PATCH 1/7] Refactor filesystem operations Separate the stdapi handling code from the OS-dependent code. This makes testing and maintaining the code easier. This also happens to fix a number of bugs as a side-effect, because it is clearer what is happening now. --- .../source/extensions/stdapi/server/fs/dir.c | 507 +++++------------- .../source/extensions/stdapi/server/fs/file.c | 377 +++++-------- .../source/extensions/stdapi/server/fs/fs.h | 31 +- .../extensions/stdapi/server/fs/fs_local.h | 70 +++ .../extensions/stdapi/server/fs/fs_posix.c | 120 +++++ .../extensions/stdapi/server/fs/fs_util.c | 75 --- .../extensions/stdapi/server/fs/fs_win.c | 197 +++++++ .../workspace/ext_server_stdapi/Makefile | 2 +- .../ext_server_stdapi.vcxproj | 5 +- 9 files changed, 666 insertions(+), 718 deletions(-) create mode 100644 c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h create mode 100644 c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c delete mode 100644 c/meterpreter/source/extensions/stdapi/server/fs/fs_util.c create mode 100644 c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c index a94e3b3f..522dbba5 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c @@ -1,370 +1,137 @@ -#include "precomp.h" -#include <sys/stat.h> - -#ifndef _WIN32 - #include <dirent.h> -#endif - -/* - * Gets the contents of a given directory path and returns the list of file - * names to the requestor. - * - * TLVs: - * - * req: TLV_TYPE_DIRECTORY_PATH - The directory that should be listed - */ -DWORD request_fs_ls(Remote *remote, Packet *packet) -{ - Packet *response = packet_create_response(packet); - LPCSTR directory; - DWORD result = ERROR_SUCCESS; - LPSTR expanded = NULL, tempFile = NULL; - size_t tempFileSize = 0; - LPSTR baseDirectory = NULL; - struct meterp_stat buf; - - directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); - - // Enumerate the directory if one was provided - if (!directory) - result = ERROR_INVALID_PARAMETER; - else - { -#ifdef _WIN32 - WIN32_FIND_DATA data; - HANDLE ctx = NULL; -#else - DIR *ctx; - struct dirent *data; -#endif - BOOLEAN freeDirectory = FALSE; - LPSTR tempDirectory = (LPSTR)directory; - -#ifdef _WIN32 - // If there is not wildcard mask on the directory, create a version of the - // directory with a mask appended - if (!strrchr(directory, '*')) - { - if (!(tempDirectory = (LPSTR)malloc(strlen(directory) + 3))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - - sprintf(tempDirectory, "%s\\*", directory); - - // Dupe! - if (!(baseDirectory = _strdup(directory))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - } - // Otherwise, if it does have an asterisk, we need to scan back and find - // the base directory. If there is no slash, it means we're listing the - // cwd. - else - { - PCHAR slash = strrchr(directory, '\\'); - - if (slash) - { - *slash = 0; - - if (!(baseDirectory = _strdup(directory))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - - *slash = '\\'; - } - } - - // Expand the path - if (!(expanded = fs_expand_path(tempDirectory))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - - // Start the find operation - ctx = FindFirstFile(expanded, &data); - - #define DF_NAME data.cFileName -#else - expanded = 0; - ctx = opendir(tempDirectory); - if(ctx == NULL) - { - result = errno; - goto out; - } - data = readdir(ctx); - if (!(baseDirectory = _strdup(directory))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - - #define DF_NAME data->d_name -#endif - - do - { - size_t fullSize = (baseDirectory ? strlen(baseDirectory) : 0) + strlen(DF_NAME) + 2; - - // No context? Sucktastic - if (ctx == INVALID_HANDLE_VALUE) - { - result = GetLastError(); - break; - } - - // Allocate temporary storage to stat the file - if ((!tempFile) || (tempFileSize < fullSize)) - { - if (tempFile) - { - free(tempFile); - } - - // No memory means we suck a lot like spoon's mom - if (!(tempFile = (LPSTR)malloc(fullSize))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - break; - } - - // Update the tempFileSize so that we don't allocate if we don't - // need to like a true efficient ninja - tempFileSize = fullSize; - } - - // Build the full path - if (baseDirectory) - { -#ifdef _WIN32 - sprintf(tempFile, "%s\\%s", baseDirectory, DF_NAME); -#else - sprintf(tempFile, "%s/%s", baseDirectory, DF_NAME); -#endif - } - else - { - sprintf(tempFile, "%s", DF_NAME); - } - - // Add the file name to the response - packet_add_tlv_string(response, TLV_TYPE_FILE_NAME, DF_NAME); - // Add the full path - packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, tempFile); - - // Stat the file to get more information about it. - if (fs_stat(tempFile, &buf) >= 0) - { - packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, sizeof(buf)); - } - -#ifdef _WIN32 - } while (FindNextFile(ctx, &data)); -#else - } while (data = readdir(ctx)); -#endif -#undef DF_NAME - - // Clean up resources - if (freeDirectory) - { - free(tempDirectory); - } - - if (ctx) - { -#ifdef _WIN32 - FindClose(ctx); -#else - closedir(ctx); -#endif - } - } - - if (expanded) - { - free(expanded); - } - -out: - if (baseDirectory) - { - free(baseDirectory); - } - - // Set the result and transmit the response - packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - return ERROR_SUCCESS; -} - -/* - * Gets the current working directory - */ -DWORD request_fs_getwd(Remote *remote, Packet *packet) -{ - Packet *response = packet_create_response(packet); - DWORD directorySize = 4096, realSize; - LPSTR directory = NULL; - DWORD result = ERROR_SUCCESS; - - do - { -again: - // Allocate storage for the directory path - if (!(directory = (LPSTR)malloc(directorySize))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - break; - } - - memset(directory, 0, directorySize); - -#ifdef _WIN32 - if (!(realSize = GetCurrentDirectory(directorySize, directory))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - break; - } - else if (realSize > directorySize) - { - free(directory); - directorySize = realSize; - goto again; - } -#else - if (!getcwd(directory, directorySize)) - { - if (errno == ERANGE && directorySize > 0) - { - // Then we didn't allocate enough to hold the whole path, - // increase the size and try again. - free(directory); - directorySize = directorySize * 2; - goto again; - } else { - dprintf("getcwd failed with errno %d", errno); - break; - } - } -#endif - - packet_add_tlv_string(response, TLV_TYPE_DIRECTORY_PATH, - directory); - - } while (0); - - // Set the result and transmit the response - packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - if (directory) - free(directory); - - return ERROR_SUCCESS; -} - -/* - * Changes the working directory of the process - * - * TLVs: - * - * req: TLV_TYPE_DIRECTORY_PATH - The directory path to change the working - * directory to. - */ -DWORD request_fs_chdir(Remote *remote, Packet *packet) -{ - Packet *response = packet_create_response(packet); - LPCSTR directory; - DWORD result = ERROR_SUCCESS; - - directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); - - if (!directory) - result = ERROR_INVALID_PARAMETER; -#ifdef _WIN32 - else if (!SetCurrentDirectory(directory)) -#else - else if (chdir(directory)) -#endif - result = GetLastError(); - - packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - return ERROR_SUCCESS; -} - -/* - * Creates a new directory - * - * TLVs: - * - * req: TLV_TYPE_DIRECTORY_PATH - The directory path to create. - */ -DWORD request_fs_mkdir(Remote *remote, Packet *packet) -{ - Packet *response = packet_create_response(packet); - LPCSTR directory; - DWORD result = ERROR_SUCCESS; - - directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); - - if (!directory) - result = ERROR_INVALID_PARAMETER; -#ifdef _WIN32 - else if (!CreateDirectory(directory, NULL)) -#else - else if (mkdir(directory, 0777)) -#endif - result = GetLastError(); - - packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - return ERROR_SUCCESS; -} - -/* - * Removes the supplied directory from disk if it's empty - * - * TLVs: - * - * req: TLV_TYPE_DIRECTORY_PATH - The directory that is to be removed. - */ -DWORD request_fs_delete_dir(Remote *remote, Packet *packet) -{ - Packet *response = packet_create_response(packet); - LPCSTR directory; - DWORD result = ERROR_SUCCESS; - - directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); - - if (!directory) - result = ERROR_INVALID_PARAMETER; -#ifdef _WIN32 - else if (!RemoveDirectory(directory)) -#else - else if (rmdir(directory)) -#endif - result = GetLastError(); - - packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - return ERROR_SUCCESS; -} +#include "precomp.h" + +#include "fs_local.h" + +void request_fs_ls_cb(void *arg, char *name, const char *dir) +{ + Packet *response = arg; + char path[FS_MAX_PATH]; + struct meterp_stat s; + + /* + * Build the full path if we have a base directory + */ + if (dir) { + _snprintf(path, sizeof(path), "%s%c%s", dir, FS_SEPARATOR, name); + } else { + _snprintf(path, sizeof(path), "%s", name); + } + + /* + * Add the file name, full path and stat information + */ + packet_add_tlv_string(response, TLV_TYPE_FILE_NAME, name); + packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, path); + if (fs_stat(path, &s) >= 0) { + packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &s, sizeof(s)); + } +} + +/* + * Gets the contents of a given directory path and returns the list of file + * names to the requestor. + * + * req: TLV_TYPE_DIRECTORY_PATH - The directory that should be listed + */ +DWORD request_fs_ls(Remote * remote, Packet * packet) +{ + Packet *response = packet_create_response(packet); + LPCSTR directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); + DWORD result; + + if (!directory) { + result = ERROR_INVALID_PARAMETER; + } else { + result = fs_ls(directory, request_fs_ls_cb, response); + } + + packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); + return packet_transmit(remote, response, NULL); +} + +/* + * Gets the current working directory + * + * req: TLV_TYPE_DIRECTORY_PATH - The directory path to change the working + * directory to. + */ +DWORD request_fs_getwd(Remote * remote, Packet * packet) +{ + Packet *response = packet_create_response(packet); + char directory[FS_MAX_PATH]; + DWORD result; + + result = fs_getwd(directory, sizeof(directory)); + + packet_add_tlv_string(response, TLV_TYPE_DIRECTORY_PATH, directory); + + packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); + return packet_transmit(remote, response, NULL); +} + +/* + * Changes the working directory of the process + * + * req: TLV_TYPE_DIRECTORY_PATH - The directory path to change the working + * directory to. + */ +DWORD request_fs_chdir(Remote * remote, Packet * packet) +{ + Packet *response = packet_create_response(packet); + LPCSTR directory; + DWORD result; + directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); + + if (directory == NULL) { + result = ERROR_INVALID_PARAMETER; + } else { + result = fs_chdir(directory); + } + + packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); + return packet_transmit(remote, response, NULL); +} + +/* + * Creates a new directory + * + * req: TLV_TYPE_DIRECTORY_PATH - The directory path to create. + */ +DWORD request_fs_mkdir(Remote * remote, Packet * packet) +{ + Packet *response = packet_create_response(packet); + LPCSTR directory; + DWORD result; + directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); + + if (directory == NULL) { + result = ERROR_INVALID_PARAMETER; + } else { + result = fs_mkdir(directory); + } + + packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); + return packet_transmit(remote, response, NULL); +} + +/* + * Removes the supplied directory from disk if it's empty + * + * req: TLV_TYPE_DIRECTORY_PATH - The directory that is to be removed. + */ +DWORD request_fs_delete_dir(Remote * remote, Packet * packet) +{ + Packet *response = packet_create_response(packet); + LPCSTR directory; + DWORD result; + directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); + + if (directory == NULL) { + result = ERROR_INVALID_PARAMETER; + } else { + result = fs_delete_dir(directory); + } + + packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); + return packet_transmit(remote, response, NULL); +} diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/file.c b/c/meterpreter/source/extensions/stdapi/server/fs/file.c index 5b45959b..b35dd5f5 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/file.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/file.c @@ -1,4 +1,6 @@ #include "precomp.h" +#include "fs_local.h" + #include <sys/stat.h> #include <openssl/md5.h> @@ -26,15 +28,14 @@ static DWORD file_channel_write(Channel *channel, Packet *request, DWORD written = 0; // Write a chunk - if (bufferSize && (written = (DWORD)fwrite(buffer, 1, bufferSize, ctx->fd)) <= 0) - { - written = 0; - result = GetLastError(); + if (bufferSize) { + written = fwrite(buffer, 1, bufferSize, ctx->fd); + if (written < bufferSize) { + result = GetLastError(); + } } - // Set bytesWritten - if (bytesWritten) - { + if (bytesWritten) { *bytesWritten = written; } @@ -67,15 +68,16 @@ static DWORD file_channel_read(Channel *channel, Packet *request, DWORD bytes = 0; // Read a chunk - if (bufferSize && (bytes= (DWORD)fread(buffer, 1, bufferSize, ctx->fd)) <= 0) - { - bytes = 0; - result = GetLastError(); + if (bufferSize) { + bytes = fread(buffer, 1, bufferSize, ctx->fd); + if (bytes < bufferSize) { + result = GetLastError(); + } } - // Set bytesRead - if (bytesRead) + if (bytesRead) { *bytesRead = bytes; + } return ERROR_SUCCESS; } @@ -112,8 +114,9 @@ static DWORD file_channel_tell(Channel *channel, Packet *request, DWORD result = ERROR_SUCCESS; LONG pos = 0; - if ((pos = ftell(ctx->fd)) < 0) + if ((pos = ftell(ctx->fd)) < 0) { result = GetLastError(); + } if (offset) *offset = pos; @@ -136,90 +139,64 @@ DWORD request_fs_file_channel_open(Remote *remote, Packet *packet) FileContext *ctx; LPSTR expandedFilePath = NULL; - do - { - // Allocate a response - response = packet_create_response(packet); + // Allocate a response + response = packet_create_response(packet); - // Get the channel flags - flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS); + // Get the channel flags + flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS); - // Allocate storage for the file context - if (!(ctx = (FileContext *)malloc(sizeof(FileContext)))) - { - res = ERROR_NOT_ENOUGH_MEMORY; - break; - } + // Allocate storage for the file context + if (!(ctx = calloc(1, sizeof(FileContext)))) { + res = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } - // Get the file path and the mode - filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); - mode = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_MODE); + // Get the file path and the mode + filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); + mode = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_MODE); - // No file path? bogus. - if (!filePath) - { - res = ERROR_INVALID_PARAMETER; - break; - } + if (mode == NULL) { + mode = "rb"; + } - // Expand the file path - if (!(expandedFilePath = fs_expand_path(filePath))) - { - res = ERROR_NOT_ENOUGH_MEMORY; - break; - } + res = fs_fopen(filePath, mode, &ctx->fd); + if (res != ERROR_SUCCESS) { + goto out; + } - if (!mode) - mode = "rb"; + memset(&chops, 0, sizeof(chops)); - // Invalid file? - if (!(ctx->fd = fopen(expandedFilePath, mode))) - { - res = GetLastError(); - break; - } + // Initialize the pool operation handlers + chops.native.context = ctx; + chops.native.write = file_channel_write; + chops.native.close = file_channel_close; + chops.read = file_channel_read; + chops.eof = file_channel_eof; + chops.seek = file_channel_seek; + chops.tell = file_channel_tell; - memset(&chops, 0, sizeof(chops)); + // Check the response allocation & allocate a un-connected + // channel + if ((!response) || (!(newChannel = channel_create_pool(0, flags, &chops)))) { + res = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } - // Initialize the pool operation handlers - chops.native.context = ctx; - chops.native.write = file_channel_write; - chops.native.close = file_channel_close; - chops.read = file_channel_read; - chops.eof = file_channel_eof; - chops.seek = file_channel_seek; - chops.tell = file_channel_tell; - - // Check the response allocation & allocate a un-connected - // channel - if ((!response) || (!(newChannel = channel_create_pool(0, flags, &chops)))) - { - res = ERROR_NOT_ENOUGH_MEMORY; - break; - } - - // Add the channel identifier to the response - packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, - channel_get_id(newChannel)); - - } while (0); + // Add the channel identifier to the response + packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel)); +out: // Transmit the packet if it's valid packet_transmit_response(res, remote, response); // Clean up on failure - if (res != ERROR_SUCCESS) - { - if (newChannel) + if (res != ERROR_SUCCESS) { + if (newChannel) { channel_destroy(newChannel, NULL); - if (ctx) - free(ctx); + } + free(ctx); } - // Free the expanded file path if it was allocated - if (expandedFilePath) - free(expandedFilePath); - return res; } @@ -229,20 +206,12 @@ DWORD request_fs_file_channel_open(Remote *remote, Packet *packet) DWORD request_fs_separator(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); -#ifdef _WIN32 - LPCSTR separator = "\\"; -#else - LPCSTR separator = "/"; -#endif - packet_add_tlv_string(response, TLV_TYPE_STRING, separator); + packet_add_tlv_string(response, TLV_TYPE_STRING, FS_SEPARATOR_S); - // Set the result and transmit the response packet_add_tlv_uint(response, TLV_TYPE_RESULT, ERROR_SUCCESS); - packet_transmit(remote, response, NULL); - - return ERROR_SUCCESS; + return packet_transmit(remote, response, NULL); } @@ -250,73 +219,62 @@ DWORD request_fs_separator(Remote *remote, Packet *packet) * Gets information about the file path that is supplied and returns it to the * requestor * - * TLVs: - * * req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd */ DWORD request_fs_stat(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); struct meterp_stat buf; - LPCSTR filePath; - LPSTR expanded = NULL; + char *filePath; + char *expanded = NULL; DWORD result = ERROR_SUCCESS; filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); - // Validate parameters - if (!filePath) + if (!filePath) { result = ERROR_INVALID_PARAMETER; - else if (!(expanded = fs_expand_path(filePath))) - result = ERROR_NOT_ENOUGH_MEMORY; - else - { - result = fs_stat(expanded, &buf); - if (0 == result) - packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, - sizeof(buf)); + goto out; } - // Set the result and transmit the response + expanded = fs_expand_path(filePath); + if (expanded == NULL) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + result = fs_stat(expanded, &buf); + if (0 == result) { + packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, sizeof(buf)); + } + + free(expanded); + +out: packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - if (expanded) - free(expanded); - - return result; + return packet_transmit(remote, response, NULL); } /* * Removes the supplied file from disk * - * TLVs: - * * req: TLV_TYPE_FILE_PATH - The file that is to be removed. */ DWORD request_fs_delete_file(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); - LPCSTR path; + char *path; DWORD result = ERROR_SUCCESS; path = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); - if (!path) + if (!path) { result = ERROR_INVALID_PARAMETER; -#ifdef _WIN32 - else if (!DeleteFile(path)) -#else - else if (unlink(path)) -#endif - result = GetLastError(); + } else { + result = fs_delete_file(path); + } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - return ERROR_SUCCESS; + return packet_transmit(remote, response, NULL); } /* @@ -328,164 +286,107 @@ DWORD request_fs_file_expand_path(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; - LPSTR expanded = NULL; - LPSTR regular; + char *expanded = NULL; + char *regular; regular = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); + if (regular == NULL) { + result = ERROR_INVALID_PARAMETER; + goto out; + } - do - { - // No regular path? - if (!regular) - { - result = ERROR_INVALID_PARAMETER; - break; - } + // Allocate storage for the expanded path + expanded = fs_expand_path(regular); + if (expanded == NULL) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } - // Allocate storage for the expanded path - if (!(expanded = fs_expand_path(regular))) - { - result = ERROR_NOT_ENOUGH_MEMORY; - break; - } - - packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, expanded); - - } while (0); - - // Transmit the response to the mofo - packet_transmit_response(result, remote, response); - - if (expanded) - free(expanded); - - return ERROR_SUCCESS; + packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, expanded); + free(expanded); +out: + packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); + return packet_transmit(remote, response, NULL); } /* * Returns the MD5 hash for a specified file path * - * TLVs: - * * req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd */ DWORD request_fs_md5(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); - LPCSTR filePath; - LPSTR expanded = NULL; + char *filePath; DWORD result = ERROR_SUCCESS; MD5_CTX context; + FILE *fd; size_t ret; unsigned char buff[16384]; - unsigned char hash[128]; + unsigned char hash[MD5_DIGEST_LENGTH + 1] = {0}; filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); - // Validate parameters - if (!filePath) - result = ERROR_INVALID_PARAMETER; - else if (!(expanded = fs_expand_path(filePath))) - result = ERROR_NOT_ENOUGH_MEMORY; - else - { - do { - MD5_Init(&context); - fd = fopen(expanded, "rb"); - if (! fd) { - result = GetLastError(); - break; - } + result = fs_fopen(filePath, "rb", &fd); + if (result == ERROR_SUCCESS) { - while((ret = fread(buff, 1, sizeof(buff), fd)) > 0 ) { - MD5_Update(&context, buff, ret); - } + MD5_Init(&context); - fclose(fd); - MD5_Final(hash, &context); + while ((ret = fread(buff, 1, sizeof(buff), fd)) > 0 ) { + MD5_Update(&context, buff, ret); + } - // One byte extra for the NULL - packet_add_tlv_raw(response, TLV_TYPE_FILE_NAME, hash, 17); - } while(0); + MD5_Final(hash, &context); + + packet_add_tlv_raw(response, TLV_TYPE_FILE_NAME, hash, sizeof(hash)); + + fclose(fd); } - // Set the result and transmit the response packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - if (expanded) - free(expanded); - - return ERROR_SUCCESS; + return packet_transmit(remote, response, NULL); } - /* * Returns the SHA1 hash for a specified file path * - * TLVs: - * * req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd */ DWORD request_fs_sha1(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); - LPCSTR filePath; - LPSTR expanded = NULL; + char *filePath; DWORD result = ERROR_SUCCESS; SHA_CTX context; FILE *fd; size_t ret; unsigned char buff[16384]; - unsigned char hash[128]; + unsigned char hash[SHA_DIGEST_LENGTH + 1] = {0}; filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); - // Validate parameters - if (!filePath) - result = ERROR_INVALID_PARAMETER; - else if (!(expanded = fs_expand_path(filePath))) - result = ERROR_NOT_ENOUGH_MEMORY; - else - { - do { - SHA1_Init(&context); - fd = fopen(expanded, "rb"); - if (! fd) { - result = GetLastError(); - break; - } + result = fs_fopen(filePath, "rb", &fd); + if (result == ERROR_SUCCESS) { + SHA1_Init(&context); - while((ret = fread(buff, 1, sizeof(buff), fd)) > 0 ) { - SHA1_Update(&context, buff, ret); - } + while ((ret = fread(buff, 1, sizeof(buff), fd)) > 0 ) { + SHA1_Update(&context, buff, ret); + } - fclose(fd); - SHA1_Final(hash, &context); + fclose(fd); + SHA1_Final(hash, &context); - // One byte extra for the NULL - packet_add_tlv_raw(response, TLV_TYPE_FILE_NAME, hash, 21); - } while(0); + packet_add_tlv_raw(response, TLV_TYPE_FILE_NAME, hash, sizeof(hash)); } - // Set the result and transmit the response packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - if (expanded) - free(expanded); - - return ERROR_SUCCESS; + return packet_transmit(remote, response, NULL); } - - /* * Copies source file path to destination * @@ -495,25 +396,19 @@ DWORD request_fs_file_move(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; - LPCSTR oldpath; - LPCSTR newpath; + char *oldpath; + char *newpath; oldpath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_NAME); newpath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); - if (!oldpath) + if (!oldpath) { result = ERROR_INVALID_PARAMETER; -#ifdef _WIN32 - else if (!MoveFile(oldpath,newpath)) -#else - else if (rename(oldpath,newpath)) -#endif - result = GetLastError(); + } else { + result = fs_move(oldpath, newpath); + } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); - - packet_transmit(remote, response, NULL); - - return ERROR_SUCCESS; + return packet_transmit(remote, response, NULL); } diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs.h b/c/meterpreter/source/extensions/stdapi/server/fs/fs.h index 517fc961..3d1ccf4a 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs.h +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs.h @@ -1,6 +1,8 @@ #ifndef _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_FS_FS_H #define _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_FS_FS_H +#include <stdint.h> + LPSTR fs_expand_path(LPCSTR regular); /* @@ -25,33 +27,4 @@ DWORD request_fs_file_move(Remote *remote, Packet *packet); */ DWORD request_fs_file_channel_open(Remote *remote, Packet *packet); - - -/* - * Stat structures on Windows and various Unixes are all slightly different. - * Use this as a means of standardization so the client has some hope of - * understanding what the stat'd file really is. - */ -struct meterp_stat { - unsigned int st_dev; - unsigned short st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short pad; - unsigned int st_rdev; - unsigned int st_size; - /* - * These are always 64-bits on Windows and usually 32-bits on Linux. Force - * them to be the same size everywhere. - */ - unsigned long long st_atime; - unsigned long long st_mtime; - unsigned long long st_ctime; -}; - -int fs_stat(LPCSTR filename, struct meterp_stat *buf); - - #endif diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h new file mode 100644 index 00000000..88b9bc82 --- /dev/null +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h @@ -0,0 +1,70 @@ +#ifndef _FS_LOCAL_H +#define _FS_LOCAL_H + +#include <stdint.h> +#include <stdio.h> + +#ifdef _WIN32 +#define FS_SEPARATOR '\\' +#define FS_SEPARATOR_S "\\" +#define FS_MAX_PATH MAX_PATH +#else +#define FS_SEPARATOR '/' +#define FS_SEPARATOR_S "/" +#define FS_MAX_PATH PATH_MAX +#endif + +/* + * Stat structures on Windows and various Unixes are all slightly different. + * Use this as a means of standardization so the client has some hope of + * understanding what the stat'd file really is. + */ +struct meterp_stat { + uint32_t st_dev; + uint16_t st_ino; + uint16_t st_mode; + uint16_t st_nlink; + uint16_t st_uid; + uint16_t st_gid; + uint16_t pad; + uint32_t st_rdev; + uint32_t st_size; + /* + * These are always 64-bits on Windows and usually 32-bits on Linux. Force + * them to be the same size everywhere. + */ + uint64_t st_atime; + uint64_t st_mtime; + uint64_t st_ctime; +}; + +typedef void (* fs_ls_cb_t)(void *arg, char *name, const char *path); + +int fs_chdir(const char *directory); + +int fs_delete_dir(const char *directory); + +int fs_delete_file(const char *path); + +/* + * Returns an expanded file path that must be freed + */ +char * fs_expand_path(const char *regular); + +int fs_fopen(const char *path, const char *mode, FILE **f); + +int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg); + +int fs_getwd(char *directory, size_t len); + +int fs_mkdir(const char *directory); + +int fs_move(const char *oldpath, const char *newpath); + +/* + * Fills the platform-independent meterp_stat buf with data from the + * platform-dependent stat() + */ +int fs_stat(char *filename, struct meterp_stat *buf); + +#endif diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c new file mode 100644 index 00000000..ee638386 --- /dev/null +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c @@ -0,0 +1,120 @@ +#include <sys/stat.h> +#include <sys/types.h> + +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "precomp.h" +#include "fs_local.h" + +int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) +{ + struct meterp_stat s; + struct dirent *data; + + DIR *ctx = opendir(directory); + if (ctx == NULL) { + return errno; + } + + while ((data = readdir(ctx))) { + cb(arg, data->d_name, directory); + } + + closedir(ctx); + return ERROR_SUCCESS; +} + +int fs_chdir(const char *directory) +{ + if (chdir(directory) == -1) { + return errno; + } + return ERROR_SUCCESS; +} + +int fs_delete_file(const char *path) +{ + if (unlink(path) == -1) { + return errno; + } + return ERROR_SUCCESS; +} + +int fs_delete_dir(const char *directory) +{ + if (rmdir(directory) == -1) { + return errno; + } + return ERROR_SUCCESS; +} + +char * fs_expand_path(const char *regular) +{ + return strdup(regular); +} + +int fs_getwd(char *directory, size_t len) +{ + if (getcwd(directory, len) == NULL) { + return errno; + } + return ERROR_SUCCESS; +} + +int fs_mkdir(const char *directory) +{ + if (mkdir(directory, 0777) == -1) { + return errno; + } + return ERROR_SUCCESS; +} + +int fs_fopen(const char *path, const char *mode, FILE **f) +{ + int rc = 0; + + if (path == NULL || f == NULL) { + return ERROR_INVALID_PARAMETER; + } + + *f = fopen(path, mode); + if (*f == NULL) { + rc = errno; + } + + return rc; +} + +int fs_move(const char *oldpath, const char *newpath) +{ + if (rename(oldpath, newpath) == -1) { + return errno; + } + return ERROR_SUCCESS; +} + +int fs_stat(char *filename, struct meterp_stat *buf) +{ + struct stat sbuf; + + if (stat(filename, &sbuf) == -1) { + return errno; + } + + buf->st_dev = sbuf.st_dev; + buf->st_ino = sbuf.st_ino; + buf->st_mode = sbuf.st_mode; + buf->st_nlink = sbuf.st_nlink; + buf->st_uid = sbuf.st_uid; + buf->st_gid = sbuf.st_gid; + buf->st_rdev = sbuf.st_rdev; + buf->st_size = sbuf.st_size; + buf->st_atime = sbuf.st_atime; + buf->st_mtime = sbuf.st_mtime; + buf->st_ctime = sbuf.st_ctime; + + return ERROR_SUCCESS; +} diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_util.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_util.c deleted file mode 100644 index 588298b5..00000000 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_util.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "precomp.h" -#include "fs.h" -#include <sys/stat.h> - -/* - * Returns an expanded file path that must be freed - */ -LPSTR fs_expand_path(LPCSTR regular) -{ -#ifdef _WIN32 - DWORD expandedFilePathSize = 32768; - LPSTR expandedFilePath = NULL; - - do - { - // Expand the file path - if (!(expandedFilePath = (LPSTR)malloc(expandedFilePathSize))) - break; - - // Expand the file path being accessed - if (!ExpandEnvironmentStrings(regular, expandedFilePath, - expandedFilePathSize - 1)) - { - free(expandedFilePath); - - expandedFilePath = 0; - - break; - } - - expandedFilePath[expandedFilePathSize - 1] = 0; - - } while (0); - - return expandedFilePath; -#else /* Hack to make it work with existing code under *nix */ - char *expandedFilePath; - DWORD expandedFilePathSize = strlen(regular)+1; - expandedFilePath = malloc(expandedFilePathSize); - strncpy(expandedFilePath, regular, expandedFilePathSize); - expandedFilePath[expandedFilePathSize - 1] = '\0'; - return expandedFilePath; -#endif -} - -/* - * Fills the platform-independent meterp_stat buf with data from the platform-dependent stat() - */ -int fs_stat(LPCSTR filename, struct meterp_stat *buf) { - struct stat sbuf; - int ret; - - ret = stat(filename, &sbuf); - - if (ret == 0) { - buf->st_dev = sbuf.st_dev; - buf->st_ino = sbuf.st_ino; - buf->st_mode = sbuf.st_mode; - buf->st_nlink = sbuf.st_nlink; - buf->st_uid = sbuf.st_uid; - buf->st_gid = sbuf.st_gid; - buf->st_rdev = sbuf.st_rdev; - buf->st_size = sbuf.st_size; - buf->st_atime = (unsigned long long)sbuf.st_atime; - buf->st_mtime = (unsigned long long)sbuf.st_mtime; - buf->st_ctime = (unsigned long long)sbuf.st_ctime; - return 0; - } else { -#ifdef _WIN32 - return GetLastError(); -#else - return errno; -#endif - } -} diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c new file mode 100644 index 00000000..2a1c89d2 --- /dev/null +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c @@ -0,0 +1,197 @@ +#include <sys/stat.h> + +#include "fs_local.h" +#include "precomp.h" + +char * fs_expand_path(const char *regular) +{ + DWORD expandedFilePathSize = 32768; + LPSTR expandedFilePath; + + /* + * Expand the file path + */ + expandedFilePath = malloc(expandedFilePathSize); + if (expandedFilePath == NULL) { + return NULL; + } + + /* + * Expand the file path being accessed. ExpandEnvironmentStrings + * NULL-terminates the result; + */ + if (ExpandEnvironmentStrings(regular, expandedFilePath, + expandedFilePathSize - 2) == 0) { + free(expandedFilePath); + return NULL; + } + + return expandedFilePath; +} + +int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) +{ + DWORD result = 0; + LPSTR expanded = NULL; + LPSTR baseDirectory = NULL; + char tempDirectory[FS_MAX_PATH]; + WIN32_FIND_DATA data; + HANDLE ctx = NULL; + + _snprintf(tempDirectory, sizeof(tempDirectory), "%s", directory); + + /* + * If there is not wildcard mask on the directory, create a version of the + * directory with a mask appended + */ + if (strrchr(directory, '*') == NULL) { + _snprintf(tempDirectory, sizeof(tempDirectory), "%s\\*", + directory); + + baseDirectory = _strdup(directory); + if (baseDirectory == NULL) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + } else { + /* + * Otherwise, if it does have an asterisk, we need to scan back + * and find the base directory. If there is no slash, it means + * we're listing the cwd. + */ + PCHAR slash = strrchr(directory, '\\'); + if (slash) { + *slash = 0; + baseDirectory = _strdup(directory); + if (baseDirectory == NULL) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + *slash = '\\'; + } + } + + expanded = fs_expand_path(tempDirectory); + if (expanded == NULL) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + /* + * Start the find operation + */ + ctx = FindFirstFile(expanded, &data); + do { + if (ctx == INVALID_HANDLE_VALUE) { + result = GetLastError(); + break; + } + + cb(arg, data.cFileName, baseDirectory); + + } while (FindNextFile(ctx, &data)); + + /* + * Clean up resources + */ + FindClose(ctx); + free(expanded); +out: + free(tempDirectory); + free(baseDirectory); + return result; +} + +int fs_chdir(const char *directory) +{ + if (SetCurrentDirectory(directory) == 0) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +int fs_delete_dir(const char *directory) +{ + if (RemoveDirectory(directory) == 0) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +int fs_delete_file(const char *path) +{ + if (DeleteFile(path) == 0) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +int fs_getwd(char *directory, size_t len) +{ + if (GetCurrentDirectory(len, directory) == 0) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +int fs_move(const char *oldpath, const char *newpath) +{ + if (MoveFile(oldpath, newpath) == 0) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +int fs_mkdir(const char *directory) +{ + if (CreateDirectory(directory, NULL) == 0) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +int fs_fopen(const char *path, const char *mode, FILE **f) +{ + char *expanded = NULL; + int rc = ERROR_SUCCESS; + + if (path == NULL || f == NULL) { + return ERROR_INVALID_PARAMETER; + } + + if ((expanded = fs_expand_path(path)) == NULL) { + return ERROR_NOT_ENOUGH_MEMORY; + } + + *f = fopen(expanded, mode); + if (*f == NULL) { + rc = GetLastError(); + } + + free(expanded); + return rc; +} + +int fs_stat(char *filename, struct meterp_stat *buf) +{ + struct stat sbuf; + + if (stat(filename, &sbuf) == -1) { + return GetLastError(); + } + + buf->st_dev = sbuf.st_dev; + buf->st_ino = sbuf.st_ino; + buf->st_mode = sbuf.st_mode; + buf->st_nlink = sbuf.st_nlink; + buf->st_uid = sbuf.st_uid; + buf->st_gid = sbuf.st_gid; + buf->st_rdev = sbuf.st_rdev; + buf->st_size = sbuf.st_size; + buf->st_atime = sbuf.st_atime; + buf->st_mtime = sbuf.st_mtime; + buf->st_ctime = sbuf.st_ctime; + + return ERROR_SUCCESS; +} diff --git a/c/meterpreter/workspace/ext_server_stdapi/Makefile b/c/meterpreter/workspace/ext_server_stdapi/Makefile index 8c59c9de..9e18aa12 100644 --- a/c/meterpreter/workspace/ext_server_stdapi/Makefile +++ b/c/meterpreter/workspace/ext_server_stdapi/Makefile @@ -9,7 +9,7 @@ CFLAGS+= -I../../source/extensions/stdapi/server objects = \ server/fs/dir.o \ server/fs/file.o \ - server/fs/fs_util.o \ + server/fs/fs_posix.o \ server/general.o \ server/net/config/interface.o \ server/net/config/route.o \ diff --git a/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj b/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj index 532fbe4f..c1788014 100644 --- a/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj +++ b/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj @@ -629,7 +629,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho </ClCompile> <ClCompile Include="..\..\source\extensions\stdapi\server\fs\dir.c" /> <ClCompile Include="..\..\source\extensions\stdapi\server\fs\file.c" /> - <ClCompile Include="..\..\source\extensions\stdapi\server\fs\fs_util.c" /> + <ClCompile Include="..\..\source\extensions\stdapi\server\fs\fs_win.c" /> <ClCompile Include="..\..\source\extensions\stdapi\server\fs\search.c" /> <ClCompile Include="..\..\source\extensions\stdapi\server\net\net.c" /> <ClCompile Include="..\..\source\extensions\stdapi\server\net\config\interface.c" /> @@ -683,6 +683,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho <ClInclude Include="..\..\source\extensions\stdapi\server\webcam\bmp2jpeg.h" /> <ClInclude Include="..\..\source\extensions\stdapi\server\webcam\webcam.h" /> <ClInclude Include="..\..\source\extensions\stdapi\server\fs\fs.h" /> + <ClInclude Include="..\..\source\extensions\stdapi\server\fs\fs_local.h" /> <ClInclude Include="..\..\source\extensions\stdapi\server\fs\search.h" /> </ItemGroup> <ItemGroup> @@ -710,4 +711,4 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho <ImportGroup Label="ExtensionTargets"> <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" /> </ImportGroup> -</Project> \ No newline at end of file +</Project> From 7c8b723c15b1ef48c79b1025411718bf3d73e171 Mon Sep 17 00:00:00 2001 From: Brent Cook <bcook@rapid7.com> Date: Tue, 17 Mar 2015 09:05:35 -0500 Subject: [PATCH 2/7] convert windows filesystem operations to unicode --- .../extensions/stdapi/server/fs/fs_local.h | 2 +- .../extensions/stdapi/server/fs/fs_win.c | 226 ++++++++++++++---- 2 files changed, 183 insertions(+), 45 deletions(-) diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h index 88b9bc82..467da074 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h @@ -7,7 +7,7 @@ #ifdef _WIN32 #define FS_SEPARATOR '\\' #define FS_SEPARATOR_S "\\" -#define FS_MAX_PATH MAX_PATH +#define FS_MAX_PATH 32768 #else #define FS_SEPARATOR '/' #define FS_SEPARATOR_S "/" diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c index 2a1c89d2..45b40705 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c @@ -3,30 +3,66 @@ #include "fs_local.h" #include "precomp.h" +static wchar_t *utf8_to_wchar(const char *in) +{ + wchar_t *out; + int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, in, -1, NULL, 0); + if (len <= 0) { + return NULL; + } + + out = calloc(len, sizeof(wchar_t)); + if (out == NULL) { + return NULL; + } + + if (MultiByteToWideChar(CP_UTF8, 0, in, -1, out, len) == 0) { + free(out); + out = NULL; + } + + return out; +} + +static char *wchar_to_utf8(const wchar_t *in) +{ + char *out; + int len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); + if (len <= 0) { + return NULL; + } + + out = calloc(len, sizeof(char)); + if (out == NULL) { + return NULL; + } + + if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out, len, NULL, FALSE) == 0) { + free(out); + out = NULL; + } + + return out; +} + char * fs_expand_path(const char *regular) { - DWORD expandedFilePathSize = 32768; - LPSTR expandedFilePath; + wchar_t expanded_path[FS_MAX_PATH]; + wchar_t *regular_w; - /* - * Expand the file path - */ - expandedFilePath = malloc(expandedFilePathSize); - if (expandedFilePath == NULL) { + regular_w = utf8_to_wchar(regular); + if (regular_w == NULL) { return NULL; } - /* - * Expand the file path being accessed. ExpandEnvironmentStrings - * NULL-terminates the result; - */ - if (ExpandEnvironmentStrings(regular, expandedFilePath, - expandedFilePathSize - 2) == 0) { - free(expandedFilePath); + if (ExpandEnvironmentStringsW(regular_w, expanded_path, FS_MAX_PATH) == 0) { + free(regular_w); return NULL; } - return expandedFilePath; + free(regular_w); + + return wchar_to_utf8(expanded_path); } int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) @@ -35,8 +71,6 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) LPSTR expanded = NULL; LPSTR baseDirectory = NULL; char tempDirectory[FS_MAX_PATH]; - WIN32_FIND_DATA data; - HANDLE ctx = NULL; _snprintf(tempDirectory, sizeof(tempDirectory), "%s", directory); @@ -78,19 +112,25 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) goto out; } - /* - * Start the find operation - */ - ctx = FindFirstFile(expanded, &data); + WIN32_FIND_DATAW data; + wchar_t *path_w = utf8_to_wchar(expanded); + if (path_w == NULL) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + HANDLE ctx = FindFirstFileW(path_w, &data); do { if (ctx == INVALID_HANDLE_VALUE) { result = GetLastError(); break; } - cb(arg, data.cFileName, baseDirectory); + char *filename = wchar_to_utf8(data.cFileName); + cb(arg, filename, baseDirectory); + free(filename); - } while (FindNextFile(ctx, &data)); + } while (FindNextFileW(ctx, &data)); /* * Clean up resources @@ -100,55 +140,135 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) out: free(tempDirectory); free(baseDirectory); + free(path_w); return result; } int fs_chdir(const char *directory) { - if (SetCurrentDirectory(directory) == 0) { - return GetLastError(); + int rc = ERROR_SUCCESS; + wchar_t *dir_w = utf8_to_wchar(directory); + + if (dir_w == NULL) { + rc = GetLastError(); + goto out; } - return ERROR_SUCCESS; + + if (SetCurrentDirectoryW(dir_w) == 0) { + rc = GetLastError(); + } + +out: + free(dir_w); + return rc; } int fs_delete_dir(const char *directory) { - if (RemoveDirectory(directory) == 0) { - return GetLastError(); + int rc = ERROR_SUCCESS; + wchar_t *dir_w = utf8_to_wchar(directory); + + if (dir_w == NULL) { + rc = GetLastError(); + goto out; } - return ERROR_SUCCESS; + + if (RemoveDirectoryW(dir_w) == 0) { + rc = GetLastError(); + } + +out: + free(dir_w); + return rc; } int fs_delete_file(const char *path) { - if (DeleteFile(path) == 0) { - return GetLastError(); + int rc = ERROR_SUCCESS; + wchar_t *path_w = utf8_to_wchar(path); + + if (path_w == NULL) { + rc = GetLastError(); + goto out; } - return ERROR_SUCCESS; + + if (DeleteFileW(path_w) == 0) { + rc = GetLastError(); + } + +out: + free(path_w); + return rc; } int fs_getwd(char *directory, size_t len) { - if (GetCurrentDirectory(len, directory) == 0) { - return GetLastError(); + int rc = ERROR_SUCCESS; + wchar_t *dir_w; + char *dir; + + dir_w = calloc(len, sizeof(wchar_t)); + if (dir_w == NULL) { + rc = GetLastError(); + goto out; } - return ERROR_SUCCESS; + + if (GetCurrentDirectoryW(len, dir_w) == 0) { + rc = GetLastError(); + goto out; + } + + dir = wchar_to_utf8(dir_w); + if (dir == NULL) { + rc = GetLastError(); + goto out; + } + strncpy(directory, dir, len); + +out: + free(dir); + free(dir_w); + return rc; } int fs_move(const char *oldpath, const char *newpath) { - if (MoveFile(oldpath, newpath) == 0) { - return GetLastError(); + int rc = ERROR_SUCCESS; + wchar_t *old_w = utf8_to_wchar(oldpath); + wchar_t *new_w = utf8_to_wchar(newpath); + + if ((old_w == NULL) || (new_w == NULL)) { + rc = GetLastError(); + goto out; } - return ERROR_SUCCESS; + + if (MoveFileW(old_w, new_w) == 0) { + rc = GetLastError(); + } + +out: + free(old_w); + free(new_w); + return rc; } int fs_mkdir(const char *directory) { - if (CreateDirectory(directory, NULL) == 0) { - return GetLastError(); + int rc = ERROR_SUCCESS; + wchar_t *dir_w = utf8_to_wchar(directory); + + if (dir_w == NULL) { + rc = GetLastError(); + goto out; } - return ERROR_SUCCESS; + + if (CreateDirectoryW(dir_w, NULL) == 0) { + rc = GetLastError(); + } + +out: + free(dir_w); + return rc; } int fs_fopen(const char *path, const char *mode, FILE **f) @@ -164,23 +284,41 @@ int fs_fopen(const char *path, const char *mode, FILE **f) return ERROR_NOT_ENOUGH_MEMORY; } - *f = fopen(expanded, mode); + wchar_t *path_w = utf8_to_wchar(expanded); + wchar_t *mode_w = utf8_to_wchar(mode); + + if ((path_w == NULL) || (mode_w == NULL)) { + rc = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + *f = _wfopen(path_w, mode_w); if (*f == NULL) { rc = GetLastError(); } +out: free(expanded); + free(path_w); + free(mode_w); return rc; } int fs_stat(char *filename, struct meterp_stat *buf) { - struct stat sbuf; + struct _stat64i32 sbuf; - if (stat(filename, &sbuf) == -1) { + wchar_t *filename_w = utf8_to_wchar(filename); + if (filename_w == NULL) { + return -1; + } + + if (_wstat(filename_w, &sbuf) == -1) { return GetLastError(); } + free(filename_w); + buf->st_dev = sbuf.st_dev; buf->st_ino = sbuf.st_ino; buf->st_mode = sbuf.st_mode; From 95e102a90c6bb110b3b53ba2354de542b5405332 Mon Sep 17 00:00:00 2001 From: Brent Cook <bcook@rapid7.com> Date: Tue, 17 Mar 2015 15:27:48 -0500 Subject: [PATCH 3/7] cast size_t on read/write to proper result for the TLV --- c/meterpreter/source/extensions/stdapi/server/fs/file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/file.c b/c/meterpreter/source/extensions/stdapi/server/fs/file.c index b35dd5f5..026c3be1 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/file.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/file.c @@ -25,7 +25,7 @@ static DWORD file_channel_write(Channel *channel, Packet *request, { FileContext *ctx = (FileContext *)context; DWORD result= ERROR_SUCCESS; - DWORD written = 0; + size_t written = 0; // Write a chunk if (bufferSize) { @@ -36,7 +36,7 @@ static DWORD file_channel_write(Channel *channel, Packet *request, } if (bytesWritten) { - *bytesWritten = written; + *bytesWritten = (DWORD)written; } return result; @@ -65,7 +65,7 @@ static DWORD file_channel_read(Channel *channel, Packet *request, { FileContext *ctx = (FileContext *)context; DWORD result = ERROR_SUCCESS; - DWORD bytes = 0; + size_t bytes = 0; // Read a chunk if (bufferSize) { @@ -76,7 +76,7 @@ static DWORD file_channel_read(Channel *channel, Packet *request, } if (bytesRead) { - *bytesRead = bytes; + *bytesRead = (DWORD)bytes; } return ERROR_SUCCESS; From 8944ca5156c8a93d9f7f7d8f323900b45d5835c0 Mon Sep 17 00:00:00 2001 From: Brent Cook <bcook@rapid7.com> Date: Tue, 17 Mar 2015 15:28:27 -0500 Subject: [PATCH 4/7] modify fs_getwd so it allocates the path --- .../source/extensions/stdapi/server/fs/dir.c | 16 +++++++------- .../extensions/stdapi/server/fs/fs_local.h | 2 +- .../extensions/stdapi/server/fs/fs_posix.c | 8 ++++--- .../extensions/stdapi/server/fs/fs_win.c | 21 +++++-------------- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c index 522dbba5..c671d289 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c @@ -58,12 +58,14 @@ DWORD request_fs_ls(Remote * remote, Packet * packet) DWORD request_fs_getwd(Remote * remote, Packet * packet) { Packet *response = packet_create_response(packet); - char directory[FS_MAX_PATH]; + char *directory = NULL; DWORD result; - result = fs_getwd(directory, sizeof(directory)); - - packet_add_tlv_string(response, TLV_TYPE_DIRECTORY_PATH, directory); + result = fs_getwd(&directory); + if (directory != NULL) { + packet_add_tlv_string(response, TLV_TYPE_DIRECTORY_PATH, directory); + free(directory); + } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return packet_transmit(remote, response, NULL); @@ -78,7 +80,7 @@ DWORD request_fs_getwd(Remote * remote, Packet * packet) DWORD request_fs_chdir(Remote * remote, Packet * packet) { Packet *response = packet_create_response(packet); - LPCSTR directory; + char *directory; DWORD result; directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); @@ -100,7 +102,7 @@ DWORD request_fs_chdir(Remote * remote, Packet * packet) DWORD request_fs_mkdir(Remote * remote, Packet * packet) { Packet *response = packet_create_response(packet); - LPCSTR directory; + char *directory; DWORD result; directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); @@ -122,7 +124,7 @@ DWORD request_fs_mkdir(Remote * remote, Packet * packet) DWORD request_fs_delete_dir(Remote * remote, Packet * packet) { Packet *response = packet_create_response(packet); - LPCSTR directory; + char *directory; DWORD result; directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h index 467da074..6e3369eb 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h @@ -55,7 +55,7 @@ int fs_fopen(const char *path, const char *mode, FILE **f); int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg); -int fs_getwd(char *directory, size_t len); +int fs_getwd(char **directory); int fs_mkdir(const char *directory); diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c index ee638386..ccf3e3e1 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c @@ -56,12 +56,14 @@ char * fs_expand_path(const char *regular) return strdup(regular); } -int fs_getwd(char *directory, size_t len) +int fs_getwd(char **directory) { - if (getcwd(directory, len) == NULL) { + char dir[FS_MAX_PATH]; + if (getcwd(dir, sizeof(dir)) == NULL) { return errno; } - return ERROR_SUCCESS; + *directory = strdup(dir); + return *directory == NULL ? ERROR_NOT_ENOUGH_MEMORY : ERROR_SUCCESS; } int fs_mkdir(const char *directory) diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c index 45b40705..18a8ba75 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c @@ -201,33 +201,22 @@ out: return rc; } -int fs_getwd(char *directory, size_t len) +int fs_getwd(char **dir) { int rc = ERROR_SUCCESS; - wchar_t *dir_w; - char *dir; + wchar_t dir_w[FS_MAX_PATH]; - dir_w = calloc(len, sizeof(wchar_t)); - if (dir_w == NULL) { + if (GetCurrentDirectoryW(FS_MAX_PATH, dir_w) == 0) { rc = GetLastError(); goto out; } - if (GetCurrentDirectoryW(len, dir_w) == 0) { + *dir = wchar_to_utf8(dir_w); + if (*dir == NULL) { rc = GetLastError(); - goto out; } - dir = wchar_to_utf8(dir_w); - if (dir == NULL) { - rc = GetLastError(); - goto out; - } - strncpy(directory, dir, len); - out: - free(dir); - free(dir_w); return rc; } From e8318f8c5b0d52dd23a0fbfffca795624675037f Mon Sep 17 00:00:00 2001 From: Brent Cook <bcook@rapid7.com> Date: Wed, 18 Mar 2015 02:11:13 -0500 Subject: [PATCH 5/7] correct various issues listing files * bury common _snprintf that snuck in while testing the Windows version back into the platform-specific code. * remove now-unneeded separator defines * don't free a stack variable on windows --- .../source/extensions/stdapi/server/fs/dir.c | 12 +---------- .../source/extensions/stdapi/server/fs/file.c | 2 +- .../extensions/stdapi/server/fs/fs_local.h | 12 +++++------ .../extensions/stdapi/server/fs/fs_posix.c | 6 +++++- .../extensions/stdapi/server/fs/fs_win.c | 21 ++++++++++++++++--- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c index c671d289..e2718fc9 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c @@ -2,21 +2,11 @@ #include "fs_local.h" -void request_fs_ls_cb(void *arg, char *name, const char *dir) +void request_fs_ls_cb(void *arg, char *name, char *path) { Packet *response = arg; - char path[FS_MAX_PATH]; struct meterp_stat s; - /* - * Build the full path if we have a base directory - */ - if (dir) { - _snprintf(path, sizeof(path), "%s%c%s", dir, FS_SEPARATOR, name); - } else { - _snprintf(path, sizeof(path), "%s", name); - } - /* * Add the file name, full path and stat information */ diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/file.c b/c/meterpreter/source/extensions/stdapi/server/fs/file.c index 026c3be1..a3b7e256 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/file.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/file.c @@ -207,7 +207,7 @@ DWORD request_fs_separator(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); - packet_add_tlv_string(response, TLV_TYPE_STRING, FS_SEPARATOR_S); + packet_add_tlv_string(response, TLV_TYPE_STRING, FS_SEPARATOR); packet_add_tlv_uint(response, TLV_TYPE_RESULT, ERROR_SUCCESS); diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h index 6e3369eb..e66a7876 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h @@ -5,13 +5,11 @@ #include <stdio.h> #ifdef _WIN32 -#define FS_SEPARATOR '\\' -#define FS_SEPARATOR_S "\\" -#define FS_MAX_PATH 32768 +#define FS_SEPARATOR "\\" +#define FS_MAX_PATH 32768 #else -#define FS_SEPARATOR '/' -#define FS_SEPARATOR_S "/" -#define FS_MAX_PATH PATH_MAX +#define FS_SEPARATOR "/" +#define FS_MAX_PATH PATH_MAX #endif /* @@ -38,7 +36,7 @@ struct meterp_stat { uint64_t st_ctime; }; -typedef void (* fs_ls_cb_t)(void *arg, char *name, const char *path); +typedef void (*fs_ls_cb_t)(void *arg, char *name, char *path); int fs_chdir(const char *directory); diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c index ccf3e3e1..c6064677 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c @@ -13,6 +13,7 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) { struct meterp_stat s; struct dirent *data; + char path[FS_MAX_PATH]; DIR *ctx = opendir(directory); if (ctx == NULL) { @@ -20,7 +21,10 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) } while ((data = readdir(ctx))) { - cb(arg, data->d_name, directory); + + snprintf(path, sizeof(path), "%s/%s", directory, data->d_name); + + cb(arg, data->d_name, path); } closedir(ctx); diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c index 18a8ba75..10fe1733 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c @@ -115,19 +115,35 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) WIN32_FIND_DATAW data; wchar_t *path_w = utf8_to_wchar(expanded); if (path_w == NULL) { - result = ERROR_NOT_ENOUGH_MEMORY; + result = GetLastError(); goto out; } HANDLE ctx = FindFirstFileW(path_w, &data); + if (ctx == NULL) { + result = GetLastError(); + goto out; + } + do { if (ctx == INVALID_HANDLE_VALUE) { result = GetLastError(); break; } + /* + * Build the full path if we have a base directory + */ char *filename = wchar_to_utf8(data.cFileName); - cb(arg, filename, baseDirectory); + char path[FS_MAX_PATH]; + + if (baseDirectory) { + _snprintf(path, sizeof(path), "%s\\%s", filename, baseDirectory); + } else { + _snprintf(path, sizeof(path), "%s", data.cFileName); + } + + cb(arg, filename, path); free(filename); } while (FindNextFileW(ctx, &data)); @@ -138,7 +154,6 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) FindClose(ctx); free(expanded); out: - free(tempDirectory); free(baseDirectory); free(path_w); return result; From eba69cf20e8e24eb0de7ecdc41b03a5be44ea9eb Mon Sep 17 00:00:00 2001 From: Brent Cook <bcook@rapid7.com> Date: Wed, 18 Mar 2015 16:05:30 -0500 Subject: [PATCH 6/7] add short name support to filesystem listing. Also, fix path builder order for stat. --- .../source/extensions/stdapi/server/fs/dir.c | 5 ++++- .../source/extensions/stdapi/server/fs/fs_local.h | 2 +- .../source/extensions/stdapi/server/fs/fs_posix.c | 2 +- .../source/extensions/stdapi/server/fs/fs_win.c | 12 ++++++------ c/meterpreter/source/extensions/stdapi/stdapi.h | 1 + 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c index e2718fc9..cf3b1662 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/dir.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/dir.c @@ -2,7 +2,7 @@ #include "fs_local.h" -void request_fs_ls_cb(void *arg, char *name, char *path) +void request_fs_ls_cb(void *arg, char *name, char *short_name, char *path) { Packet *response = arg; struct meterp_stat s; @@ -12,6 +12,9 @@ void request_fs_ls_cb(void *arg, char *name, char *path) */ packet_add_tlv_string(response, TLV_TYPE_FILE_NAME, name); packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, path); + if (short_name) { + packet_add_tlv_string(response, TLV_TYPE_FILE_SHORT_NAME, short_name); + } if (fs_stat(path, &s) >= 0) { packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &s, sizeof(s)); } diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h index e66a7876..4a6b615b 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_local.h @@ -36,7 +36,7 @@ struct meterp_stat { uint64_t st_ctime; }; -typedef void (*fs_ls_cb_t)(void *arg, char *name, char *path); +typedef void (*fs_ls_cb_t)(void *arg, char *name, char *short_name, char *path); int fs_chdir(const char *directory); diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c index c6064677..3fc9054a 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_posix.c @@ -24,7 +24,7 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) snprintf(path, sizeof(path), "%s/%s", directory, data->d_name); - cb(arg, data->d_name, path); + cb(arg, data->d_name, NULL, path); } closedir(ctx); diff --git a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c index 10fe1733..ada29181 100644 --- a/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c +++ b/c/meterpreter/source/extensions/stdapi/server/fs/fs_win.c @@ -131,20 +131,20 @@ int fs_ls(const char *directory, fs_ls_cb_t cb, void *arg) break; } - /* - * Build the full path if we have a base directory - */ char *filename = wchar_to_utf8(data.cFileName); + char *short_filename = wchar_to_utf8(data.cAlternateFileName); char path[FS_MAX_PATH]; if (baseDirectory) { - _snprintf(path, sizeof(path), "%s\\%s", filename, baseDirectory); + _snprintf(path, sizeof(path), "%s\\%s", baseDirectory, filename); } else { - _snprintf(path, sizeof(path), "%s", data.cFileName); + _snprintf(path, sizeof(path), "%s", filename); } - cb(arg, filename, path); + cb(arg, filename, short_filename, path); + free(filename); + free(short_filename); } while (FindNextFileW(ctx, &data)); diff --git a/c/meterpreter/source/extensions/stdapi/stdapi.h b/c/meterpreter/source/extensions/stdapi/stdapi.h index 52e7e36a..45b4f0ae 100644 --- a/c/meterpreter/source/extensions/stdapi/stdapi.h +++ b/c/meterpreter/source/extensions/stdapi/stdapi.h @@ -23,6 +23,7 @@ #define TLV_TYPE_FILE_PATH MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 1202 ) #define TLV_TYPE_FILE_MODE MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 1203 ) #define TLV_TYPE_FILE_SIZE MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 1204 ) +#define TLV_TYPE_FILE_SHORT_NAME MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 1205 ) #define TLV_TYPE_STAT_BUF MAKE_CUSTOM_TLV( TLV_META_TYPE_COMPLEX, TLV_TYPE_EXTENSION_STDAPI, 1220 ) From c93ba9608c4a7dc9657ef6711c203959c7b72408 Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Thu, 19 Mar 2015 10:58:01 +1000 Subject: [PATCH 7/7] Fix bad POSIX support for stageless meterpreter * Make sure POSIX has the new extension command enumeration function. * Add support for deinit of extensions. * Make sure extensions are tracked like they in Windows. * Fix up a few export definitions. * Stop using strncpy_s in POSIX code. --- .../source/extensions/networkpug/networkpug.c | 30 +++++++++- .../source/extensions/sniffer/sniffer.c | 18 +++++- .../source/extensions/stdapi/server/stdapi.c | 8 +++ .../source/server/posix/remote_dispatch.c | 31 ++++++++-- .../source/server/remote_dispatch_common.c | 57 ++++++++++++++++++- .../source/server/win/remote_dispatch.c | 50 +--------------- 6 files changed, 139 insertions(+), 55 deletions(-) diff --git a/c/meterpreter/source/extensions/networkpug/networkpug.c b/c/meterpreter/source/extensions/networkpug/networkpug.c index 2bdf6b16..cbea7e45 100644 --- a/c/meterpreter/source/extensions/networkpug/networkpug.c +++ b/c/meterpreter/source/extensions/networkpug/networkpug.c @@ -76,7 +76,7 @@ void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *byt } np->packet_stream = tmp; - size = (unsigned short int *)(np->packet_stream + np->packet_stream_length); + size = (unsigned short int *)(np->packet_stream + np->packet_stream_length); *size = htons(h->caplen); data = (unsigned char *)(np->packet_stream + np->packet_stream_length + 2); @@ -438,7 +438,11 @@ Command customCommands[] = /* * Initialize the server extension */ +#ifdef _WIN32 DWORD __declspec(dllexport) InitServerExtension(Remote *remote) +#else +DWORD InitServerExtension(Remote *remote) +#endif { int peername_len; struct sockaddr peername; @@ -497,7 +501,11 @@ DWORD __declspec(dllexport) InitServerExtension(Remote *remote) /* * Deinitialize the server extension */ +#ifdef _WIN32 DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote) +#else +DWORD DeinitServerExtension(Remote *remote) +#endif { command_deregister_all(customCommands); @@ -507,3 +515,23 @@ DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote) return ERROR_SUCCESS; } + +/*! + * @brief Get the name of the extension. + * @param buffer Pointer to the buffer to write the name to. + * @param bufferSize Size of the \c buffer parameter. + * @return Indication of success or failure. + */ +#ifdef _WIN32 +DWORD __declspec(dllexport) GetExtensionName(char* buffer, int bufferSize) +#else +DWORD GetExtensionName(char* buffer, int bufferSize) +#endif +{ +#ifdef _WIN32 + strncpy_s(buffer, bufferSize, "networkpug", bufferSize - 1); +#else + strncpy(buffer, "networkpug", bufferSize - 1); +#endif + return ERROR_SUCCESS; +} diff --git a/c/meterpreter/source/extensions/sniffer/sniffer.c b/c/meterpreter/source/extensions/sniffer/sniffer.c index a4430a3d..efa06096 100644 --- a/c/meterpreter/source/extensions/sniffer/sniffer.c +++ b/c/meterpreter/source/extensions/sniffer/sniffer.c @@ -1197,7 +1197,11 @@ DWORD request_sniffer_capture_dump(Remote *remote, Packet *packet) * @param remote Pointer to the remote instance. * @return Indication of success or failure. */ +#ifdef _WIN32 DWORD __declspec(dllexport) InitServerExtension(Remote *remote) +#else +DWORD InitServerExtension(Remote *remote) +#endif { #ifdef _WIN32 // This handle has to be set before calls to command_register @@ -1271,7 +1275,11 @@ DWORD __declspec(dllexport) InitServerExtension(Remote *remote) * @param remote Pointer to the remote instance. * @return Indication of success or failure. */ +#ifdef _WIN32 DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote) +#else +DWORD DeinitServerExtension(Remote *remote) +#endif { command_register_all( customCommands ); @@ -1294,8 +1302,16 @@ DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote) * @param bufferSize Size of the \c buffer parameter. * @return Indication of success or failure. */ +#ifdef _WIN32 DWORD __declspec(dllexport) GetExtensionName(char* buffer, int bufferSize) +#else +DWORD GetExtensionName(char* buffer, int bufferSize) +#endif { +#ifdef _WIN32 strncpy_s(buffer, bufferSize, "sniffer", bufferSize - 1); +#else + strncpy(buffer, "sniffer", bufferSize - 1); +#endif return ERROR_SUCCESS; -} \ No newline at end of file +} diff --git a/c/meterpreter/source/extensions/stdapi/server/stdapi.c b/c/meterpreter/source/extensions/stdapi/server/stdapi.c index 0e1dde40..6ffcd3fc 100644 --- a/c/meterpreter/source/extensions/stdapi/server/stdapi.c +++ b/c/meterpreter/source/extensions/stdapi/server/stdapi.c @@ -216,8 +216,16 @@ DWORD DeinitServerExtension(Remote *remote) * @param bufferSize Size of the \c buffer parameter. * @return Indication of success or failure. */ +#ifdef _WIN32 DWORD __declspec(dllexport) GetExtensionName(char* buffer, int bufferSize) +#else +DWORD GetExtensionName(char* buffer, int bufferSize) +#endif { +#ifdef _WIN32 strncpy_s(buffer, bufferSize, "stdapi", bufferSize - 1); +#else + strncpy(buffer, "stdapi", bufferSize - 1); +#endif return ERROR_SUCCESS; } diff --git a/c/meterpreter/source/server/posix/remote_dispatch.c b/c/meterpreter/source/server/posix/remote_dispatch.c index 6873c635..c004cf7a 100644 --- a/c/meterpreter/source/server/posix/remote_dispatch.c +++ b/c/meterpreter/source/server/posix/remote_dispatch.c @@ -2,6 +2,7 @@ #include "metsrv.h" extern Command *extensionCommands; +extern PLIST gExtensionList; DWORD request_core_loadlib(Remote *remote, Packet *packet) { @@ -61,15 +62,37 @@ DWORD request_core_loadlib(Remote *remote, Packet *packet) // call its Init routine if (flags & LOAD_LIBRARY_FLAG_EXTENSION) { - DWORD(*init)(Remote *remote); + PEXTENSION pExtension = (PEXTENSION)malloc(sizeof(EXTENSION)); + if (!pExtension) + { + res = ERROR_NOT_ENOUGH_MEMORY; + break; + } + //DWORD(*init)(Remote *remote); + + pExtension->init = dlsym(library, "InitServerExtension"); - init = dlsym(library, "InitServerExtension"); // Call the init routine in the library - if (init) + if (pExtension->init) { dprintf("calling InitServerExtension"); - res = init(remote); + res = pExtension->init(remote); + pExtension->getname = dlsym(library, "GetExtensionName"); + + + pExtension->deinit = dlsym(library, "DeinitServerExtension"); + + if (pExtension->getname) + { + pExtension->getname(pExtension->name, sizeof(pExtension->name)); + } + list_push(gExtensionList, pExtension); } + else + { + free(pExtension); + } + if (response) { for (command = extensionCommands; command != first; command = command->next) diff --git a/c/meterpreter/source/server/remote_dispatch_common.c b/c/meterpreter/source/server/remote_dispatch_common.c index a044e886..f8e9c1fa 100644 --- a/c/meterpreter/source/server/remote_dispatch_common.c +++ b/c/meterpreter/source/server/remote_dispatch_common.c @@ -7,6 +7,8 @@ extern HINSTANCE hAppInstance; PLIST gExtensionList = NULL; +DWORD request_core_enumextcmd(Remote* pRemote, Packet* pPacket); + // Dispatch table Command customCommands[] = { @@ -15,6 +17,56 @@ Command customCommands[] = COMMAND_TERMINATOR }; +typedef struct _EnumExtensions +{ + Packet* pResponse; + char* lpExtensionName; +} EnumExtensions, * PEnumExtensions; + +BOOL ext_cmd_callback(LPVOID pState, LPVOID pData) +{ + PEnumExtensions pEnum = (PEnumExtensions)pState; + Command* command = NULL; + + if (pEnum != NULL && pEnum->pResponse != NULL && pData != NULL) + { + PEXTENSION pExt = (PEXTENSION)pData; + if (pExt->name[0] != '\0' && pEnum->lpExtensionName != NULL && strcmp(pExt->name, pEnum->lpExtensionName) == 0) + { + dprintf("[LISTEXT] Found extension: %s", pExt->name); + for (command = pExt->start; command != pExt->end; command = command->next) + { + packet_add_tlv_string(pEnum->pResponse, TLV_TYPE_STRING, command->method); + } + + return TRUE; + } + } + return FALSE; +} + +DWORD request_core_enumextcmd(Remote* pRemote, Packet* pPacket) +{ + BOOL bResult = FALSE; + Packet* pResponse = packet_create_response(pPacket); + + if (pResponse != NULL) + { + EnumExtensions enumExt; + enumExt.pResponse = pResponse; + enumExt.lpExtensionName = packet_get_tlv_value_string(pPacket, TLV_TYPE_STRING); + + dprintf("[LISTEXTCMD] Listing extension commands for %s ...", enumExt.lpExtensionName); + // Start by enumerating the names of the extensions + bResult = list_enumerate(gExtensionList, ext_cmd_callback, &enumExt); + + packet_add_tlv_uint(pResponse, TLV_TYPE_RESULT, ERROR_SUCCESS); + packet_transmit(pRemote, pResponse, NULL); + } + + return ERROR_SUCCESS; +} + /* * Registers custom command handlers */ @@ -38,7 +90,10 @@ VOID deregister_dispatch_routines(Remote * remote) break; } - extension->deinit(remote); + if (extension->deinit) + { + extension->deinit(remote); + } free(extension); } diff --git a/c/meterpreter/source/server/win/remote_dispatch.c b/c/meterpreter/source/server/win/remote_dispatch.c index 61efea1d..96e937fd 100644 --- a/c/meterpreter/source/server/win/remote_dispatch.c +++ b/c/meterpreter/source/server/win/remote_dispatch.c @@ -8,54 +8,6 @@ extern PLIST gExtensionList; // see common/base.c extern Command *extensionCommands; -typedef struct _EnumExtensions -{ - Packet* pResponse; - char* lpExtensionName; -} EnumExtensions, * PEnumExtensions; - -BOOL ext_cmd_callback(LPVOID pState, LPVOID pData) -{ - PEnumExtensions pEnum = (PEnumExtensions)pState; - - if (pEnum != NULL && pEnum->pResponse != NULL && pData != NULL) - { - PEXTENSION pExt = (PEXTENSION)pData; - if (pExt->name[0] != '\0' && pEnum->lpExtensionName != NULL && strcmp(pExt->name, pEnum->lpExtensionName) == 0) - { - dprintf("[LISTEXT] Found extension: %s", pExt->name); - for (Command* command = pExt->start; command != pExt->end; command = command->next) - { - packet_add_tlv_string(pEnum->pResponse, TLV_TYPE_STRING, command->method); - } - - return TRUE; - } - } - return FALSE; -} - -DWORD request_core_enumextcmd(Remote* pRemote, Packet* pPacket) -{ - Packet* pResponse = packet_create_response(pPacket); - - if (pResponse != NULL) - { - EnumExtensions enumExt; - enumExt.pResponse = pResponse; - enumExt.lpExtensionName = packet_get_tlv_value_string(pPacket, TLV_TYPE_STRING); - - dprintf("[LISTEXTCMD] Listing extension commands for %s ...", enumExt.lpExtensionName); - // Start by enumerating the names of the extensions - BOOL bResult = list_enumerate(gExtensionList, ext_cmd_callback, &enumExt); - - packet_add_tlv_uint(pResponse, TLV_TYPE_RESULT, ERROR_SUCCESS); - packet_transmit(pRemote, pResponse, NULL); - } - - return ERROR_SUCCESS; -} - DWORD initialise_extension(HMODULE hLibrary, BOOL bLibLoadedReflectivly, Remote* pRemote, Packet* pResponse, Command* pFirstCommand) { DWORD dwResult = ERROR_OUTOFMEMORY; @@ -129,6 +81,8 @@ DWORD initialise_extension(HMODULE hLibrary, BOOL bLibLoadedReflectivly, Remote* return dwResult; } + + /* * core_loadlib * ------------