mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-05-12 19:04:32 +02:00

The 'common' library has been removed. The only project that actually used it was metsrv, so the code that metsrv required from common is now directly compiled in as part of that project. The common folder now contains files that are importanta cross all of the projects, with a primary focus on the new "API" style function. What this means is that MetSrv has an API that it exposes through a function pointer that is passed to the extension when it's initialised. This pointer references a structure with all the API functions wired in. This means that: * Extensions don't need to know anything about metsrv at compile time. * The delay loading code can be removed, which was one of the last instances of "metsrv.dll" as a string. * Metsrv.dll no longer exports any functions. More to come.
470 lines
11 KiB
C
470 lines
11 KiB
C
#include "precomp.h"
|
|
#include "fs_local.h"
|
|
#include "common_metapi.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
/***************************
|
|
* File Channel Operations *
|
|
***************************/
|
|
|
|
typedef struct
|
|
{
|
|
FILE *fd;
|
|
DWORD mode;
|
|
} FileContext;
|
|
|
|
/*
|
|
* Writes the supplied data to the file
|
|
*/
|
|
static DWORD file_channel_write(Channel *channel, Packet *request,
|
|
LPVOID context, LPVOID buffer, DWORD bufferSize,
|
|
LPDWORD bytesWritten)
|
|
{
|
|
FileContext *ctx = (FileContext *)context;
|
|
DWORD result= ERROR_SUCCESS;
|
|
size_t written = 0;
|
|
|
|
// Write a chunk
|
|
if (bufferSize) {
|
|
written = fwrite(buffer, 1, bufferSize, ctx->fd);
|
|
if (written < bufferSize) {
|
|
result = GetLastError();
|
|
}
|
|
}
|
|
|
|
if (bytesWritten) {
|
|
*bytesWritten = (DWORD)written;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Closes the file
|
|
*/
|
|
static DWORD file_channel_close(Channel *channel, Packet *request,
|
|
LPVOID context)
|
|
{
|
|
FileContext *ctx = (FileContext *)context;
|
|
|
|
fclose(ctx->fd);
|
|
free(ctx);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Reads data from the file (if any)
|
|
*/
|
|
static DWORD file_channel_read(Channel *channel, Packet *request,
|
|
LPVOID context, LPVOID buffer, DWORD bufferSize,
|
|
LPDWORD bytesRead)
|
|
{
|
|
FileContext *ctx = (FileContext *)context;
|
|
DWORD result = ERROR_SUCCESS;
|
|
size_t bytes = 0;
|
|
|
|
// Read a chunk
|
|
if (bufferSize) {
|
|
bytes = fread(buffer, 1, bufferSize, ctx->fd);
|
|
if (bytes < bufferSize) {
|
|
result = GetLastError();
|
|
}
|
|
}
|
|
|
|
if (bytesRead) {
|
|
*bytesRead = (DWORD)bytes;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Checks to see if the file pointer is currently at the end of the file
|
|
*/
|
|
static DWORD file_channel_eof(Channel *channel, Packet *request,
|
|
LPVOID context, LPBOOL isEof)
|
|
{
|
|
FileContext *ctx = (FileContext *)context;
|
|
*isEof = feof(ctx->fd) ? TRUE : FALSE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Changes the current file pointer position in the file
|
|
*/
|
|
static DWORD file_channel_seek(Channel *channel, Packet *request,
|
|
LPVOID context, LONG offset, DWORD whence)
|
|
{
|
|
FileContext *ctx = (FileContext *)context;
|
|
|
|
return fseek(ctx->fd, offset, whence);
|
|
}
|
|
|
|
/*
|
|
* Returns the current offset in the file to the requestor
|
|
*/
|
|
static DWORD file_channel_tell(Channel *channel, Packet *request,
|
|
LPVOID context, LPLONG offset)
|
|
{
|
|
FileContext *ctx = (FileContext *)context;
|
|
DWORD result = ERROR_SUCCESS;
|
|
LONG pos = 0;
|
|
|
|
if ((pos = ftell(ctx->fd)) < 0) {
|
|
result = GetLastError();
|
|
}
|
|
|
|
if (offset)
|
|
*offset = pos;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Handles the open request for a file channel and returns a valid channel
|
|
* identifier to the requestor if the file is opened successfully
|
|
*/
|
|
DWORD request_fs_file_channel_open(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = NULL;
|
|
PCHAR filePath, mode;
|
|
DWORD res = ERROR_SUCCESS;
|
|
DWORD flags = 0;
|
|
Channel *newChannel = NULL;
|
|
PoolChannelOps chops = { 0 };
|
|
FileContext *ctx;
|
|
LPSTR expandedFilePath = NULL;
|
|
|
|
// Allocate a response
|
|
response = met_api->packet.create_response(packet);
|
|
|
|
// Get the channel flags
|
|
flags = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_FLAGS);
|
|
|
|
// 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 = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
|
|
mode = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_MODE);
|
|
|
|
if (mode == NULL) {
|
|
mode = "rb";
|
|
}
|
|
|
|
res = fs_fopen(filePath, mode, &ctx->fd);
|
|
if (res != ERROR_SUCCESS) {
|
|
goto out;
|
|
}
|
|
|
|
memset(&chops, 0, sizeof(chops));
|
|
|
|
// 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 = met_api->channel.create_pool(0, flags, &chops)))) {
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// Add the channel identifier to the response
|
|
met_api->packet.add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, met_api->channel.get_id(newChannel));
|
|
|
|
out:
|
|
// Transmit the packet if it's valid
|
|
met_api->packet.transmit_response(res, remote, response);
|
|
|
|
// Clean up on failure
|
|
if (res != ERROR_SUCCESS) {
|
|
if (newChannel) {
|
|
met_api->channel.destroy(newChannel, NULL);
|
|
}
|
|
free(ctx);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Gets the directory separator for this system
|
|
*/
|
|
DWORD request_fs_separator(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
|
|
met_api->packet.add_tlv_string(response, TLV_TYPE_STRING, FS_SEPARATOR);
|
|
|
|
return met_api->packet.transmit_response(ERROR_SUCCESS, remote, response);
|
|
}
|
|
|
|
|
|
/*
|
|
* Gets information about the file path that is supplied and returns it to the
|
|
* requestor
|
|
*
|
|
* req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd
|
|
*/
|
|
DWORD request_fs_stat(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
struct meterp_stat buf;
|
|
char *filePath;
|
|
char *expanded = NULL;
|
|
DWORD result = ERROR_SUCCESS;
|
|
|
|
filePath = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
|
|
|
|
if (!filePath) {
|
|
result = ERROR_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
expanded = fs_expand_path(filePath);
|
|
if (expanded == NULL) {
|
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
result = fs_stat(expanded, &buf);
|
|
if (0 == result) {
|
|
met_api->packet.add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, sizeof(buf));
|
|
}
|
|
|
|
free(expanded);
|
|
|
|
out:
|
|
return met_api->packet.transmit_response(result, remote, response);
|
|
}
|
|
|
|
/*
|
|
* Removes the supplied file from disk
|
|
*
|
|
* req: TLV_TYPE_FILE_PATH - The file that is to be removed.
|
|
*/
|
|
DWORD request_fs_delete_file(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
char *path;
|
|
DWORD result = ERROR_SUCCESS;
|
|
|
|
path = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
|
|
|
|
if (!path) {
|
|
result = ERROR_INVALID_PARAMETER;
|
|
} else {
|
|
result = fs_delete_file(path);
|
|
}
|
|
|
|
return met_api->packet.transmit_response(result, remote, response);
|
|
}
|
|
|
|
/*
|
|
* Expands a file path and returns the expanded path to the requestor
|
|
*
|
|
* req: TLV_TYPE_FILE_PATH - The file path to expand
|
|
*/
|
|
DWORD request_fs_file_expand_path(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
DWORD result = ERROR_SUCCESS;
|
|
char *expanded = NULL;
|
|
char *regular;
|
|
|
|
regular = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
|
|
if (regular == NULL) {
|
|
result = ERROR_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
// Allocate storage for the expanded path
|
|
expanded = fs_expand_path(regular);
|
|
if (expanded == NULL) {
|
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
met_api->packet.add_tlv_string(response, TLV_TYPE_FILE_PATH, expanded);
|
|
free(expanded);
|
|
out:
|
|
return met_api->packet.transmit_response(result, remote, response);
|
|
}
|
|
|
|
DWORD request_fs_file_hash(Remote* remote, Packet* packet, ALG_ID hashType)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
char *filePath;
|
|
DWORD result = ERROR_SUCCESS;
|
|
HCRYPTPROV cryptProv = 0;
|
|
HCRYPTHASH hashInstance = 0;
|
|
|
|
FILE *fd = NULL;
|
|
size_t ret;
|
|
unsigned char buff[16384];
|
|
|
|
filePath = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
|
|
|
|
do
|
|
{
|
|
result = fs_fopen(filePath, "rb", &fd);
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
dprintf("[FILE HASH] Failed to open file: %s", filePath);
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
if (!CryptAcquireContext(&cryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
|
{
|
|
result = GetLastError();
|
|
dprintf("[FILE HASH] Failed to get the Crypt context: %d (%x)", result, result);
|
|
break;
|
|
}
|
|
|
|
if (!CryptCreateHash(cryptProv, hashType, 0, 0, &hashInstance))
|
|
{
|
|
result = GetLastError();
|
|
dprintf("[FILE HASH] Failed to get the hash instance: %d (%x)", result, result);
|
|
break;
|
|
}
|
|
|
|
BOOL failed = FALSE;
|
|
while ((ret = fread(buff, 1, sizeof(buff), fd)) > 0) {
|
|
if (!CryptHashData(hashInstance, buff, (DWORD)ret, 0))
|
|
{
|
|
result = GetLastError();
|
|
dprintf("[FILE HASH] Failed to hash a chunk of data", result, result);
|
|
failed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
break;
|
|
}
|
|
|
|
DWORD hashSize = 0;
|
|
DWORD hashBufferSize = sizeof(hashSize);
|
|
if (!CryptGetHashParam(hashInstance, HP_HASHSIZE, (BYTE*)&hashSize, &hashBufferSize, 0) || hashSize == 0)
|
|
{
|
|
result = GetLastError();
|
|
dprintf("[FILE HASH] Failed to get the hash size: %d (%x)", result, result);
|
|
}
|
|
|
|
dprintf("[FILE HASH] The given hash is %d bytes in size", hashSize);
|
|
|
|
// We'll reuse the buff var here because it's more than big enough for the
|
|
// size of any hash that'll be calculated
|
|
if (!CryptGetHashParam(hashInstance, HP_HASHVAL, buff, &hashSize, 0))
|
|
{
|
|
result = GetLastError();
|
|
dprintf("[FILE HASH] Failed to get the hash value: %d (%x)", result, result);
|
|
break;
|
|
}
|
|
|
|
dprintf("[FILE HASH] Successfully generated hash");
|
|
|
|
met_api->packet.add_tlv_raw(response, TLV_TYPE_FILE_HASH, buff, hashSize);
|
|
|
|
} while (0);
|
|
|
|
if (hashInstance != 0)
|
|
{
|
|
CryptDestroyHash(hashInstance);
|
|
}
|
|
|
|
if (cryptProv != 0)
|
|
{
|
|
CryptReleaseContext(cryptProv, 0);
|
|
}
|
|
|
|
if (fd != NULL)
|
|
{
|
|
fclose(fd);
|
|
}
|
|
|
|
return met_api->packet.transmit_response(result, remote, response);}
|
|
|
|
|
|
/*
|
|
* Returns the MD5 hash for a specified file path
|
|
*
|
|
* req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd
|
|
*/
|
|
DWORD request_fs_md5(Remote *remote, Packet *packet)
|
|
{
|
|
return request_fs_file_hash(remote, packet, CALG_MD5);
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns the SHA1 hash for a specified file path
|
|
*
|
|
* req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd
|
|
*/
|
|
DWORD request_fs_sha1(Remote *remote, Packet *packet)
|
|
{
|
|
return request_fs_file_hash(remote, packet, CALG_SHA1);
|
|
}
|
|
|
|
/*
|
|
* Moves source file path to destination
|
|
*
|
|
* req: TLV_TYPE_FILE_PATH - The file path to expand
|
|
*/
|
|
DWORD request_fs_file_move(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
DWORD result = ERROR_SUCCESS;
|
|
char *oldpath;
|
|
char *newpath;
|
|
|
|
oldpath = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_NAME);
|
|
newpath = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
|
|
|
|
if (!oldpath) {
|
|
result = ERROR_INVALID_PARAMETER;
|
|
} else {
|
|
result = fs_move(oldpath, newpath);
|
|
}
|
|
|
|
return met_api->packet.transmit_response(result, remote, response);
|
|
}
|
|
|
|
/*
|
|
* Copies source file path to destination
|
|
*
|
|
* req: TLV_TYPE_FILE_PATH - The file path to expand
|
|
*/
|
|
DWORD request_fs_file_copy(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
DWORD result = ERROR_SUCCESS;
|
|
char *oldpath;
|
|
char *newpath;
|
|
|
|
oldpath = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_NAME);
|
|
newpath = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_FILE_PATH);
|
|
|
|
if (!oldpath) {
|
|
result = ERROR_INVALID_PARAMETER;
|
|
} else {
|
|
result = fs_copy(oldpath, newpath);
|
|
}
|
|
|
|
return met_api->packet.transmit_response(result, remote, response);
|
|
}
|