Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process injection RWX injection / Mockingjay local injection #2587

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions atomics/T1055/T1055.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,36 @@ atomic_tests:
cleanup_command: 'Get-Process -Name calc, CalculatorApp -ErrorAction SilentlyContinue | Stop-Process -Force'
name: powershell
elevation_required: false
- name: Read-Write-Execute process Injection
auto_generated_guid: 49543237-25db-497b-90df-d0a0a6e8fe2c
description: |
This test exploited the vulnerability in legitimate PE formats where sections have RWX permission and enough space for shellcode.
The RWX injection avoided the use of VirtualAlloc, WriteVirtualMemory, and ProtectVirtualMemory, thus evading detection mechanisms
that relied on API call sequences and heuristics. The RWX injection utilises API call sequences: LoadLibrary --> GetModuleInformation --> GetModuleHandleA --> RtlCopyMemory --> CreateThread.
The injected shellcode will open a message box and a notepad.
RWX Process Injection, also known as MockingJay, was introduced to the security community by SecurityJoes.
More details can be found at https://www.securityjoes.com/post/process-mockingjay-echoing-rwx-in-userland-to-achieve-code-execution.
The original injector and idea were developed for game cheats, as visible at https://github.com/M-r-J-o-h-n/SWH-Injector.
supported_platforms:
- windows
input_arguments:
vuln_dll:
description: vulnerable DLL
type: path
default: PathToAtomicsFolder\T1055\bin\x64\vuln_dll\msys-2.0.dll
dependency_executor_name: powershell
dependencies:
- description: |
Utility to inject must exist on disk at specified location (#{vuln_dll})
prereq_command: |
if (Test-Path "#{vuln_dll}") {exit 0} else {exit 1}
get_prereq_command: |
New-Item -Type Directory (split-path "#{vuln_dll}") -ErrorAction ignore | Out-Null
Invoke-WebRequest "https://github.com/redcanaryco/atomic-red-team/raw/master/atomics/T1055/bin/x64/vuln_dll/msys-2.0.dll" -OutFile "#{vuln_dll}"
executor:
command: |
$address = (& "$PathToAtomicsFolder\T1055\bin\x64\searchVuln.exe" "$PathToAtomicsFolder\T1055\bin\x64\vuln_dll\" | Out-String | Select-String -Pattern "VirtualAddress: (\w+)").Matches.Groups[1].Value
& "PathToAtomicsFolder\T1055\bin\x64\RWXinjectionLocal.exe" "#{vuln_dll}" $address
cleanup_command: 'Get-Process -Name Notepad -ErrorAction SilentlyContinue | Stop-Process -Force'
name: powershell
elevation_required: true
Binary file added atomics/T1055/bin/x64/RWXinjectionLocal.exe
Binary file not shown.
Binary file added atomics/T1055/bin/x64/searchVuln.exe
Binary file not shown.
Binary file added atomics/T1055/bin/x64/vuln_dll/msys-2.0.dll
Binary file not shown.
1 change: 1 addition & 0 deletions atomics/T1055/src/x64/RWXInjection/build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /LocalInjection.c /link Psapi.lib /OUT:RWXinjectionLocal.exe /SUBSYSTEM:CONSOLE /MACHINE:x64
207 changes: 207 additions & 0 deletions atomics/T1055/src/x64/RWXInjection/path_finder.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/**
Author: Thomas X Meng
The code scans for PE sections with WRX permission. It does it sequentially which
is not as efficient as parallel processing. I did not find a equivalent 3rd lib in C as pefile
Windows defender bypassed, engine Version: 1.1.23100.2009
**/

#include <windows.h>
#include <stdio.h>
#include <wintrust.h>
#include <softpub.h>

// Define the GUID for WinVerifyTrust action if it is not already defined.
#ifndef WINTRUST_ACTION_GENERIC_VERIFY_V2
#define WINTRUST_ACTION_GENERIC_VERIFY_V2 \
{ 0xaac56b, 0xcd44, 0x11d0, { 0x8c, 0xc2, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee } }
#endif

GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;

#define MAX_SECTION_NAME_LEN 8

// Prototypes
const char* GetPEArchitecture(WORD machine);
BOOL IsSigned(LPCWSTR filepath);

typedef struct {
char name[MAX_SECTION_NAME_LEN + 1]; // Plus null terminator
DWORD offset;
DWORD size;
DWORD raw_size;
} RWXSection;

// Add this function to check for architecture
const char* GetPEArchitecture(WORD machine) {
switch(machine) {
case IMAGE_FILE_MACHINE_I386:
return "x86";
case IMAGE_FILE_MACHINE_AMD64:
return "x64";
// Add other architectures as needed
default:
return "Unknown";
}
}

BOOL IsSigned(LPCWSTR filepath) {
LONG lStatus; // Declare lStatus here, once
DWORD dwLastError;

// Initialize the WINTRUST_FILE_INFO structure.
WINTRUST_FILE_INFO FileData;
memset(&FileData, 0, sizeof(FileData));
FileData.cbStruct = sizeof(WINTRUST_FILE_INFO);
FileData.pcwszFilePath = filepath;
FileData.hFile = NULL;
FileData.pgKnownSubject = NULL;

// Initialize the WINTRUST_DATA structure.
WINTRUST_DATA WinTrustData;
memset(&WinTrustData, 0, sizeof(WinTrustData));
WinTrustData.cbStruct = sizeof(WinTrustData);
WinTrustData.pPolicyCallbackData = NULL;
WinTrustData.pSIPClientData = NULL;
WinTrustData.dwUIChoice = WTD_UI_NONE;
WinTrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
WinTrustData.dwUnionChoice = WTD_CHOICE_FILE;
WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY;
WinTrustData.hWVTStateData = NULL;
WinTrustData.pwszURLReference = NULL;
WinTrustData.dwProvFlags = WTD_USE_DEFAULT_OSVER_CHECK;
WinTrustData.dwUIContext = 0;
WinTrustData.pFile = &FileData;

// Use the WinVerifyTrust function to check the signature.
lStatus = WinVerifyTrust(NULL, &WVTPolicyGUID, &WinTrustData);

dwLastError = GetLastError();

// Any value other than zero indicates that there is no signature.
if (lStatus != ERROR_SUCCESS) {
SetLastError(dwLastError);
WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust(NULL, &WVTPolicyGUID, &WinTrustData); // Use WVTPolicyGUID
return FALSE;
}

// Cleanup after the trust verification is done.
WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust(NULL, &WVTPolicyGUID, &WinTrustData); // Use WVTPolicyGUID again
return TRUE;
}


void checkSectionCharacteristics(PIMAGE_SECTION_HEADER section, RWXSection* rwxSection, const char* filePath) {
if ((section->Characteristics & IMAGE_SCN_MEM_EXECUTE) &&
(section->Characteristics & IMAGE_SCN_MEM_READ) &&
(section->Characteristics & IMAGE_SCN_MEM_WRITE)) {
// Make sure to null-terminate the name
strncpy(rwxSection->name, (const char*)section->Name, IMAGE_SIZEOF_SHORT_NAME);
rwxSection->name[IMAGE_SIZEOF_SHORT_NAME] = '\0';

rwxSection->offset = section->VirtualAddress;
rwxSection->size = section->Misc.VirtualSize;
rwxSection->raw_size = section->SizeOfRawData;

// Print section info with file path
printf("[+] File: %s\n", filePath);
printf("[+] RWX Section Found: %s\n", rwxSection->name);
printf("[+] VirtualAddress (sec offset): 0x%X\n", rwxSection->offset);
printf("[+] VirtualSize: 0x%X\n", rwxSection->size);
printf("[+] SizeOfRawData: 0x%X\n", rwxSection->raw_size);
}
}


// Updated ProcessFile function
void ProcessFile(const char* filePath) {
HANDLE hFile = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[-] Could not open file %s. Error: %d\n", filePath, GetLastError());
return;
}

HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hMapping == NULL) {
printf("[-] Could not create file mapping for %s. Error: %d\n", filePath, GetLastError());
CloseHandle(hFile);
return;
}

LPVOID lpBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
if (lpBase == NULL) {
printf("[-] Could not map view of file %s. Error: %d\n", filePath, GetLastError());
CloseHandle(hMapping);
CloseHandle(hFile);
return;
}

PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)lpBase;
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
printf("[-] File %s is not a valid PE file.\n", filePath);
UnmapViewOfFile(lpBase);
CloseHandle(hMapping);
CloseHandle(hFile);
return;
}

PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)lpBase + dosHeader->e_lfanew);
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
printf("[-] File %s is not a valid PE file.\n", filePath);
UnmapViewOfFile(lpBase);
CloseHandle(hMapping);
CloseHandle(hFile);
return;
}

// Print the PE file architecture
printf("[+] Architecture: %s\n", GetPEArchitecture(ntHeaders->FileHeader.Machine));

// Check and print if the file is digitally signed
wchar_t wFilePath[MAX_PATH];
mbstowcs(wFilePath, filePath, MAX_PATH);
printf("[+] Signed: %s\n", IsSigned(wFilePath) ? "Yes" : "No");

// Process the sections
PIMAGE_SECTION_HEADER sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
RWXSection rwxSection;

for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i) {
checkSectionCharacteristics(&sectionHeaders[i], &rwxSection, filePath);
}
// Cleanup
UnmapViewOfFile(lpBase);
CloseHandle(hMapping);
CloseHandle(hFile);
}

int main(int argc, char* argv[]) {
if (argc != 2) {
printf("[+] Usage: %s <directory>\n", argv[0]);
return 1;
}

char* directoryPath = argv[1];
char searchPath[MAX_PATH];
snprintf(searchPath, sizeof(searchPath), "%s\\*.*", directoryPath);

WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile(searchPath, &findFileData);

if (hFind == INVALID_HANDLE_VALUE) {
printf("[-] Unable to find files in directory %s. Error is %u\n", directoryPath, GetLastError());
return 1;
}

do {
if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
char filePath[MAX_PATH];
snprintf(filePath, sizeof(filePath), "%s\\%s", directoryPath, findFileData.cFileName);
ProcessFile(filePath);
}
} while (FindNextFile(hFind, &findFileData) != 0);

FindClose(hFind);
return 0;
} //25519
Loading