From e133c085a71b3688c439f2dd7ba56a39eb74d66a Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Wed, 12 Mar 2025 01:50:20 +0000 Subject: [PATCH 1/5] Return to stderr instead of failing --- docs/_docs/user-guide/eldritch.md | 9 +++++---- implants/lib/eldritch/src/pivot/ssh_exec_impl.rs | 16 ++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 08b348c96..622fd96bd 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -565,14 +565,15 @@ The file directory the `dst` file exists in must exist in order for ssh_copy to `pivot.ssh_exec(target: str, port: int, command: str, username: str, password: Optional, key: Optional, key_password: Optional, timeout: Optional) -> List` -The pivot.ssh_exec method executes a command string on the remote host using the default shell. If no password or key is specified the function will error out with: -`Failed to run handle_ssh_exec: Failed to authenticate to host` -If the connection is successful but the command fails no output will be returned but the status code will be set. -Not returning stderr is a limitation of the way we're performing execution. Since it's not using the SSH shell directive we're limited on the return output we can capture. +The pivot.ssh_exec method executes a command string on the remote host using the default shell. +Stdout returns the string result from the command output. +Stderr will return any errors from the SSH connection but not the command being executed. +Status will be equal to the code returned by the command being run and -1 in the event that the ssh connection raises an error. ```json { "stdout": "uid=1000(kali) gid=1000(kali) groups=1000(kali),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),118(bluetooth),128(lpadmin),132(scanner),143(docker)\n", + "stderr":"", "status": 0 } ``` diff --git a/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs b/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs index b00d6549c..299370a33 100644 --- a/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs +++ b/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs @@ -63,7 +63,7 @@ pub fn ssh_exec( let key_password_ref = key_password.as_deref(); let local_port: u16 = port.try_into()?; - let cmd_res = match runtime.block_on(handle_ssh_exec( + let (out, status, err) = match runtime.block_on(handle_ssh_exec( target, local_port, command, @@ -73,19 +73,15 @@ pub fn ssh_exec( key_password_ref, timeout, )) { - Ok(local_res) => local_res, - Err(local_err) => { - return Err(anyhow::anyhow!( - "Failed to run handle_ssh_exec: {}", - local_err.to_string() - )) - } + Ok(local_res) => (local_res.stdout, local_res.status, String::from("")), + Err(local_err) => (String::from(""), -1, local_err.to_string()) }; let res = SmallMap::new(); let mut dict_res = Dict::new(res); - insert_dict_kv!(dict_res, starlark_heap, "stdout", &cmd_res.stdout, String); - insert_dict_kv!(dict_res, starlark_heap, "status", cmd_res.status, i32); + insert_dict_kv!(dict_res, starlark_heap, "stdout", &out, String); + insert_dict_kv!(dict_res, starlark_heap, "stderr", &err, String); + insert_dict_kv!(dict_res, starlark_heap, "status", status, i32); Ok(dict_res) } From b2798300b6ed2f659f9bd83ed006ea1b341b8077 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Wed, 12 Mar 2025 01:56:02 +0000 Subject: [PATCH 2/5] Update ssh_copy too --- docs/_docs/user-guide/eldritch.md | 10 +++++----- implants/lib/eldritch/src/pivot/mod.rs | 5 ++--- implants/lib/eldritch/src/pivot/ssh_copy_impl.rs | 9 +++------ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 622fd96bd..d1895e4aa 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -552,15 +552,15 @@ The pivot.smb_exec method is being proposed to allow users a way to move ### pivot.ssh_copy -`pivot.ssh_copy(target: str, port: int, src: str, dst: str, username: str, password: Optional, key: Optional, key_password: Optional, timeout: Optional) -> None` +`pivot.ssh_copy(target: str, port: int, src: str, dst: str, username: str, password: Optional, key: Optional, key_password: Optional, timeout: Optional) -> str` -The pivot.ssh_copy method copies a local file to a remote system. If no password or key is specified the function will error out with: -`Failed to run handle_ssh_exec: Failed to authenticate to host` +The pivot.ssh_copy method copies a local file to a remote system. +ssh_copy will return `"Sucess"` if successful and `"Failed to run handle_ssh_copy: ..."` on failure. If the connection is successful but the copy writes a file error will be returned. - -ssh_copy will first delete the remote file and then write to its location. +ssh_copy will overwrite the remote file if it exists. The file directory the `dst` file exists in must exist in order for ssh_copy to work. + ### pivot.ssh_exec `pivot.ssh_exec(target: str, port: int, command: str, username: str, password: Optional, key: Optional, key_password: Optional, timeout: Optional) -> List` diff --git a/implants/lib/eldritch/src/pivot/mod.rs b/implants/lib/eldritch/src/pivot/mod.rs index 6cf790ff5..b94374ed3 100644 --- a/implants/lib/eldritch/src/pivot/mod.rs +++ b/implants/lib/eldritch/src/pivot/mod.rs @@ -48,9 +48,8 @@ fn methods(builder: &mut MethodsBuilder) { } #[allow(unused_variables)] - fn ssh_copy<'v>(this: &PivotLibrary, target: String, port: i32, src: String, dst: String, username: String, password: Option, key: Option, key_password: Option, timeout: Option) -> anyhow::Result { - ssh_copy_impl::ssh_copy(target, port, src, dst, username, password, key, key_password, timeout)?; - Ok(NoneType{}) + fn ssh_copy<'v>(this: &PivotLibrary, target: String, port: i32, src: String, dst: String, username: String, password: Option, key: Option, key_password: Option, timeout: Option) -> anyhow::Result { + ssh_copy_impl::ssh_copy(target, port, src, dst, username, password, key, key_password, timeout) } #[allow(unused_variables)] diff --git a/implants/lib/eldritch/src/pivot/ssh_copy_impl.rs b/implants/lib/eldritch/src/pivot/ssh_copy_impl.rs index e426295dd..3d0250212 100644 --- a/implants/lib/eldritch/src/pivot/ssh_copy_impl.rs +++ b/implants/lib/eldritch/src/pivot/ssh_copy_impl.rs @@ -42,7 +42,7 @@ pub fn ssh_copy( key: Option, key_password: Option, timeout: Option, -) -> Result<()> { +) -> Result { let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() .build()?; @@ -63,14 +63,11 @@ pub fn ssh_copy( )) { Ok(local_res) => local_res, Err(local_err) => { - return Err(anyhow::anyhow!( - "Failed to run handle_ssh_exec: {}", - local_err.to_string() - )) + return Ok(format!("Failed to run handle_ssh_copy: {}", local_err)); } }; - Ok(()) + Ok("Sucess".to_string()) } #[cfg(test)] From 86e5a5e11aaf32d80b2b50982206a1ff8b50ecff Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Wed, 12 Mar 2025 02:00:37 +0000 Subject: [PATCH 3/5] Remove password spayrer --- implants/lib/eldritch/src/pivot/mod.rs | 6 ------ .../eldritch/src/pivot/ssh_password_spray_impl.rs | 12 ------------ implants/lib/eldritch/src/runtime/mod.rs | 2 +- 3 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 implants/lib/eldritch/src/pivot/ssh_password_spray_impl.rs diff --git a/implants/lib/eldritch/src/pivot/mod.rs b/implants/lib/eldritch/src/pivot/mod.rs index b94374ed3..eb8457bcc 100644 --- a/implants/lib/eldritch/src/pivot/mod.rs +++ b/implants/lib/eldritch/src/pivot/mod.rs @@ -7,7 +7,6 @@ mod reverse_shell_pty_impl; mod smb_exec_impl; mod ssh_copy_impl; mod ssh_exec_impl; -mod ssh_password_spray_impl; use anyhow::Result; use async_trait::async_trait; @@ -52,11 +51,6 @@ fn methods(builder: &mut MethodsBuilder) { ssh_copy_impl::ssh_copy(target, port, src, dst, username, password, key, key_password, timeout) } - #[allow(unused_variables)] - fn ssh_password_spray(this: &PivotLibrary, targets: UnpackList, port: i32, credentials: UnpackList, keys: UnpackList, command: String, shell_path: String) -> anyhow::Result { - ssh_password_spray_impl::ssh_password_spray(targets.items, port, credentials.items, keys.items, command, shell_path) - } - #[allow(unused_variables)] fn smb_exec(this: &PivotLibrary, target: String, port: i32, username: String, password: String, hash: String, command: String) -> anyhow::Result { smb_exec_impl::smb_exec(target, port, username, password, hash, command) diff --git a/implants/lib/eldritch/src/pivot/ssh_password_spray_impl.rs b/implants/lib/eldritch/src/pivot/ssh_password_spray_impl.rs deleted file mode 100644 index c0ad7a2dc..000000000 --- a/implants/lib/eldritch/src/pivot/ssh_password_spray_impl.rs +++ /dev/null @@ -1,12 +0,0 @@ -use anyhow::Result; - -pub fn ssh_password_spray( - _targets: Vec, - _port: i32, - _credentials: Vec, - _keys: Vec, - _command: String, - _shell_path: String, -) -> Result { - unimplemented!("Method unimplemented") -} diff --git a/implants/lib/eldritch/src/runtime/mod.rs b/implants/lib/eldritch/src/runtime/mod.rs index 99792a2be..483024cf7 100644 --- a/implants/lib/eldritch/src/runtime/mod.rs +++ b/implants/lib/eldritch/src/runtime/mod.rs @@ -119,7 +119,7 @@ mod tests { parameters: HashMap::new(), file_names: Vec::new(), }, - want_text: format!("{}\n", r#"["arp_scan", "bind_proxy", "ncat", "port_forward", "port_scan", "reverse_shell_pty", "smb_exec", "ssh_copy", "ssh_exec", "ssh_password_spray"]"#), + want_text: format!("{}\n", r#"["arp_scan", "bind_proxy", "ncat", "port_forward", "port_scan", "reverse_shell_pty", "smb_exec", "ssh_copy", "ssh_exec"]"#), want_error: None, }, assets_bindings: TestCase { From 27fcf2bbd758d3b006b15849886b5ac34f9f8739 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Wed, 12 Mar 2025 02:06:12 +0000 Subject: [PATCH 4/5] Format --- implants/lib/eldritch/src/pivot/ssh_exec_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs b/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs index 299370a33..3ac3f4ad7 100644 --- a/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs +++ b/implants/lib/eldritch/src/pivot/ssh_exec_impl.rs @@ -74,7 +74,7 @@ pub fn ssh_exec( timeout, )) { Ok(local_res) => (local_res.stdout, local_res.status, String::from("")), - Err(local_err) => (String::from(""), -1, local_err.to_string()) + Err(local_err) => (String::from(""), -1, local_err.to_string()), }; let res = SmallMap::new(); From ba1dd80538c07d320ef841e04a5a321f1bcfdd4e Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:35:28 +0000 Subject: [PATCH 5/5] Remove docs --- docs/_docs/user-guide/eldritch.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index d1895e4aa..82990ccb8 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -578,12 +578,6 @@ Status will be equal to the code returned by the command being run and -1 in the } ``` -### pivot.ssh_password_spray - -`pivot.ssh_password_spray(targets: List, port: int, credentials: List, keys: List, command: str, shell_path: str) -> List` - -The pivot.ssh_password_spray method is being proposed to allow users a way to test found credentials against neighboring targets. It will iterate over the targets list and try each credential set. Credentials will be a formatted list of usernames and passwords Eg. "username:password". The function will return a formatted list of "target:username:password". command and shell_path is intended to give more flexibility but may be adding complexity. - --- ## Process