NixOS-based container for running GitHub actions
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix spoof binaries

+101 -13
+31 -1
build.zig
··· 54 54 const gid = b.option(u32, "gid", "gid to run as") orelse 1001; 55 55 const groups = b.option([]const u8, "groups", "list of supplemental groups") orelse "1001"; 56 56 const username = b.option([]const u8, "username", "username to run as") orelse "github"; 57 + const homedir = b.option([]const u8, "homedir", "home directory of user") orelse "/github/home"; 57 58 const tail = tail: { 58 59 const tail = b.option([]const u8, "tail", "real tail binary") orelse try find(b, "tail"); 59 60 if (!std.mem.eql(u8, std.fs.path.basename(tail), "coreutils")) break :tail tail; ··· 62 63 }; 63 64 const nix = b.option([]const u8, "nix", "real nix binary") orelse try find(b, "nix"); 64 65 const bash = b.option([]const u8, "bash", "real bash binary") orelse try find(b, "bash"); 66 + const sh = sh: { 67 + const sh = b.option([]const u8, "sh", "real sh binary") orelse try find(b, "sh"); 68 + if (!std.mem.eql(u8, std.fs.path.basename(sh), "sh")) break :sh sh; 69 + const dir = std.fs.path.dirname(sh) orelse break :sh sh; 70 + break :sh try std.fs.path.join(b.allocator, &.{ dir, "sh" }); 71 + }; 65 72 66 73 const options = b.addOptions(); 67 74 options.addOption(u32, "uid", uid); ··· 76 83 break :groups list.items; 77 84 }); 78 85 options.addOption([]const u8, "username", username); 86 + options.addOption([]const u8, "homedir", homedir); 79 87 options.addOption([]const u8, "tail", tail); 80 88 options.addOption([]const u8, "nix", nix); 81 89 options.addOption([]const u8, "bash", bash); 90 + options.addOption([]const u8, "sh", sh); 82 91 83 92 const execas_exe = b.addExecutable(.{ 84 93 .name = b.fmt("execas-{d}", .{uid}), ··· 135 144 b.installArtifact(bash_exe); 136 145 137 146 const bash_tests = b.addTest(.{ 138 - .root_module = tail_exe.root_module, 147 + .root_module = bash_exe.root_module, 139 148 }); 140 149 141 150 const run_bash_tests = b.addRunArtifact(bash_tests); 142 151 test_step.dependOn(&run_bash_tests.step); 152 + 153 + const sh_exe = b.addExecutable(.{ 154 + .name = "sh", 155 + .root_module = b.createModule(.{ 156 + .root_source_file = b.path("src/sh.zig"), 157 + .target = target, 158 + .optimize = optimize, 159 + }), 160 + .use_llvm = false, 161 + .use_lld = false, 162 + }); 163 + sh_exe.root_module.addOptions("options", options); 164 + 165 + b.installArtifact(sh_exe); 166 + 167 + const sh_tests = b.addTest(.{ 168 + .root_module = sh_exe.root_module, 169 + }); 170 + 171 + const run_sh_tests = b.addRunArtifact(sh_tests); 172 + test_step.dependOn(&run_sh_tests.step); 143 173 }
+1
flake.nix
··· 498 498 ] 499 499 ); 500 500 username = "github"; 501 + homedir = "/github/home"; 501 502 zig = zig.packages.${pkgs.stdenv.hostPlatform.system}.master; 502 503 }; 503 504 in
+3
package.nix
··· 8 8 uid, 9 9 gid, 10 10 username, 11 + homedir, 11 12 groups, 12 13 coreutils-full, 13 14 bashInteractive, ··· 25 26 "-Dgid=${toString gid}" 26 27 "-Dgroups=${groups}" 27 28 "-Dusername=${username}" 29 + "-Dhomedir=${homedir}" 28 30 "-Dtail=${lib.getExe' coreutils-full "tail"}" 29 31 "-Dnix=${lib.getExe' nix "nix"}" 30 32 "-Dbash=${lib.getExe' bashInteractive "bash"}" 33 + "-Dsh=${lib.getExe' bashInteractive "sh"}" 31 34 ]; 32 35 meta = { 33 36 mainProgram = "execas-${toString uid}";
+2 -2
src/bash.zig
··· 10 10 const arena: std.mem.Allocator = init.arena.allocator(); 11 11 const io = init.io; 12 12 13 - var environ_map = try lib.fixupEnvironMap(arena, init.environ_map); 13 + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .user); 14 14 defer environ_map.deinit(); 15 15 16 16 var argv: std.ArrayList([]const u8) = .empty; ··· 26 26 try argv.append(arena, arg); 27 27 } 28 28 29 - try lib.setUID(); 29 + try lib.switchToUser(); 30 30 31 31 const err = std.process.replace(io, .{ 32 32 .argv = argv.items,
+2 -2
src/lib.zig
··· 4 4 const std = @import("std"); 5 5 6 6 pub const fixupEnvironMap = @import("lib/env.zig").fixupEnvironMap; 7 - pub const setUID = @import("lib/setuid.zig").setUID; 7 + pub const switchToUser = @import("lib/switchtouser.zig").switchToUser; 8 8 9 9 test { 10 10 _ = @import("lib/env.zig"); 11 - _ = @import("lib/setuid.zig"); 11 + _ = @import("lib/switchtouser.zig"); 12 12 }
+15 -2
src/lib/env.zig
··· 4 4 const std = @import("std"); 5 5 const options = @import("options"); 6 6 7 - pub fn fixupEnvironMap(alloc: std.mem.Allocator, old: *const std.process.Environ.Map) (std.mem.Allocator.Error || std.Io.Writer.Error)!std.process.Environ.Map { 7 + pub fn fixupEnvironMap( 8 + alloc: std.mem.Allocator, 9 + old: *const std.process.Environ.Map, 10 + user: enum { root, user }, 11 + ) (std.mem.Allocator.Error || std.Io.Writer.Error)!std.process.Environ.Map { 8 12 var new = try old.clone(alloc); 9 13 errdefer new.deinit(); 10 14 ··· 28 32 try new.put("PATH", writer.written()); 29 33 } 30 34 31 - try new.put("USER", options.username); 35 + switch (user) { 36 + .root => { 37 + try new.put("USER", "root"); 38 + try new.put("HOME", "/root"); 39 + }, 40 + .user => { 41 + try new.put("USER", options.username); 42 + try new.put("HOME", "/github/home"); 43 + }, 44 + } 32 45 33 46 return new; 34 47 }
+1 -1
src/lib/setuid.zig src/lib/switchtouser.zig
··· 4 4 const std = @import("std"); 5 5 const options = @import("options"); 6 6 7 - pub fn setUID() !void { 7 + pub fn switchToUser() !void { 8 8 { 9 9 const rc = std.os.linux.setgroups(options.groups.len, options.groups.ptr); 10 10 switch (std.os.linux.errno(rc)) {
+39
src/sh.zig
··· 1 + // SPDX-FileCopyrightText: © 2023 Jeffrey C. Ollie 2 + // SPDX-License-Identifier: MIT 3 + 4 + const std = @import("std"); 5 + const options = @import("options"); 6 + 7 + const lib = @import("lib.zig"); 8 + 9 + pub fn main(init: std.process.Init) !u8 { 10 + const arena: std.mem.Allocator = init.arena.allocator(); 11 + const io = init.io; 12 + 13 + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .user); 14 + defer environ_map.deinit(); 15 + 16 + var argv: std.ArrayList([]const u8) = .empty; 17 + defer argv.deinit(arena); 18 + 19 + try argv.append(arena, options.sh); 20 + 21 + var it = try init.minimal.args.iterateAllocator(arena); 22 + defer it.deinit(); 23 + 24 + _ = it.next(); 25 + while (it.next()) |arg| { 26 + try argv.append(arena, arg); 27 + } 28 + 29 + try lib.switchToUser(); 30 + 31 + const err = std.process.replace(io, .{ 32 + .argv = argv.items, 33 + .environ_map = &environ_map, 34 + }); 35 + 36 + std.debug.print("unable to execute: {t}\n", .{err}); 37 + 38 + return 127; 39 + }
+7 -5
src/tail.zig
··· 10 10 const arena: std.mem.Allocator = init.arena.allocator(); 11 11 const io = init.io; 12 12 13 - var environ_map = try lib.fixupEnvironMap(arena, init.environ_map); 14 - defer environ_map.deinit(); 15 - 16 13 nix: { 17 14 if (!std.mem.eql(u8, init.environ_map.get("CI") orelse break :nix, "true")) break :nix; 18 15 if (!std.mem.eql(u8, init.environ_map.get("GITHUB_ACTIONS") orelse break :nix, "true")) break :nix; ··· 24 21 if (!std.mem.eql(u8, it.next() orelse break :nix, "-f")) break :nix; 25 22 if (!std.mem.eql(u8, it.next() orelse break :nix, "/dev/null")) break :nix; 26 23 if (it.next() != null) break :nix; 24 + 25 + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .root); 26 + defer environ_map.deinit(); 27 27 28 28 cwd: { 29 29 var dir = std.Io.Dir.openDirAbsolute(io, "/", .{}) catch break :cwd; ··· 35 35 .argv = &.{ 36 36 options.nix, 37 37 "daemon", 38 - "--trusted", 39 38 }, 40 39 .environ_map = &environ_map, 41 40 }); ··· 43 42 std.debug.print("unable to execute: {t}\n", .{err}); 44 43 return; 45 44 } 45 + 46 + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .user); 47 + defer environ_map.deinit(); 46 48 47 49 var argv: std.ArrayList([]const u8) = .empty; 48 50 defer argv.deinit(arena); ··· 57 59 try argv.append(arena, arg); 58 60 } 59 61 60 - try lib.setUID(); 62 + try lib.switchToUser(); 61 63 62 64 const err = std.process.replace(io, .{ 63 65 .argv = argv.items,