From 23d98c3062e295127d93fd143e165ab857e15169 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 10 Mar 2024 05:11:43 +0000 Subject: [PATCH 1/9] We support proxies woot --- docs/_docs/user-guide/imix.md | 11 +++++++ implants/imix/src/agent.rs | 2 +- implants/imix/src/config.rs | 40 ++++++++++++++++++++++++- implants/lib/transport/Cargo.toml | 6 ++-- implants/lib/transport/src/grpc.rs | 31 ++++++++++++++++--- implants/lib/transport/src/mock.rs | 2 +- implants/lib/transport/src/transport.rs | 2 +- 7 files changed, 84 insertions(+), 10 deletions(-) diff --git a/docs/_docs/user-guide/imix.md b/docs/_docs/user-guide/imix.md index b06b63e66..49e12eb51 100644 --- a/docs/_docs/user-guide/imix.md +++ b/docs/_docs/user-guide/imix.md @@ -18,6 +18,7 @@ Imix has compile-time configuration, that may be specified using environment var | IMIX_CALLBACK_URI | URI for initial callbacks (must specify a scheme, e.g. `http://`) | `http://127.0.0.1:80` | No | | IMIX_CALLBACK_INTERVAL | Duration between callbacks, in seconds. | `5` | No | | IMIX_RETRY_INTERVAL | Duration to wait before restarting the agent loop if an error occurs, in seconds. | `5` | No | +| IMIX_PROXY_URI | Overide system settings for proxy URI over HTTP(S) (must specify a scheme, e.g. `https://`) | `http://127.0.0.1:1080` | No | ## Logging @@ -43,6 +44,16 @@ See the [Eldritch User Guide](/user-guide/eldritch) for more information. Imix can execute up to 127 threads concurrently after that the main imix thread will block behind other threads. Every callback interval imix will query each active thread for new output and rely that back to the c2. This means even long running tasks will report their status as new data comes in. +## Proxy support + +Imix's default `grpc` transport supports http and https proxies for outbound communication. +By default imix will try to determine the systems proxy settings: + +- On Linux reading the environment variables `http_proxy` and then `https_proxy` +- On Windows - we cannot automatically determine the default proxy +- On MacOS - we cannot automatically determine the default proxy +- On FreeBSD - we cannot automatically determine the default proxy + ## Static cross compilation ### Linux diff --git a/implants/imix/src/agent.rs b/implants/imix/src/agent.rs index de64ba1cc..2b29c7767 100644 --- a/implants/imix/src/agent.rs +++ b/implants/imix/src/agent.rs @@ -77,7 +77,7 @@ impl Agent { * Callback once using the configured client to claim new tasks and report available output. */ pub async fn callback(&mut self) -> Result<()> { - let transport = GRPC::new(self.cfg.callback_uri.clone())?; + let transport = GRPC::new(self.cfg.callback_uri.clone(), self.cfg.proxy_uri.clone())?; self.claim_tasks(transport.clone()).await?; self.report(transport.clone()).await?; diff --git a/implants/imix/src/config.rs b/implants/imix/src/config.rs index 4f972d3c9..8e2e222c2 100644 --- a/implants/imix/src/config.rs +++ b/implants/imix/src/config.rs @@ -54,6 +54,7 @@ pub const RETRY_INTERVAL: &str = retry_interval!(); pub struct Config { pub info: pb::c2::Beacon, pub callback_uri: String, + pub proxy_uri: Option, pub retry_interval: u64, } @@ -92,6 +93,7 @@ impl Default for Config { Config { info, callback_uri: String::from(CALLBACK_URI), + proxy_uri: get_system_proxy(), retry_interval: match RETRY_INTERVAL.parse::() { Ok(i) => i, Err(_err) => { @@ -100,13 +102,49 @@ impl Default for Config { "failed to parse retry interval constant, defaulting to 5 seconds: {_err}" ); - 5_u64 + 5 } }, } } } +fn get_system_proxy() -> Option { + #[cfg(target_os = "linux")] + { + match std::env::var("http_proxy") { + Ok(val) => return Some(val), + Err(_e) => { + #[cfg(debug_assertions)] + log::debug!("Didn't find http_proxy env var: {}", _e); + } + } + + match std::env::var("https_proxy") { + Ok(val) => return Some(val), + Err(_e) => { + #[cfg(debug_assertions)] + log::debug!("Didn't find https_proxy env var: {}", _e); + } + } + return None; + } + #[cfg(target_os = "windows")] + { + return None; + } + #[cfg(target_os = "macos")] + { + return None; + } + #[cfg(target_os = "freebsd")] + { + return None; + } + + todo!() +} + /* * Returns which Platform imix has been compiled for. */ diff --git a/implants/lib/transport/Cargo.toml b/implants/lib/transport/Cargo.toml index 4db51f8d8..21cdb06c6 100644 --- a/implants/lib/transport/Cargo.toml +++ b/implants/lib/transport/Cargo.toml @@ -13,12 +13,14 @@ pb = { workspace = true } anyhow = { workspace = true } log = { workspace = true } -prost = { workspace = true} +prost = { workspace = true } prost-types = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tokio-stream = { workspace = true } tonic = { workspace = true, features = ["tls-roots"] } trait-variant = { workspace = true } +hyper = { version = "0.14", features = ["client"] } +hyper-proxy = "0.9.1" # [feature = mock] -mockall = {workspace = true, optional = true } +mockall = { workspace = true, optional = true } diff --git a/implants/lib/transport/src/grpc.rs b/implants/lib/transport/src/grpc.rs index 577b298de..3baab7230 100644 --- a/implants/lib/transport/src/grpc.rs +++ b/implants/lib/transport/src/grpc.rs @@ -1,6 +1,11 @@ use crate::Transport; use anyhow::Result; +use hyper::client::HttpConnector; +use hyper::service::service_fn; +use hyper::Uri; use pb::c2::*; +use std::borrow::Borrow; +use std::str::FromStr; use std::sync::mpsc::{Receiver, Sender}; use tonic::codec::ProstCodec; use tonic::GrpcMethod; @@ -21,12 +26,30 @@ pub struct GRPC { } impl Transport for GRPC { - fn new(callback: String) -> Result { + fn new(callback: String, proxy_uri: Option) -> Result { let endpoint = tonic::transport::Endpoint::from_shared(callback)?; - let channel = endpoint - .rate_limit(1, Duration::from_millis(25)) - .connect_lazy(); + let mut http = hyper::client::HttpConnector::new(); + http.enforce_http(false); + http.set_nodelay(true); + + let channel = match proxy_uri { + Some(proxy_uri_string) => { + let proxy: hyper_proxy::Proxy = hyper_proxy::Proxy::new( + hyper_proxy::Intercept::All, + Uri::from_str(proxy_uri_string.as_str())?, + ); + let mut proxy_connector = hyper_proxy::ProxyConnector::from_proxy(http, proxy)?; + proxy_connector.set_tls(None); + + endpoint + .rate_limit(1, Duration::from_millis(25)) + .connect_with_connector_lazy(proxy_connector) + } + None => endpoint + .rate_limit(1, Duration::from_millis(25)) + .connect_lazy(), + }; let grpc = tonic::client::Grpc::new(channel); Ok(Self { grpc }) diff --git a/implants/lib/transport/src/mock.rs b/implants/lib/transport/src/mock.rs index b429178d6..5f3624ff1 100644 --- a/implants/lib/transport/src/mock.rs +++ b/implants/lib/transport/src/mock.rs @@ -10,7 +10,7 @@ mock! { fn clone(&self) -> Self; } impl super::Transport for Transport { - fn new(uri: String) -> Result; + fn new(uri: String, proxy_uri: Option) -> Result; async fn claim_tasks(&mut self, request: ClaimTasksRequest) -> Result; diff --git a/implants/lib/transport/src/transport.rs b/implants/lib/transport/src/transport.rs index 32577f31f..8271e6402 100644 --- a/implants/lib/transport/src/transport.rs +++ b/implants/lib/transport/src/transport.rs @@ -5,7 +5,7 @@ use std::sync::mpsc::{Receiver, Sender}; #[trait_variant::make(Transport: Send)] pub trait UnsafeTransport: Clone + Send { // New will initialize a new instance of the transport using the provided URI. - fn new(uri: String) -> Result; + fn new(uri: String, proxy_uri: Option) -> Result; /// /// Contact the server for new tasks to execute. From e667d299f0515902a3ca982874cad7042aa3caf5 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 10 Mar 2024 05:23:35 +0000 Subject: [PATCH 2/9] Actually include override. --- implants/imix/src/config.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/implants/imix/src/config.rs b/implants/imix/src/config.rs index 8e2e222c2..92d51cee0 100644 --- a/implants/imix/src/config.rs +++ b/implants/imix/src/config.rs @@ -15,6 +15,17 @@ macro_rules! callback_uri { } }; } + +/* + * Compile-time constant for the agent proxy URI, derived from the IMIX_PROXY_URI environment variable during compilation. + * Defaults to None if this is unset. + */ +macro_rules! proxy_uri { + () => { + option_env!("IMIX_PROXY_URI") + }; +} + /* * Compile-time constant for the agent callback URI, derived from the IMIX_CALLBACK_URI environment variable during compilation. * Defaults to "http://127.0.0.1:80/grpc" if this is unset. @@ -110,6 +121,11 @@ impl Default for Config { } fn get_system_proxy() -> Option { + let proxy_uri_compile_time_override = proxy_uri!(); + if let Some(proxy_uri) = proxy_uri_compile_time_override { + return Some(proxy_uri.to_string()); + } + #[cfg(target_os = "linux")] { match std::env::var("http_proxy") { @@ -141,8 +157,6 @@ fn get_system_proxy() -> Option { { return None; } - - todo!() } /* From 9d53ed451fcc565e25ec00a7bc499595060133e1 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 10 Mar 2024 05:26:51 +0000 Subject: [PATCH 3/9] Cleanup --- implants/imix/src/config.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/implants/imix/src/config.rs b/implants/imix/src/config.rs index 92d51cee0..cf75e97df 100644 --- a/implants/imix/src/config.rs +++ b/implants/imix/src/config.rs @@ -143,19 +143,19 @@ fn get_system_proxy() -> Option { log::debug!("Didn't find https_proxy env var: {}", _e); } } - return None; + None } #[cfg(target_os = "windows")] { - return None; + None } #[cfg(target_os = "macos")] { - return None; + None } #[cfg(target_os = "freebsd")] { - return None; + None } } From 3d089f8fe6ac466485b54380830f55cb35397056 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 10 Mar 2024 05:28:40 +0000 Subject: [PATCH 4/9] Fix docs. --- docs/_docs/user-guide/imix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/user-guide/imix.md b/docs/_docs/user-guide/imix.md index 49e12eb51..e0942ebe0 100644 --- a/docs/_docs/user-guide/imix.md +++ b/docs/_docs/user-guide/imix.md @@ -18,7 +18,7 @@ Imix has compile-time configuration, that may be specified using environment var | IMIX_CALLBACK_URI | URI for initial callbacks (must specify a scheme, e.g. `http://`) | `http://127.0.0.1:80` | No | | IMIX_CALLBACK_INTERVAL | Duration between callbacks, in seconds. | `5` | No | | IMIX_RETRY_INTERVAL | Duration to wait before restarting the agent loop if an error occurs, in seconds. | `5` | No | -| IMIX_PROXY_URI | Overide system settings for proxy URI over HTTP(S) (must specify a scheme, e.g. `https://`) | `http://127.0.0.1:1080` | No | +| IMIX_PROXY_URI | Overide system settings for proxy URI over HTTP(S) (must specify a scheme, e.g. `https://`) | No proxy | No | ## Logging From 501c72462087df65ff30fdd04918c0fe8c13e106 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 10 Mar 2024 20:04:58 +0000 Subject: [PATCH 5/9] Fix lint --- implants/lib/transport/src/grpc.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/implants/lib/transport/src/grpc.rs b/implants/lib/transport/src/grpc.rs index 3baab7230..78fa2b681 100644 --- a/implants/lib/transport/src/grpc.rs +++ b/implants/lib/transport/src/grpc.rs @@ -1,10 +1,7 @@ use crate::Transport; use anyhow::Result; -use hyper::client::HttpConnector; -use hyper::service::service_fn; use hyper::Uri; use pb::c2::*; -use std::borrow::Borrow; use std::str::FromStr; use std::sync::mpsc::{Receiver, Sender}; use tonic::codec::ProstCodec; From 4cc156bf38cc81f1c8c3035ea5d08c5454ef2a8d Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 10 Mar 2024 20:11:37 +0000 Subject: [PATCH 6/9] Fix file list impl lint. --- implants/lib/eldritch/src/file/list_impl.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/implants/lib/eldritch/src/file/list_impl.rs b/implants/lib/eldritch/src/file/list_impl.rs index 901dd0b3e..3e49ec78d 100644 --- a/implants/lib/eldritch/src/file/list_impl.rs +++ b/implants/lib/eldritch/src/file/list_impl.rs @@ -1,7 +1,7 @@ use super::super::insert_dict_kv; use super::{File, FileType}; use anyhow::{Context, Result}; -use chrono::{DateTime, NaiveDateTime, Utc}; +use chrono::DateTime; use glob::glob; use starlark::{ collections::SmallMap, @@ -98,7 +98,7 @@ fn create_file_from_pathbuf(path_entry: PathBuf) -> Result { } }; - let naive_datetime = match NaiveDateTime::from_timestamp_opt(timestamp, 0) { + let naive_datetime = match DateTime::from_timestamp(timestamp, 0) { Some(local_naive_datetime) => local_naive_datetime, None => { return Err(anyhow::anyhow!( @@ -107,7 +107,6 @@ fn create_file_from_pathbuf(path_entry: PathBuf) -> Result { )) } }; - let time_modified: DateTime = DateTime::from_naive_utc_and_offset(naive_datetime, Utc); Ok(File { name: file_name, @@ -117,7 +116,7 @@ fn create_file_from_pathbuf(path_entry: PathBuf) -> Result { owner: owner_username, group: group_id.to_string(), permissions: permissions.to_string(), - time_modified: time_modified.to_string(), + time_modified: naive_datetime.to_string(), }) } @@ -247,7 +246,6 @@ mod tests { runtime.finish().await; // Read Messages - let expected_output = format!("{}\n", path); let mut found = false; for msg in runtime.messages() { if let Message::ReportText(m) = msg { @@ -374,6 +372,7 @@ for f in file.list(input_params['path']): found = true; } } + assert!(found); Ok(()) } From 8deec0d571f1c57a162bbc1efa543c611d0f571b Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Thu, 14 Mar 2024 01:49:47 +0000 Subject: [PATCH 7/9] =?UTF-8?q?gg=20tests=20pass=20=F0=9F=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- implants/lib/eldritch/src/file/list_impl.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/implants/lib/eldritch/src/file/list_impl.rs b/implants/lib/eldritch/src/file/list_impl.rs index 3e49ec78d..9076aa6b4 100644 --- a/implants/lib/eldritch/src/file/list_impl.rs +++ b/implants/lib/eldritch/src/file/list_impl.rs @@ -328,7 +328,7 @@ for f in file.list(input_params['path']): .join(file); std::fs::File::create(test_file)?; - // Run Eldritch (until finished) + // Run Eldritch (until finished) /tmp/.tmpabc123/down the/rabbit hole/win let mut runtime = crate::start( 123, Tome { @@ -341,6 +341,7 @@ for f in file.list(input_params['path']): String::from("path"), test_dir .path() + .join(expected_dir) .join("*") .join("win") .to_str() @@ -353,21 +354,11 @@ for f in file.list(input_params['path']): .await; runtime.finish().await; - let expected_output = format!( - "{}\n", - test_dir - .path() - .join(expected_dir) - .join(nested_dir) - .join(file) - .to_str() - .unwrap() - ); let mut found = false; for msg in runtime.messages() { if let Message::ReportText(m) = msg { assert_eq!(123, m.id); - assert_eq!(expected_output, m.text); + assert!(m.text.contains(file)); log::debug!("text: {:?}", m.text); found = true; } From 20342b835ac9d9f45b38901e84d42db812a2a8dc Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Thu, 14 Mar 2024 02:33:34 +0000 Subject: [PATCH 8/9] Switch the mozilla cert chain --- implants/lib/transport/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implants/lib/transport/Cargo.toml b/implants/lib/transport/Cargo.toml index 21cdb06c6..ff3f8d21e 100644 --- a/implants/lib/transport/Cargo.toml +++ b/implants/lib/transport/Cargo.toml @@ -17,7 +17,7 @@ prost = { workspace = true } prost-types = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tokio-stream = { workspace = true } -tonic = { workspace = true, features = ["tls-roots"] } +tonic = { workspace = true, features = ["tls-webpki-roots"] } trait-variant = { workspace = true } hyper = { version = "0.14", features = ["client"] } hyper-proxy = "0.9.1" From 5ad8f746f6f17ec1c21b4a073dda212c31937d7b Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Thu, 14 Mar 2024 02:34:41 +0000 Subject: [PATCH 9/9] comment hyper dep --- implants/lib/transport/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/implants/lib/transport/Cargo.toml b/implants/lib/transport/Cargo.toml index ff3f8d21e..c64ecf311 100644 --- a/implants/lib/transport/Cargo.toml +++ b/implants/lib/transport/Cargo.toml @@ -19,7 +19,9 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tokio-stream = { workspace = true } tonic = { workspace = true, features = ["tls-webpki-roots"] } trait-variant = { workspace = true } -hyper = { version = "0.14", features = ["client"] } +hyper = { version = "0.14", features = [ + "client", +] } # Had to user an older version of hyper to support hyper-proxy hyper-proxy = "0.9.1" # [feature = mock]