mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-30 22:19:17 +02:00

The goals of this work are: * To fix issue where backgrounding and re-interacting with channels wasn't working. * To fix issue where closing of meterpreter was not closing off background prcoesses (such as cmd.exe). The two things preventing this stuff from working were: * When interactive channels are backgrounded their handles were destroyed along with the context that wraps them up. Making them interactive again had no impact because the handle and context were invalid. If anything, this made meterpreter unstable. Sometimes the session would die when attempting to interact with the channel again. * When closing channels, there was no way of terminating the process that sat behind the scenes because no reference to the process was retained. Channels would close and handles would close, but no process termination was done. To fix these problems: * The interactive thread no longer terminates when backgrounded. Instead its put in a suspended state where it's waiting a signal from a resume handle that's associated with the channel's context. This means that the destruction of the context doesn't happen at all until the termination of the channel, which is exactly when it should happen anyway. * Process handles are stored alongside the input/output handles so that when the time comes, the process can be terminated if required. This means that when the channels are closed, the code has a reference to the associated process which can be terminated. This is only done for interactive processes, non-interactive processes do not have this problem because meterpreter doesn't have to keep track of them.
529 lines
11 KiB
C
529 lines
11 KiB
C
#include "common.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <pthread.h>
|
|
|
|
int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout);
|
|
int __futex_wake(volatile void *ftx, int count);
|
|
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
|
|
#endif
|
|
|
|
// thread.c contains wrappers for the primitives of locks, events and threads for use in
|
|
// the multithreaded meterpreter. This is the win32/win64 implementation.
|
|
|
|
/*****************************************************************************************/
|
|
|
|
/*
|
|
* Create a new lock. We choose Mutex's over CriticalSections as their appears to be an issue
|
|
* when using CriticalSections with OpenSSL on some Windows systems. Mutex's are not as optimal
|
|
* as CriticalSections but they appear to resolve the OpenSSL deadlock issue.
|
|
*/
|
|
LOCK * lock_create( VOID )
|
|
{
|
|
LOCK * lock = (LOCK *)malloc( sizeof( LOCK ) );
|
|
if( lock != NULL )
|
|
{
|
|
memset( lock, 0, sizeof( LOCK ) );
|
|
|
|
#ifdef _WIN32
|
|
lock->handle = CreateMutex( NULL, FALSE, NULL );
|
|
#else
|
|
pthread_mutex_init(lock->handle, NULL);
|
|
#endif
|
|
}
|
|
return lock;
|
|
}
|
|
|
|
/*
|
|
* Destroy a lock that is no longer required.
|
|
*/
|
|
VOID lock_destroy( LOCK * lock )
|
|
{
|
|
if( lock != NULL )
|
|
{
|
|
lock_release( lock );
|
|
|
|
#ifdef _WIN32
|
|
CloseHandle( lock->handle );
|
|
#else
|
|
pthread_mutex_destroy(lock->handle);
|
|
#endif
|
|
|
|
free( lock );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Acquire a lock and block untill it is acquired.
|
|
*/
|
|
VOID lock_acquire( LOCK * lock )
|
|
{
|
|
if( lock != NULL ) {
|
|
#ifdef _WIN32
|
|
WaitForSingleObject( lock->handle, INFINITE );
|
|
#else
|
|
pthread_mutex_lock(lock->handle);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Release a lock previously held.
|
|
*/
|
|
VOID lock_release( LOCK * lock )
|
|
{
|
|
if( lock != NULL ) {
|
|
#ifdef _WIN32
|
|
ReleaseMutex( lock->handle );
|
|
#else
|
|
pthread_mutex_unlock(lock->handle);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************************/
|
|
|
|
/*
|
|
* Create a new event which can be signaled/polled/and blocked on.
|
|
*/
|
|
EVENT * event_create( VOID )
|
|
{
|
|
EVENT * event = NULL;
|
|
|
|
event = (EVENT *)malloc( sizeof( EVENT ) );
|
|
if( event == NULL )
|
|
return NULL;
|
|
|
|
memset( event, 0, sizeof( EVENT ) );
|
|
|
|
#ifdef _WIN32
|
|
event->handle = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
if( event->handle == NULL )
|
|
{
|
|
free( event );
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
return event;
|
|
}
|
|
|
|
/*
|
|
* Destroy an event.
|
|
*/
|
|
BOOL event_destroy( EVENT * event )
|
|
{
|
|
if( event == NULL )
|
|
return FALSE;
|
|
|
|
#ifdef _WIN32
|
|
CloseHandle( event->handle );
|
|
#endif
|
|
|
|
free( event );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Signal an event.
|
|
*/
|
|
BOOL event_signal( EVENT * event )
|
|
{
|
|
if( event == NULL )
|
|
return FALSE;
|
|
|
|
#ifdef _WIN32
|
|
dprintf( "Signalling 0x%x", event->handle );
|
|
if( SetEvent( event->handle ) == 0 ) {
|
|
dprintf( "Signalling 0x%x failed %u", event->handle, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
#else
|
|
event->handle = (HANDLE)1;
|
|
__futex_wake(&(event->handle), 1);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Poll an event to see if it has been signaled. Set timeout to -1 to block indefinatly.
|
|
* If timeout is 0 this function does not block but returns immediately.
|
|
*/
|
|
BOOL event_poll( EVENT * event, DWORD timeout )
|
|
{
|
|
if( event == NULL )
|
|
return FALSE;
|
|
|
|
#ifdef _WIN32
|
|
if( WaitForSingleObject( event->handle, timeout ) == WAIT_OBJECT_0 )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
#else
|
|
// DWORD WINAPI WaitForSingleObject(
|
|
// __in HANDLE hHandle,
|
|
// __in DWORD dwMilliseconds
|
|
// );
|
|
// http://msdn.microsoft.com/en-us/library/ms687032(VS.85).aspx
|
|
|
|
if(timeout) {
|
|
struct timespec ts;
|
|
|
|
// XXX, need to verify for -1. below modified from bionic/pthread.c
|
|
// and maybe loop if needed ;\
|
|
|
|
ts.tv_sec = timeout / 1000;
|
|
ts.tv_nsec = (timeout%1000)*1000000;
|
|
if (ts.tv_nsec >= 1000000000) {
|
|
ts.tv_sec++;
|
|
ts.tv_nsec -= 1000000000;
|
|
}
|
|
|
|
// atomically checks if event->handle is 0, if so,
|
|
// it sleeps for timeout. if event->handle is 1, it
|
|
// returns straight away.
|
|
|
|
__futex_wait(&(event->handle), 0, &ts);
|
|
}
|
|
|
|
return event->handle ? TRUE : FALSE;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************************/
|
|
|
|
/*
|
|
* Opens and create a THREAD item for the current/calling thread.
|
|
*/
|
|
THREAD * thread_open( VOID )
|
|
{
|
|
THREAD * thread = NULL;
|
|
#ifdef _WIN32
|
|
OPENTHREAD pOpenThread = NULL;
|
|
HMODULE hKernel32 = NULL;
|
|
|
|
|
|
thread = (THREAD *)malloc( sizeof( THREAD ) );
|
|
if( thread != NULL )
|
|
{
|
|
memset( thread, 0, sizeof(THREAD) );
|
|
|
|
thread->id = GetCurrentThreadId();
|
|
thread->sigterm = event_create();
|
|
|
|
// Windows specific process of opening a handle to the current thread which
|
|
// works on NT4 up. We only want THREAD_TERMINATE|THREAD_SUSPEND_RESUME access
|
|
// for now.
|
|
|
|
// First we try to use the normal OpenThread function, available on Windows 2000 and up...
|
|
hKernel32 = LoadLibrary( "kernel32.dll" );
|
|
pOpenThread = (OPENTHREAD)GetProcAddress( hKernel32, "OpenThread" );
|
|
if( pOpenThread )
|
|
{
|
|
thread->handle = pOpenThread( THREAD_TERMINATE|THREAD_SUSPEND_RESUME, FALSE, thread->id );
|
|
}
|
|
else
|
|
{
|
|
NTOPENTHREAD pNtOpenThread = NULL;
|
|
// If we can't use OpenThread, we try the older NtOpenThread function as found on NT4 machines.
|
|
HMODULE hNtDll = LoadLibrary( "ntdll.dll" );
|
|
pNtOpenThread = (NTOPENTHREAD)GetProcAddress( hNtDll, "NtOpenThread" );
|
|
if( pNtOpenThread )
|
|
{
|
|
_OBJECT_ATTRIBUTES oa = {0};
|
|
_CLIENT_ID cid = {0};
|
|
|
|
cid.UniqueThread = (PVOID)thread->id;
|
|
|
|
pNtOpenThread( &thread->handle, THREAD_TERMINATE|THREAD_SUSPEND_RESUME, &oa, &cid );
|
|
}
|
|
|
|
FreeLibrary( hNtDll );
|
|
}
|
|
|
|
FreeLibrary( hKernel32 );
|
|
}
|
|
|
|
return thread;
|
|
#else
|
|
thread = (THREAD *)malloc( sizeof( THREAD ) );
|
|
|
|
if( thread != NULL )
|
|
{
|
|
memset( thread, 0, sizeof(THREAD) );
|
|
|
|
thread->id = gettid();
|
|
thread->sigterm = event_create();
|
|
thread->pid = pthread_self();
|
|
}
|
|
return thread;
|
|
#endif
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
struct thread_conditional {
|
|
pthread_mutex_t suspend_mutex;
|
|
pthread_cond_t suspend_cond;
|
|
int engine_running;
|
|
THREADFUNK (*funk)(void *arg);
|
|
THREAD *thread;
|
|
};
|
|
|
|
void __thread_cancelled(int signo)
|
|
{
|
|
signal(SIGTERM, SIG_DFL);
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
/*
|
|
* This is the entry point for threads created with thread_create.
|
|
*
|
|
* To implement suspended threads, we need to do some messing around with
|
|
* mutexes and conditional broadcasts ;\
|
|
*/
|
|
|
|
void *__paused_thread(void *req)
|
|
{
|
|
LPVOID (*funk)(void *arg);
|
|
THREAD *thread;
|
|
|
|
struct thread_conditional *tc = (struct thread_conditional *)(req);
|
|
tc->thread->id = gettid();
|
|
|
|
signal(SIGTERM, __thread_cancelled);
|
|
|
|
pthread_mutex_lock(&tc->suspend_mutex);
|
|
|
|
while(tc->engine_running == FALSE) {
|
|
pthread_cond_wait(&tc->suspend_cond, &tc->suspend_mutex);
|
|
}
|
|
|
|
pthread_mutex_unlock(&tc->suspend_mutex);
|
|
|
|
funk = tc->funk;
|
|
thread = tc->thread;
|
|
free(tc);
|
|
|
|
if(event_poll(thread->sigterm, 0) == TRUE) {
|
|
/*
|
|
* In some cases, we might want to stop a thread before it does anything :/
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
return funk(thread);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Create a new thread in a suspended state.
|
|
*/
|
|
THREAD * thread_create( THREADFUNK funk, LPVOID param1, LPVOID param2 )
|
|
{
|
|
THREAD * thread = NULL;
|
|
|
|
if( funk == NULL )
|
|
return NULL;
|
|
|
|
thread = (THREAD *)malloc( sizeof( THREAD ) );
|
|
if( thread == NULL )
|
|
return NULL;
|
|
|
|
memset( thread, 0, sizeof( THREAD ) );
|
|
|
|
thread->sigterm = event_create();
|
|
if( thread->sigterm == NULL )
|
|
{
|
|
free( thread );
|
|
return NULL;
|
|
}
|
|
|
|
|
|
thread->parameter1 = param1;
|
|
thread->parameter2 = param2;
|
|
|
|
#ifdef _WIN32
|
|
thread->handle = CreateThread( NULL, 0, funk, thread, CREATE_SUSPENDED, &thread->id );
|
|
|
|
if( thread->handle == NULL )
|
|
{
|
|
event_destroy( thread->sigterm );
|
|
free( thread );
|
|
return NULL;
|
|
}
|
|
|
|
#else
|
|
// PKS, this is fucky.
|
|
// we need to use conditionals to implement this.
|
|
|
|
thread->thread_started = FALSE;
|
|
|
|
do {
|
|
pthread_t pid;
|
|
|
|
struct thread_conditional *tc;
|
|
tc = (struct thread_conditional *) malloc(sizeof(struct thread_conditional));
|
|
|
|
if( tc == NULL ) {
|
|
event_destroy(thread->sigterm);
|
|
free(thread);
|
|
return NULL;
|
|
}
|
|
|
|
memset( tc, 0, sizeof(struct thread_conditional));
|
|
|
|
pthread_mutex_init(&tc->suspend_mutex, NULL);
|
|
pthread_cond_init(&tc->suspend_cond, NULL);
|
|
|
|
tc->funk = funk;
|
|
tc->thread = thread;
|
|
|
|
thread->suspend_thread_data = (void *)(tc);
|
|
|
|
if(pthread_create(&(thread->pid), NULL, __paused_thread, tc) == -1) {
|
|
free(tc);
|
|
event_destroy(thread->sigterm);
|
|
free(thread);
|
|
return NULL;
|
|
}
|
|
// __paused_thread free's the allocated memory.
|
|
|
|
} while(0);
|
|
#endif
|
|
|
|
return thread;
|
|
}
|
|
|
|
/*
|
|
* Run a thread.
|
|
*/
|
|
BOOL thread_run( THREAD * thread )
|
|
{
|
|
if( thread == NULL )
|
|
return FALSE;
|
|
|
|
#ifdef _WIN32
|
|
if( ResumeThread( thread->handle ) < 0 )
|
|
return FALSE;
|
|
|
|
#else
|
|
struct thread_conditional *tc;
|
|
tc = (struct thread_conditional *)thread->suspend_thread_data;
|
|
pthread_mutex_lock(&tc->suspend_mutex);
|
|
tc->engine_running = TRUE;
|
|
pthread_mutex_unlock(&tc->suspend_mutex);
|
|
pthread_cond_signal(&tc->suspend_cond);
|
|
|
|
thread->thread_started = TRUE;
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Signals the thread to terminate. It is the responsibility of the thread to wait for and process this signal.
|
|
* Should be used to signal the thread to terminate.
|
|
*/
|
|
BOOL thread_sigterm( THREAD * thread )
|
|
{
|
|
BOOL ret;
|
|
|
|
if( thread == NULL )
|
|
return FALSE;
|
|
|
|
ret = event_signal( thread->sigterm );
|
|
|
|
#ifndef _WIN32
|
|
/*
|
|
* If we sig term a thread before it's started execution, we will leak memory / not be
|
|
* able to join on the thread, etc.
|
|
*
|
|
* Therefore, we need to start the thread executing before calling thread_join
|
|
*/
|
|
if(thread->thread_started != TRUE) {
|
|
thread_run(thread);
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Terminate a thread. Use with caution! better to signal your thread to terminate and wait for it to do so.
|
|
*/
|
|
BOOL thread_kill( THREAD * thread )
|
|
{
|
|
if( thread == NULL )
|
|
return FALSE;
|
|
|
|
#ifdef _WIN32
|
|
if( TerminateThread( thread->handle, -1 ) == 0 )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
#else
|
|
// bionic/libc/bionic/CAVEATS
|
|
// - pthread cancellation is *not* supported. this seemingly simple "feature" is the source
|
|
// of much bloat and complexity in a C library. Besides, you'd better write correct
|
|
// multi-threaded code instead of relying on this stuff.
|
|
|
|
// pthread_kill says: Note that pthread_kill() only causes the
|
|
// signal to be handled in the context of the given thread; the signal
|
|
// action (termination or stopping) affects the process as a whole.
|
|
|
|
// We send our thread a SIGTERM, and a signal handler calls pthread_exit().
|
|
|
|
pthread_kill(thread->id, SIGTERM);
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Blocks untill the thread has terminated.
|
|
*/
|
|
BOOL thread_join( THREAD * thread )
|
|
{
|
|
if( thread == NULL )
|
|
return FALSE;
|
|
|
|
#ifdef _WIN32
|
|
if( WaitForSingleObject( thread->handle, INFINITE ) == WAIT_OBJECT_0 )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
#else
|
|
if(pthread_join(thread->pid, NULL) == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Destroys a previously created thread. Note, this does not terminate the thread. You must signal your
|
|
* thread to terminate and wait for it to do so (via thread_signal/thread_join).
|
|
*/
|
|
BOOL thread_destroy( THREAD * thread )
|
|
{
|
|
if( thread == NULL )
|
|
return FALSE;
|
|
|
|
event_destroy( thread->sigterm );
|
|
|
|
#ifdef _WIN32
|
|
CloseHandle( thread->handle );
|
|
#else
|
|
pthread_detach(thread->pid);
|
|
#endif
|
|
|
|
free( thread );
|
|
|
|
return TRUE;
|
|
}
|