From ab4bf676b32878a8698e7c0e93c30676b3763f11 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 17 Feb 2024 20:26:35 +0000 Subject: [PATCH 1/5] I think this might work. --- .../install_service/main.eldritch | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/implants/imix/install_scripts/install_service/main.eldritch b/implants/imix/install_scripts/install_service/main.eldritch index ce6167feb..24e4d0abf 100644 --- a/implants/imix/install_scripts/install_service/main.eldritch +++ b/implants/imix/install_scripts/install_service/main.eldritch @@ -138,6 +138,72 @@ launch_daemon_template = """ """ +bsdinit_template = """ +#!/bin/sh +# +# PROVIDE: {{ service_name }} +# REQUIRE: LOGIN FILESYSTEMS +# KEYWORD: shutdown + +. /etc/rc.subr + +name="{{ service_name }}" +rcvar="{{ service_name }}_enable" + +# The command to start the service +command="{{ service_start_cmd }}" +# Additional command arguments if any +command_args="" + +# Load the rc.subr script +load_rc_config $name +: ${name}_enable:=no } + +# Define the function to start the service +start_cmd="${name}_start" + +# Start function +{{ service_name }}_start() { + echo "Starting {{ service_name }}." + # Execute the command to start the service + ${command} ${command_args} & +} + +# Define the function to stop the service +stop_cmd="${name}_stop" + +# Stop function +{{ service_name }}_stop() { + echo "Stopping {{ service_name }}." + # Command to stop the service if required + # For example, if {{ service_name }} supports graceful shutdown: + # killall -SIGTERM {{ service_name }} +} + +# Define the function to check if the service is running +status_cmd="${name}_status" + +# Status function +{{ service_name }}_status() { + # Check if the service is running + # For example, check if the process exists + if pgrep -q -x "{{ service_name }}"; then + echo "{{ service_name }} is not running." + else + echo "{{ service_name }} is not running." + fi +} + +# Define command line arguments to control the service +# e.g., {{ service_name }}_enable="YES" to enable the service + +# Start the service automatically during system startup +{{ service_name }}_enable="YES" + +# Call the rc.subr functions to handle the service +run_rc_command "$1" +""" + def is_using_systemd(): command_get_res = sys.shell("command -v systemctl") if command_get_res['status'] == 0 and file.is_file(command_get_res['stdout'].strip()): @@ -152,6 +218,13 @@ def is_using_sysvinit(): return True return False +def is_using_bsdinit(): + # Lol this is how ansible does it too :shrug: + # https://github.com/ansible/ansible/blob/386edc666ec2a053b4d576fc4b2deeb46fe492b8/lib/ansible/module_utils/facts/system/service_mgr.py#L124 + if sys.is_bsd(): + return True + return False + def systemd(service_name, service_desc, executable_path, executable_args): # assets.copy("persist_service/files/systemd.service.j2","/tmp/systemd.service.j2") file.write("/tmp/systemd.service.j2", systemd_service_template) @@ -190,6 +263,32 @@ def sysvinit(service_name, service_desc, executable_path, executable_args): sys.shell("service "+service_name+" start") print("sysvinit installed") +def bsdinit(service_name, service_desc, executable_path, executable_args): + # assets.copy("persist_service/files/sysvinit.sh.j2","/tmp/svc.sh.j2") + if not file.is_dir("/etc/rc.d"): + print("/etc/rc.d not found") + return + + file.write("/tmp/svc.sh.j2", bsdinit_template) + args = { + "service_name":service_name, + "service_desc":service_desc, + "service_start_cmd":executable_path+" "+executable_args + } + file.template("/tmp/svc.sh.j2","/etc/rc.d/"+service_name, args, False) + file.remove("/tmp/svc.sh.j2") + sys.shell("chmod +x "+"/etc/rc.d/"+service_name) + + sys.shell("chmod +x "+executable_path) + + rc_file = "/etc/rc.conf.local" + if not file.is_file(rc_file): + file.write(rc_file, "") + + sys.shell("sysrc -f /etc/rc.conf.local "+service_name+"=\"YES\"") + sys.shell("service "+service_name+" start") + print("bsdinit installed") + def launch_daemon(service_name, executable_path, executable_args): # assets.copy("persist_service/files/launch_daemon.plist.j2","/tmp/plist.j2") file.write("/tmp/plist.j2",launch_daemon_template) @@ -218,6 +317,9 @@ def persist_service(service_name, service_desc, executable_name, executable_args executable_path = "/var/root/"+executable_name file.copy(src_path, executable_path) launch_daemon(service_name, executable_path, executable_args) + elif sys.is_bsd(): + if is_using_bsdinit(): + bsdinit(service_name, service_desc, executable_path, executable_args) else: print("OS not supported") From 7bccdd8c1288ab877fc5c578be6062e68d0aa018 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:57:05 -0500 Subject: [PATCH 2/5] Freebsd (#615) * Add BSD Support * Fix Netstat for non-BSD --------- Co-authored-by: Joe Abbate Co-authored-by: Joe Abbate <40615740+jabbate19@users.noreply.github.com> --- implants/lib/eldritch/Cargo.toml | 4 +++- implants/lib/eldritch/src/file/list_impl.rs | 2 ++ implants/lib/eldritch/src/process/netstat_impl.rs | 9 +++++++++ implants/lib/eldritch/src/sys/exec_impl.rs | 6 +++--- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/implants/lib/eldritch/Cargo.toml b/implants/lib/eldritch/Cargo.toml index b659ac002..0afa7d1c6 100644 --- a/implants/lib/eldritch/Cargo.toml +++ b/implants/lib/eldritch/Cargo.toml @@ -29,7 +29,6 @@ hex-literal = { workspace = true } ipnetwork = { workspace = true } log = { workspace = true } md5 = { workspace = true } -netstat2 = { workspace = true } nix = { workspace = true } notify = { workspace = true } object = { workspace = true } @@ -76,6 +75,9 @@ winreg = { workspace = true } [target.'cfg(not(windows))'.dependencies] pnet = { workspace = true } +[target.'cfg(not(target_os = "freebsd"))'.dependencies] +netstat2 = { workspace = true } + [dev-dependencies] transport = { workspace = true, features = ["mock"]} httptest = { workspace = true } diff --git a/implants/lib/eldritch/src/file/list_impl.rs b/implants/lib/eldritch/src/file/list_impl.rs index f5451f490..4412789f3 100644 --- a/implants/lib/eldritch/src/file/list_impl.rs +++ b/implants/lib/eldritch/src/file/list_impl.rs @@ -16,6 +16,8 @@ use std::os::macos::fs::MetadataExt; use std::os::unix::fs::PermissionsExt; #[cfg(target_os = "windows")] use std::os::windows::fs::MetadataExt; +#[cfg(target_os = "freebsd")] +use std::os::freebsd::fs::MetadataExt; use sysinfo::{System, SystemExt, UserExt}; const UNKNOWN: &str = "UNKNOWN"; diff --git a/implants/lib/eldritch/src/process/netstat_impl.rs b/implants/lib/eldritch/src/process/netstat_impl.rs index 4d82dc968..4dae95b9e 100644 --- a/implants/lib/eldritch/src/process/netstat_impl.rs +++ b/implants/lib/eldritch/src/process/netstat_impl.rs @@ -1,5 +1,8 @@ use super::super::insert_dict_kv; use anyhow::Result; +#[cfg(target_os = "freebsd")] +use anyhow::anyhow; +#[cfg(not(target_os = "freebsd"))] use netstat2::*; use starlark::{ collections::SmallMap, @@ -7,6 +10,12 @@ use starlark::{ values::{dict::Dict, Heap, Value}, }; +#[cfg(target_os = "freebsd")] +pub fn netstat(_: &Heap) -> Result> { + Err(anyhow!("Not implemented for FreeBSD")) +} + +#[cfg(not(target_os = "freebsd"))] pub fn netstat(starlark_heap: &Heap) -> Result> { let mut out: Vec = Vec::new(); let af_flags = AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6; diff --git a/implants/lib/eldritch/src/sys/exec_impl.rs b/implants/lib/eldritch/src/sys/exec_impl.rs index 6578ac67a..1071bf3d7 100644 --- a/implants/lib/eldritch/src/sys/exec_impl.rs +++ b/implants/lib/eldritch/src/sys/exec_impl.rs @@ -1,7 +1,7 @@ use super::super::insert_dict_kv; use super::CommandOutput; use anyhow::{Context, Result}; -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] use nix::{ sys::wait::waitpid, unistd::{fork, ForkResult}, @@ -11,7 +11,7 @@ use starlark::{ const_frozen_string, values::{dict::Dict, Heap}, }; -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] use std::process::exit; use std::process::Command; // https://stackoverflow.com/questions/62978157/rust-how-to-spawn-child-process-that-continues-to-live-after-parent-receives-si#:~:text=You%20need%20to%20double%2Dfork,is%20not%20related%20to%20rust.&text=You%20must%20not%20forget%20to,will%20become%20a%20zombie%20process. @@ -54,7 +54,7 @@ fn handle_exec(path: String, args: Vec, disown: Option) -> Result< "Windows is not supported for disowned processes." )); - #[cfg(any(target_os = "linux", target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] match unsafe { fork()? } { ForkResult::Parent { child } => { // Wait for intermediate process to exit. From 2dd5c6b26750561bba0b340292a541b73a1bbc93 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 18 Feb 2024 19:12:29 +0000 Subject: [PATCH 3/5] Tested and works. --- .../install_service/main.eldritch | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/implants/imix/install_scripts/install_service/main.eldritch b/implants/imix/install_scripts/install_service/main.eldritch index 24e4d0abf..270e1a659 100644 --- a/implants/imix/install_scripts/install_service/main.eldritch +++ b/implants/imix/install_scripts/install_service/main.eldritch @@ -221,7 +221,7 @@ def is_using_sysvinit(): def is_using_bsdinit(): # Lol this is how ansible does it too :shrug: # https://github.com/ansible/ansible/blob/386edc666ec2a053b4d576fc4b2deeb46fe492b8/lib/ansible/module_utils/facts/system/service_mgr.py#L124 - if sys.is_bsd(): + if sys.get_os()['platform'] == "BSD": return True return False @@ -264,9 +264,9 @@ def sysvinit(service_name, service_desc, executable_path, executable_args): print("sysvinit installed") def bsdinit(service_name, service_desc, executable_path, executable_args): - # assets.copy("persist_service/files/sysvinit.sh.j2","/tmp/svc.sh.j2") - if not file.is_dir("/etc/rc.d"): - print("/etc/rc.d not found") + startup_dir = "/usr/local/etc/rc.d/" + if not file.is_dir(startup_dir): + print(startup_dir+" not found") return file.write("/tmp/svc.sh.j2", bsdinit_template) @@ -275,18 +275,13 @@ def bsdinit(service_name, service_desc, executable_path, executable_args): "service_desc":service_desc, "service_start_cmd":executable_path+" "+executable_args } - file.template("/tmp/svc.sh.j2","/etc/rc.d/"+service_name, args, False) + file.template("/tmp/svc.sh.j2",startup_dir+service_name+".sh", args, False) file.remove("/tmp/svc.sh.j2") - sys.shell("chmod +x "+"/etc/rc.d/"+service_name) - - sys.shell("chmod +x "+executable_path) - rc_file = "/etc/rc.conf.local" - if not file.is_file(rc_file): - file.write(rc_file, "") + sys.exec("/bin/sh",["-c","chmod +x "+startup_dir+service_name+".sh"]) + sys.exec("/bin/sh",["-c","chmod +x "+executable_path]) + sys.exec("/bin/sh",["-c","service "+service_name+".sh start"]) - sys.shell("sysrc -f /etc/rc.conf.local "+service_name+"=\"YES\"") - sys.shell("service "+service_name+" start") print("bsdinit installed") def launch_daemon(service_name, executable_path, executable_args): @@ -317,7 +312,9 @@ def persist_service(service_name, service_desc, executable_name, executable_args executable_path = "/var/root/"+executable_name file.copy(src_path, executable_path) launch_daemon(service_name, executable_path, executable_args) - elif sys.is_bsd(): + elif sys.get_os()['platform'] == "BSD": + executable_path = "/bin/"+executable_name + file.copy(src_path, executable_path) if is_using_bsdinit(): bsdinit(service_name, service_desc, executable_path, executable_args) else: From 45e878573d222488a8a0f79763bbdafc13cf38b7 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 18 Feb 2024 19:25:42 +0000 Subject: [PATCH 4/5] bsd bash to sh --- implants/lib/eldritch/src/sys/shell_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implants/lib/eldritch/src/sys/shell_impl.rs b/implants/lib/eldritch/src/sys/shell_impl.rs index 4a5abbacf..480f4782d 100644 --- a/implants/lib/eldritch/src/sys/shell_impl.rs +++ b/implants/lib/eldritch/src/sys/shell_impl.rs @@ -33,7 +33,7 @@ fn handle_shell(cmd: String) -> Result { command_args = ["/c", cmd.as_str()].to_vec(); } else { // linux and such - command_string = "bash"; + command_string = "sh"; command_args = ["-c", cmd.as_str()].to_vec(); } From 71f2e1d67c0b01a4868d5985e7053b57bff96e29 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 18 Feb 2024 19:29:17 +0000 Subject: [PATCH 5/5] Swap to shell --- implants/imix/install_scripts/install_service/main.eldritch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/implants/imix/install_scripts/install_service/main.eldritch b/implants/imix/install_scripts/install_service/main.eldritch index 88dc0bd0b..edfcb0c7a 100644 --- a/implants/imix/install_scripts/install_service/main.eldritch +++ b/implants/imix/install_scripts/install_service/main.eldritch @@ -278,9 +278,9 @@ def bsdinit(service_name, service_desc, executable_path, executable_args): file.template("/tmp/svc.sh.j2",startup_dir+service_name+".sh", args, False) file.remove("/tmp/svc.sh.j2") - sys.exec("/bin/sh",["-c","chmod +x "+startup_dir+service_name+".sh"]) - sys.exec("/bin/sh",["-c","chmod +x "+executable_path]) - sys.exec("/bin/sh",["-c","service "+service_name+".sh start"]) + sys.shell("chmod +x "+startup_dir+service_name+".sh") + sys.shell("chmod +x "+executable_path) + sys.shell("service "+service_name+".sh start") print("bsdinit installed")