From 0af3e6af24ab59d7931ca268a4e51608914e5297 Mon Sep 17 00:00:00 2001 From: hulto <7121375+hulto@users.noreply.github.com> Date: Tue, 20 Feb 2024 21:19:35 -0800 Subject: [PATCH 01/10] Add new tome --- tavern/tomes/get_registry/main.eldritch | 15 +++++++++++++++ tavern/tomes/get_registry/metadata.yml | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tavern/tomes/get_registry/main.eldritch create mode 100644 tavern/tomes/get_registry/metadata.yml diff --git a/tavern/tomes/get_registry/main.eldritch b/tavern/tomes/get_registry/main.eldritch new file mode 100644 index 000000000..0bfd13cb1 --- /dev/null +++ b/tavern/tomes/get_registry/main.eldritch @@ -0,0 +1,15 @@ +def pad_key(key, max_len): + res = key+" "*(max_len-len(key)) + return res + +def get_registry(hive, path): + res = sys.get_reg(hive, path) + max_len = max([ len(i) for i in res.keys()]) + for k in res: + v = res[k] + pk = pad_key(k,max_len) + print(f"{pk} : {v}") + +get_registry(input_params['hive'], input_params['path']) +print() + diff --git a/tavern/tomes/get_registry/metadata.yml b/tavern/tomes/get_registry/metadata.yml new file mode 100644 index 000000000..525c6b5c9 --- /dev/null +++ b/tavern/tomes/get_registry/metadata.yml @@ -0,0 +1,14 @@ +name: Get Registry +description: List the subkeys and their values at a provided hive and path. +author: hulto +support_model: FIRST_PARTY +tactic: RECON +paramdefs: +- name: hive + type: string + label: Registry hive + placeholder: "HKEY_LOCAL_MACHINE" +- name: path + type: string + label: Registry key path + placeholder: "SOFTWARE\\Microsoft\\Windows\\CurrentVersion" # Single backslash can be used too but you may encounter issues if you specify "\x64" as that's hex. From 2380616ece7b18d085c7fbdc087849bff40a08a7 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 00:33:00 +0000 Subject: [PATCH 02/10] Implement, test, docs --- docs/_docs/user-guide/eldritch.md | 6 ++ implants/lib/eldritch/src/file/mod.rs | 5 ++ .../lib/eldritch/src/file/parent_dir_impl.rs | 69 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 implants/lib/eldritch/src/file/parent_dir_impl.rs diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 2132a69be..dd4779872 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -346,6 +346,12 @@ The file.mkdir method will make a new directory at `path`. If the parent The file.moveto method moves a file or directory from `src` to `dst`. If the `dst` directory or file exists it will be deleted before being replaced to ensure consistency across systems. +### file.parent_dir + +`file.parent_dir(path: str) -> str` + +The file.parent_dir method returns the parent directory of a give path. Eg `/etc/ssh/sshd_config` -> `/etc/ssh` + ### file.read `file.read(path: str) -> str` diff --git a/implants/lib/eldritch/src/file/mod.rs b/implants/lib/eldritch/src/file/mod.rs index c7b1a6d99..a02cfbe8e 100644 --- a/implants/lib/eldritch/src/file/mod.rs +++ b/implants/lib/eldritch/src/file/mod.rs @@ -9,6 +9,7 @@ mod is_file_impl; mod list_impl; mod mkdir_impl; mod moveto_impl; +mod parent_dir_impl; mod read_impl; mod remove_impl; mod replace_all_impl; @@ -130,6 +131,10 @@ fn methods(builder: &mut MethodsBuilder) { moveto_impl::moveto(old, new)?; Ok(NoneType{}) } + #[allow(unused_variables)] + fn parent_dir(this: &FileLibrary, path: String) -> anyhow::Result { + parent_dir_impl::parent_dir(path) + } #[allow(unused_variables)] fn replace_all(this: &FileLibrary, path: String, pattern: String, value: String) -> anyhow::Result { diff --git a/implants/lib/eldritch/src/file/parent_dir_impl.rs b/implants/lib/eldritch/src/file/parent_dir_impl.rs new file mode 100644 index 000000000..79be18f07 --- /dev/null +++ b/implants/lib/eldritch/src/file/parent_dir_impl.rs @@ -0,0 +1,69 @@ +use anyhow::{Context, Result}; +use std::path::PathBuf; + +pub fn parent_dir(path: String) -> Result { + let mut res = PathBuf::from(&path); + res.pop(); + Ok(res + .to_str() + .context("Failed to convert to str")? + .to_string()) +} + +#[cfg(test)] +mod test { + use crate::runtime::Message; + use pb::eldritch::Tome; + use std::collections::HashMap; + + macro_rules! test_cases { + ($($name:ident: $value:expr,)*) => { + $( + #[tokio::test] + async fn $name() { + let tc: TestCase = $value; + + // Run Eldritch (until finished) + let mut runtime = crate::start(tc.id, tc.tome).await; + runtime.finish().await; + + // Read Messages + let mut found = false; + for msg in runtime.messages() { + if let Message::ReportText(m) = msg { + assert_eq!(tc.id, m.id); + assert_eq!(tc.want_text, m.text); + found = true; + } + } + assert!(found); + } + )* + } + } + + struct TestCase { + pub id: i64, + pub tome: Tome, + pub want_text: String, + } + + + test_cases! { + simple_ssh: TestCase{ + id: 123, + tome: Tome{ + eldritch: String::from(r#"print(file.parent_dir(input_params['path']))"#), + #[cfg(target_os="linux")] + parameters: HashMap::from([(String::from("path"),String::from("/etc/ssh/sshd_config"))]), + #[cfg(target_os="windows")] + parameters: HashMap::from([(String::from("path"),String::from("C:\\ProgramData\\ssh\\sshd_config"))]), + file_names: Vec::new(), + }, + #[cfg(target_os="linux")] + want_text: String::from("/etc/ssh\n"), + #[cfg(target_os="windows")] + want_text: String::from("C:\\ProgramData\\ssh\n"), + }, + } +} From a9ecad45e2e449a5d12d08e33cd64ab18487921b Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 00:44:31 +0000 Subject: [PATCH 03/10] Fix mod test. --- implants/lib/eldritch/src/runtime/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implants/lib/eldritch/src/runtime/mod.rs b/implants/lib/eldritch/src/runtime/mod.rs index 1a8d7b449..1c6bd7b1a 100644 --- a/implants/lib/eldritch/src/runtime/mod.rs +++ b/implants/lib/eldritch/src/runtime/mod.rs @@ -89,7 +89,7 @@ mod tests { parameters: HashMap::new(), file_names: Vec::new(), }, - want_text: format!("{}\n", r#"["append", "compress", "copy", "exists", "find", "follow", "is_dir", "is_file", "list", "mkdir", "moveto", "read", "remove", "replace", "replace_all", "template", "timestomp", "write"]"#), + want_text: format!("{}\n", r#"["append", "compress", "copy", "exists", "find", "follow", "is_dir", "is_file", "list", "mkdir", "moveto", "parent_dir", "read", "remove", "replace", "replace_all", "template", "timestomp", "write"]"#), want_error: None, }, process_bindings: TestCase { From 34c67c8b6fdfd4ca4d170f82bcf1ba71fb3b6428 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 01:04:07 +0000 Subject: [PATCH 04/10] fix for osx --- implants/lib/eldritch/src/file/parent_dir_impl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implants/lib/eldritch/src/file/parent_dir_impl.rs b/implants/lib/eldritch/src/file/parent_dir_impl.rs index 79be18f07..8a15db5a0 100644 --- a/implants/lib/eldritch/src/file/parent_dir_impl.rs +++ b/implants/lib/eldritch/src/file/parent_dir_impl.rs @@ -54,13 +54,13 @@ mod test { id: 123, tome: Tome{ eldritch: String::from(r#"print(file.parent_dir(input_params['path']))"#), - #[cfg(target_os="linux")] + #[cfg(not(target_os="windows"))] parameters: HashMap::from([(String::from("path"),String::from("/etc/ssh/sshd_config"))]), #[cfg(target_os="windows")] parameters: HashMap::from([(String::from("path"),String::from("C:\\ProgramData\\ssh\\sshd_config"))]), file_names: Vec::new(), }, - #[cfg(target_os="linux")] + #[cfg(not(target_os="windows"))] want_text: String::from("/etc/ssh\n"), #[cfg(target_os="windows")] want_text: String::from("C:\\ProgramData\\ssh\n"), From 5ef38e128c83a64304c23ed8ac01cfe331eb0052 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 01:45:07 +0000 Subject: [PATCH 05/10] Implement download tome --- tavern/tomes/download/main.eldritch | 14 ++++++++++++++ tavern/tomes/download/metadata.yml | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tavern/tomes/download/main.eldritch create mode 100644 tavern/tomes/download/metadata.yml diff --git a/tavern/tomes/download/main.eldritch b/tavern/tomes/download/main.eldritch new file mode 100644 index 000000000..b848fb486 --- /dev/null +++ b/tavern/tomes/download/main.eldritch @@ -0,0 +1,14 @@ +def download_file(remote_src, local_dst, insecure): + if file.is_dir(file.parent_dir(local_dst)): + if file.is_file(local_dst): + file.remove(local_dst) + http.download(remote_src, local_dst, insecure) + else: + eprint("Specified path has no parent directory") + + +download_file( + input_params['url'], + input_params['dst'], + input_params['insecure'] +) diff --git a/tavern/tomes/download/metadata.yml b/tavern/tomes/download/metadata.yml new file mode 100644 index 000000000..9e271f0ff --- /dev/null +++ b/tavern/tomes/download/metadata.yml @@ -0,0 +1,14 @@ +name: Download and execute +description: Download a file and execute it. If possible background and disown the process. +author: hulto +support_model: FIRST_PARTY +tactic: EXECUTION +paramdefs: +- name: url + type: string + label: Remote file's URL + placeholder: "https://example.com/executable_file" +- name: dst + type: string + label: Agent side file path (not directory) + placeholder: "/tmp/my_file - C:\ProgramData\MyFile.txt" From 9df435031117dc42acd3d76681502bb51cee1011 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 01:57:44 +0000 Subject: [PATCH 06/10] Fix bool cast --- tavern/tomes/download/main.eldritch | 24 +++++++++++++++++++----- tavern/tomes/download/metadata.yml | 4 ++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/tavern/tomes/download/main.eldritch b/tavern/tomes/download/main.eldritch index b848fb486..8b79fac47 100644 --- a/tavern/tomes/download/main.eldritch +++ b/tavern/tomes/download/main.eldritch @@ -6,9 +6,23 @@ def download_file(remote_src, local_dst, insecure): else: eprint("Specified path has no parent directory") +def cast_bool(bool_str) + if input_params['insecure'].lower() == "true" + return True + else if input_params['insecure'].lower() == "false" + return False + else: + return -1 + eprint("Only true or false are accepted for input_params") + + +def main(): + insecure = cast_bool(input_params['insecure']) + if insecure == -1: + return -download_file( - input_params['url'], - input_params['dst'], - input_params['insecure'] -) + download_file( + input_params['url'], + input_params['dst'], + insecure + ) diff --git a/tavern/tomes/download/metadata.yml b/tavern/tomes/download/metadata.yml index 9e271f0ff..fb21400c5 100644 --- a/tavern/tomes/download/metadata.yml +++ b/tavern/tomes/download/metadata.yml @@ -12,3 +12,7 @@ paramdefs: type: string label: Agent side file path (not directory) placeholder: "/tmp/my_file - C:\ProgramData\MyFile.txt" +- name: insecure + type: bool + label: Should TLS certificates be ignored + placeholder: "False" From 022662d302838f8dcd41b5b0e67a2a94aa65d49a Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 01:57:51 +0000 Subject: [PATCH 07/10] Update meta --- tavern/tomes/download/metadata.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tavern/tomes/download/metadata.yml b/tavern/tomes/download/metadata.yml index fb21400c5..4cd667017 100644 --- a/tavern/tomes/download/metadata.yml +++ b/tavern/tomes/download/metadata.yml @@ -1,5 +1,5 @@ -name: Download and execute -description: Download a file and execute it. If possible background and disown the process. +name: Download +description: Download a file author: hulto support_model: FIRST_PARTY tactic: EXECUTION From 1602e6be5bb821855e3b4e59f702f1eb573d7f73 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Fri, 23 Feb 2024 21:48:58 -0500 Subject: [PATCH 08/10] Fix yaml test --- tavern/tomes/download/metadata.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tavern/tomes/download/metadata.yml b/tavern/tomes/download/metadata.yml index 4cd667017..25511cde5 100644 --- a/tavern/tomes/download/metadata.yml +++ b/tavern/tomes/download/metadata.yml @@ -11,7 +11,7 @@ paramdefs: - name: dst type: string label: Agent side file path (not directory) - placeholder: "/tmp/my_file - C:\ProgramData\MyFile.txt" + placeholder: /tmp/my_file - C:\ProgramData\MyFile.txt - name: insecure type: bool label: Should TLS certificates be ignored From 7aac3f9bb0743bfdec4dac79dbf551e0a607f68d Mon Sep 17 00:00:00 2001 From: hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:23:11 +0000 Subject: [PATCH 09/10] Resolve feedback --- tavern/tomes/download/main.eldritch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tavern/tomes/download/main.eldritch b/tavern/tomes/download/main.eldritch index 8b79fac47..3f405dd56 100644 --- a/tavern/tomes/download/main.eldritch +++ b/tavern/tomes/download/main.eldritch @@ -12,8 +12,8 @@ def cast_bool(bool_str) else if input_params['insecure'].lower() == "false" return False else: - return -1 eprint("Only true or false are accepted for input_params") + return -1 def main(): From d0c191e5a30a1a62441798260fb33b3a111df971 Mon Sep 17 00:00:00 2001 From: hulto <7121375+hulto@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:25:47 +0000 Subject: [PATCH 10/10] Streamline bool cast --- tavern/tomes/download/main.eldritch | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tavern/tomes/download/main.eldritch b/tavern/tomes/download/main.eldritch index 3f405dd56..a53cd4582 100644 --- a/tavern/tomes/download/main.eldritch +++ b/tavern/tomes/download/main.eldritch @@ -6,23 +6,13 @@ def download_file(remote_src, local_dst, insecure): else: eprint("Specified path has no parent directory") -def cast_bool(bool_str) - if input_params['insecure'].lower() == "true" - return True - else if input_params['insecure'].lower() == "false" - return False - else: - eprint("Only true or false are accepted for input_params") - return -1 - - def main(): - insecure = cast_bool(input_params['insecure']) + insecure = cast_bool() if insecure == -1: return download_file( input_params['url'], input_params['dst'], - insecure + input_params['insecure'].lower() == "true" )