added a module to copy the command to run a portscan for the scope, and

added the ability to add new configs to separate projects, like work vs
personal. Also added a way to switch between these configs.
This commit is contained in:
2026-08-19 16:56:27 -05:00
parent e28306d07d
commit 8ab7a2bec4
10 changed files with 759 additions and 291 deletions
+62 -3
View File
@@ -9,12 +9,12 @@ use ratatui::{
prelude::*,
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
};
use std::thread::sleep;
use std::thread::spawn;
use std::thread::{JoinHandle, sleep};
use std::time::Duration;
use std::{error::Error, time::Instant};
use std::{io::Write, usize};
use sysinfo::{MemoryRefreshKind, System};
use sysinfo::System;
pub fn get_user_input(prompt: &str) -> Result<String, Box<dyn Error>> {
println!("{}", prompt);
@@ -23,9 +23,51 @@ pub fn get_user_input(prompt: &str) -> Result<String, Box<dyn Error>> {
return Ok(response.trim().to_string());
}
pub fn startup(
mut state: AppState,
nested: bool,
handles: Arc<Mutex<Vec<JoinHandle<()>>>>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut app = AppState::new();
let (tx, rx) = channel();
app.main_tx = tx.clone();
if let Some(line) = state.config.get("alternate_configs") {
let mut selections = HashMap::new();
line.split(",").into_iter().for_each(|line| {
if let Some((name, path)) = line.split_once("|") {
selections.insert(name.to_string(), path.to_string());
println!("{} | {} ", name, path);
}
});
selections.insert(
"default".to_string(),
state.config_file.display().to_string(),
);
println!("default | {}", state.config_file.display());
let response = get_user_input("which config would you like to load?")?;
if let Some(path) = selections.get(&response) {
app.load_config(PathBuf::from(path), true)?;
run_tui(app, rx, handles.clone())?;
} else {
eprintln!("error invalid selection! exiting...");
return Err("invalid config selected.".into());
}
} else {
app.load_config(state.config_file.clone(), true)?;
run_tui(app, rx, handles.clone())?;
}
if nested {
let (tx, rx) = channel();
state.main_tx = tx;
run_tui(state, rx, handles.clone())?;
}
Ok(())
}
pub fn run_tui(
mut state: AppState,
main_rx: Receiver<ToolMessage>,
main_handles: Arc<Mutex<Vec<JoinHandle<()>>>>,
) -> Result<(), Box<dyn std::error::Error>> {
enable_raw_mode()?;
let mut stdout = io::stdout();
@@ -43,6 +85,7 @@ pub fn run_tui(
let mut system = System::new_all();
let system_timer = Duration::from_secs(1);
let mut last_print = Instant::now();
let mut switch_config = false;
const BYTES_PER_GB: u64 = 1024 * 1024 * 1024;
if !state.projects.is_empty() {
project_list_state.select(Some(0));
@@ -87,6 +130,7 @@ pub fn run_tui(
let mut history_index = state.history.len();
let mut handles = Vec::new();
loop {
if switch_config {}
if Instant::now().duration_since(last_print) > system_timer {
system.refresh_cpu_usage();
system.refresh_memory();
@@ -556,7 +600,10 @@ pub fn run_tui(
.send(ToolMessage::Output((0, "\n".to_string())));
let prompt = state.prompt.clone();
if prompt.action.is_some() {
state.prompt.responses.push(trimmed.clone());
state.prompt.responses.push(PromptResponse {
query: prompt.last_prompted.clone().unwrap(),
response: trimmed.clone(),
});
if state.prompt.responses.len() == state.prompt.num_responses {
state.execute_command(
prompt.execute_command.as_str(),
@@ -604,6 +651,10 @@ pub fn run_tui(
);
}
}
"switch_config" => {
switch_config = true;
break;
}
command_name => {
if state.module_loader.commands.contains_key(command) {
let _ = state.main_tx.send(ToolMessage::Output((
@@ -756,5 +807,13 @@ pub fn run_tui(
for handle in handles {
handle.join().unwrap();
}
if switch_config {
let handles_clone = main_handles.clone();
if let Ok(mut lock) = main_handles.lock() {
lock.push(spawn(move || {
let _ = startup(state, true, handles_clone);
}));
}
}
Ok(())
}
+1
View File
@@ -97,6 +97,7 @@ pub fn client_install() -> Result<(), Box<dyn Error>> {
client_config_file.write(format!("template_box: {}\n", template_box).as_bytes())?;
client_config_file.write(format!("term_cmd: {}\n", term_cmd).as_bytes())?;
client_config_file.write(format!("tools: {}\n", tools_folder).as_bytes())?;
client_config_file.write("alternate_configs: \n".as_bytes())?;
println!("\ndefault config files written!");
println!("downloading note templates, victim templates, and modules...");
create_dir_all("./temp")?;
+376 -108
View File
@@ -1,3 +1,5 @@
use clipboard::ClipboardContext;
use clipboard::ClipboardProvider;
use fs_extra::dir::{CopyOptions, copy};
use ipnet::IpNet;
use keyring::Entry;
@@ -5,6 +7,7 @@ use ratatui::crossterm::event;
use rhai::{AST, Dynamic, Engine, Scope};
use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned, pki_types::ServerName};
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::fs::{self, File, create_dir_all, read_dir, read_to_string, remove_dir, remove_dir_all};
use std::io::{self, BufRead, BufReader, Read, Write};
@@ -94,48 +97,46 @@ pub struct AppState {
}
impl AppState {
pub fn new() -> (Self, Receiver<ToolMessage>) {
let (main_tx, main_rx) = channel();
(
Self {
projects: Vec::new(),
servers: Vec::new(),
config: HashMap::new(),
config_file: PathBuf::new(),
workers: rayon::ThreadPoolBuilder::new()
.num_threads(4)
.build()
.unwrap(),
worker_txes: Vec::new(),
main_tx,
history: Vec::new(),
log: Vec::new(),
output: Vec::new(),
selected_server: 0,
selected_project: 0,
curent_intput: String::new(),
module_loader: ModuleLoader::new(),
output_scroll: 0,
output_follow: true,
prompt: Prompt {
action: None,
responses: Vec::new(),
execute_command: String::new(),
num_responses: 0,
prompts: Vec::new(),
},
info_scroll: 0,
server_broker_running: true,
app_state_running: true,
remoting: false,
help: Vec::new(),
name: String::new(),
pub fn new() -> Self {
let (main_tx, _) = channel();
Self {
projects: Vec::new(),
servers: Vec::new(),
config: HashMap::new(),
config_file: PathBuf::new(),
workers: rayon::ThreadPoolBuilder::new()
.num_threads(4)
.build()
.unwrap(),
worker_txes: Vec::new(),
main_tx,
history: Vec::new(),
log: Vec::new(),
output: Vec::new(),
selected_server: 0,
selected_project: 0,
curent_intput: String::new(),
module_loader: ModuleLoader::new(),
output_scroll: 0,
output_follow: true,
prompt: Prompt {
action: None,
responses: Vec::new(),
execute_command: String::new(),
num_responses: 0,
prompts: Vec::new(),
last_prompted: None,
},
main_rx,
)
info_scroll: 0,
server_broker_running: true,
app_state_running: true,
remoting: false,
help: Vec::new(),
name: String::new(),
}
}
pub fn load_config(&mut self, file: PathBuf) -> Result<(), Box<dyn Error>> {
pub fn load_config(&mut self, file: PathBuf, display: bool) -> Result<(), Box<dyn Error>> {
self.config_file = file.clone();
let config_contents = read_to_string(file)?;
for line in config_contents.lines() {
@@ -187,7 +188,7 @@ impl AppState {
if conf_file.file_name().to_string_lossy() == "project.conf".to_string() {
let mut new_project = Project::new();
new_project.config_folder(conf_file.path());
new_project.load_config()?;
new_project.load_config(display)?;
let template_box = self.config.get("template_box").unwrap();
let tools = self.config.get("tools").unwrap();
let db = DistroBox {
@@ -242,6 +243,7 @@ impl AppState {
);
self.help.push("parse_scope\nparse the projects general.md notes file for the scope copied from the workbook\n".to_string());
self.help.push("exit\nquit the tool\n".to_string());
self.initialize_modules();
return Ok(());
}
@@ -427,10 +429,12 @@ impl AppState {
}
}
"remove_project_confirm" => {
self.prompt.action = None;
self.prompt.num_responses = 0;
if self.prompt.responses.len() > 0 {
if self.prompt.responses[0] == "y".to_string() {
if self.prompt.responses[0]
.response
.to_lowercase()
.contains("y")
{
let project = self.projects[self.selected_project].clone();
let mut config_file = project.config_folder.clone();
config_file.pop();
@@ -507,14 +511,17 @@ impl AppState {
self.prompt.action = Some(ToolMessage::RemoveProject);
self.prompt.num_responses = 1;
self.prompt.execute_command = String::from("remove_project_confirm");
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!(
"{}, {} and all contents will be deleted. Continue? (y/N)",
self.prompt.prompts.push(PromptQuery {
id: 0,
query: format!(
"{}, {} and all contentes wil lbe deleted. Contineue? (y/N",
project.files.display(),
project.notes.display()
),
)));
});
let _ = self
.main_tx
.send(ToolMessage::Input((rid, "prompt".to_string())));
}
"new_terminal" | "nt" => {
let project = self.projects[self.selected_project].clone();
@@ -528,17 +535,30 @@ impl AppState {
self.prompt.num_responses = 2;
self.prompt.action = Some(ToolMessage::AddServer);
self.prompt.execute_command = String::from("add_server");
self.prompt.prompts = vec![
"What is the IP for the server?".to_string(),
"What is the port for the server?".to_string(),
];
self.prompt.prompts.push(PromptQuery {
id: 0,
query: "What is the IP of the server?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 1,
query: "What is the port for the server?".to_string(),
});
let _ = self
.main_tx
.send(ToolMessage::Input((0, "prompt".to_string())));
}
"add_server" => {
let mut ip = String::new();
let mut port = String::new();
self.prompt.responses.iter().for_each(|res| {
if res.query.id == 0 {
ip = res.response.clone();
} else if res.query.id == 1 {
port = res.response.clone();
}
});
let new_server = Server {
address: format!("{}:{}", self.prompt.responses[0], self.prompt.responses[1]),
address: format!("{}:{}", ip, port),
connected: false,
timer: Duration::from_secs(5),
last_check: time::Instant::now(),
@@ -565,12 +585,20 @@ impl AppState {
.send(ToolMessage::Input((0, self.prompt.execute_command.clone())));
let _ = self.main_tx.send(ToolMessage::EndPrompt);
} else {
let mut prompt_index = 0;
if self.prompt.responses.len() > 0 {
prompt_index = self.prompt.num_responses - self.prompt.responses.len();
for query in &self.prompt.prompts {
if !self
.prompt
.responses
.iter()
.any(|res| res.query.id == query.id)
{
self.prompt.last_prompted = Some(query.clone());
let _ = self
.main_tx
.send(ToolMessage::Output((rid, query.query.clone())));
break;
}
}
let query = self.prompt.prompts[prompt_index].clone();
let _ = self.main_tx.send(ToolMessage::Output((rid, query)));
}
}
"list_servers" => {
@@ -614,7 +642,10 @@ impl AppState {
"select_server" => {
self.prompt.action = Some(ToolMessage::SelectServer);
self.prompt.num_responses = 1;
self.prompt.prompts.push(String::from("Selection?"));
self.prompt.prompts.push(PromptQuery {
id: 0,
query: String::from("Selection?"),
});
self.prompt.execute_command = String::from("server_selected");
self.servers.iter().enumerate().for_each(|(id, servermut)| {
if let Ok(server) = servermut.lock() {
@@ -634,7 +665,7 @@ impl AppState {
}
"server_selected" => {
if self.prompt.responses.len() == 1 {
if let Ok(id) = self.prompt.responses[0].parse::<usize>() {
if let Ok(id) = self.prompt.responses[0].response.parse::<usize>() {
self.selected_server = id;
if let Ok(server) = self.servers[id].lock() {
let _ = self.main_tx.send(ToolMessage::Output((
@@ -855,6 +886,67 @@ impl AppState {
}
}
}
"new_config" => {
self.prompt.num_responses = 8;
self.prompt.action = Some(ToolMessage::Input((0, "add_config".to_string())));
self.prompt.execute_command = String::from("add_config");
self.prompt.prompts.push(PromptQuery {
id: 0,
query: "name for the new config?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 1,
query: "Path to save the config folder?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 2,
query: "Path for upcoming project files (not notes)?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 3,
query: "Path for current project files (not notes)?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 4,
query: "Path for upcoming notes?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 5,
query: "Path for current notes?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 6,
query: "Path to tool directory?".to_string(),
});
self.prompt.prompts.push(PromptQuery {
id: 7,
query: "Template box name?".to_string(),
});
let _ = self
.main_tx
.send(ToolMessage::Input((0, "prompt".to_string())));
}
"add_config" => {
self.prompt.responses.iter().for_each(|res| {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
format!("{} | {}", res.query.query, res.response),
)));
});
self.new_config()?;
self.save_config()?;
}
"list_configs" => {
if let Some(config_line) = self.config.get("alternate_configs") {
config_line.split(",").into_iter().for_each(|entry| {
if let Some((name, path)) = entry.split_once("|") {
let _ = self
.main_tx
.send(ToolMessage::Output((rid, format!("{}: {}", name, path))));
}
});
}
}
"exit" => {
self.save_all()?;
let _ = self.main_tx.send(ToolMessage::AppStateExit);
@@ -938,7 +1030,11 @@ impl AppState {
let out_string = result
.into_string()
.unwrap_or_else(|_| "failed to parse string".into());
if cmd_meta.calls != "none" {
if cmd_meta.calls == "clipboard" {
let mut ctx: ClipboardContext =
ClipboardProvider::new().unwrap();
ctx.set_contents(out_string.clone()).unwrap();
} else if cmd_meta.calls != "none" {
let _ =
worker_tx.send(ToolMessage::Input((0, out_string.clone())));
}
@@ -1173,50 +1269,193 @@ impl AppState {
return Ok(());
}
pub fn new_server(&mut self, rid: usize) -> Result<(), Box<dyn Error>> {
let prompt = self.prompt.clone();
if prompt.responses.len() == prompt.num_responses {
let new_server = Server {
address: format!(
"{}:{}",
self.prompt.responses[0].clone(),
self.prompt.responses[1].clone()
),
connected: false,
timer: Duration::from_secs(60),
last_check: time::Instant::now(),
message_que: Vec::new(),
action_que: Vec::new(),
client_id: 0,
selected_client: 0,
config: None,
logged_in: false,
password: String::new(),
name: String::new(),
cert_text: String::new(),
};
self.servers.push(Arc::new(Mutex::new(new_server)));
self.prompt.action = None;
self.prompt.responses.clear();
self.prompt.num_responses = 0;
return Ok(());
} else {
match prompt.responses.len() {
0 => {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
String::from("What is the IP for the server?"),
)));
}
1 => {
let _ = self.main_tx.send(ToolMessage::Output((
rid,
String::from("What is the port for the server?"),
)));
}
pub fn new_server(&mut self) -> Result<(), Box<dyn Error>> {
let mut ip = String::new();
let mut port = String::new();
self.prompt.responses.iter().for_each(|res| {
if res.query.id == 0 {
ip = res.response.clone();
} else if res.query.id == 1 {
port = res.response.clone();
}
});
let new_server = Server {
address: format!("{}:{}", ip, port),
connected: false,
timer: Duration::from_secs(60),
last_check: time::Instant::now(),
message_que: Vec::new(),
action_que: Vec::new(),
client_id: 0,
selected_client: 0,
config: None,
logged_in: false,
password: String::new(),
name: String::new(),
cert_text: String::new(),
};
self.servers.push(Arc::new(Mutex::new(new_server)));
self.prompt.reset();
Ok(())
}
pub fn new_config(&mut self) -> Result<(), Box<dyn Error>> {
let mut name = String::new();
let mut config_path = PathBuf::new();
let mut upcoming_files_path = PathBuf::new();
let mut current_files_path = PathBuf::new();
let mut upcoming_notes_path = PathBuf::new();
let mut current_notes_path = PathBuf::new();
let mut tools_folder = PathBuf::new();
let mut template_box = String::new();
let term_cmd = self.config.get("term_cmd").unwrap().clone();
self.prompt
.responses
.iter()
.for_each(|res| match res.query.id {
0 => name = res.response.clone(),
1 => config_path = PathBuf::from(res.response.clone()),
2 => upcoming_files_path = PathBuf::from(res.response.clone()),
3 => current_files_path = PathBuf::from(res.response.clone()),
4 => upcoming_notes_path = PathBuf::from(res.response.clone()),
5 => current_notes_path = PathBuf::from(res.response.clone()),
6 => tools_folder = PathBuf::from(res.response.clone()),
7 => template_box = res.response.clone(),
_ => {}
});
let mut existing = false;
if let Some(line) = self.config.get("alternate_configs") {
line.split(",").into_iter().for_each(|line| {
if let Some((ename, _)) = line.split_once("|") {
if ename.trim().to_string() == name {
existing = true;
}
}
});
}
if existing {
let _ = self.main_tx.send(ToolMessage::Output((
0,
format!("{} already exists!", name),
)));
return Ok(());
}
let root_config_folder_path = config_path.clone();
let mut projects_path = config_path.clone();
projects_path.push("projects");
let mut module_path = config_path.clone();
module_path.push("modules");
let mut note_templates_path = config_path.clone();
note_templates_path.push("note_templates");
create_dir_all(&module_path)?;
config_path.push("client.conf");
let client_conf_path = config_path.clone();
projects_path.push("default");
match create_dir_all(&projects_path) {
Ok(_) => {
let _ = self.main_tx.send(ToolMessage::Output((
0,
format!(
"[success] create projects path at {}",
projects_path.display()
),
)));
}
Err(e) => {
let _ = self.main_tx.send(ToolMessage::Output((
0,
format!(
"[error] couldn't create projects path at {}: {e}",
projects_path.display()
),
)));
return Ok(());
}
}
projects_path.push("project.conf");
let mut client_config_file = File::create(&config_path)?;
client_config_file.write(format!("projects: {}\n", projects_path.display()).as_bytes())?;
client_config_file.write("servers: 127.0.0.1:31337\n".as_bytes())?;
let mut default_project_file = File::create(projects_path)?;
config_path.pop();
let mut server_path = config_path.clone();
server_path.push("server");
create_dir_all(&server_path)?;
server_path.push("server.conf");
let mut server_config_file = File::create(&server_path)?;
server_config_file.write("address: 127.0.0.1:31337\n".as_bytes())?;
server_config_file.write("running: false\n".as_bytes())?;
default_project_file.write("org_name: default\n".as_bytes())?;
default_project_file.write("name: default\n".as_bytes())?;
default_project_file
.write(format!("notes: {}\n", current_files_path.display()).as_bytes())?;
default_project_file
.write(format!("files: {}\n", current_notes_path.display()).as_bytes())?;
default_project_file.write("stage: current".as_bytes())?;
client_config_file
.write(format!("current_files: {}\n", current_files_path.display()).as_bytes())?;
client_config_file
.write(format!("current_notes: {}\n", current_notes_path.display()).as_bytes())?;
client_config_file
.write(format!("upcoming_files: {}\n", upcoming_files_path.display()).as_bytes())?;
client_config_file
.write(format!("upcoming_notes: {}\n", upcoming_notes_path.display()).as_bytes())?;
client_config_file.write(format!("module_path: {}\n", module_path.display()).as_bytes())?;
client_config_file.write(format!("template_box: {}\n", template_box).as_bytes())?;
client_config_file.write(format!("term_cmd: {}\n", term_cmd).as_bytes())?;
client_config_file.write(format!("tools: {}\n", tools_folder.display()).as_bytes())?;
create_dir_all("./temp")?;
env::set_current_dir("./temp")?;
let git_clone_output = Command::new("git")
.arg("clone")
.arg("https://git.pyro.monster/pyro/tetanus.git")
.output()?;
let git_clone_status = git_clone_output.status.success();
if git_clone_status {
let mut options = CopyOptions::new();
options.overwrite = true;
options.copy_inside = true;
copy(
"./tetanus/note_templates",
root_config_folder_path.clone(),
&options,
)?;
copy(
"./tetanus/default-modules",
module_path.join("default"),
&options,
)?;
copy(
"./tetanus/victim_templates",
root_config_folder_path.join("victim"),
&options,
)?;
env::set_current_dir("../")?;
remove_dir_all("./temp")?;
let _ = self.main_tx.send(ToolMessage::Output((
0,
"New config structure created!".to_string(),
)));
}
if let Some(original_value) = self.config.get("alternate_configs") {
let new_value = format!(
"{}|{},{}",
name,
client_conf_path.display().to_string(),
original_value,
);
self.config
.insert("alternate_configs".to_string(), new_value);
} else {
self.config.insert(
"alternate_configs".to_string(),
format!("{}|{},", name, client_conf_path.display().to_string()),
);
}
self.prompt.responses.clear();
self.prompt.prompts.clear();
self.prompt.action = None;
self.prompt.execute_command.clear();
Ok(())
}
}
@@ -1413,7 +1652,7 @@ impl Project {
}
}
pub fn load_config(&mut self) -> Result<(), Box<dyn Error>> {
pub fn load_config(&mut self, display: bool) -> Result<(), Box<dyn Error>> {
let config_contents = read_to_string(&self.config_folder)?;
for line in config_contents.lines() {
let parts: Vec<&str> = line.split(": ").collect();
@@ -1438,8 +1677,10 @@ impl Project {
}
}
}
println!("{} | {} config loaded!", self.org_name, self.name);
println!("loading hosts...");
if display {
println!("{} | {} config loaded!", self.org_name, self.name);
println!("loading hosts...");
}
let mut hosts_folder = self.config_folder.clone();
let mut users_folder = self.config_folder.clone();
hosts_folder.pop();
@@ -1512,7 +1753,9 @@ impl Project {
}
}
}
println!("{} | {} loaded!", self.org_name, self.name);
if display {
println!("{} | {} loaded!", self.org_name, self.name);
}
return Ok(());
}
@@ -1811,6 +2054,17 @@ impl ModuleLoader {
}
results
});
engine.register_fn("filter_ips", |s: &str| -> rhai::Array {
let mut results = rhai::Array::new();
if let Ok(net) = s.trim().parse::<IpNet>() {
results.push(net.addr().to_string().trim().into());
} else if let Ok(ip) = s.trim().parse::<IpAddr>() {
results.push(ip.to_string().trim().into());
} else if s.contains(".") {
results.push(s.to_string().trim().into());
}
results
});
engine.register_fn("write_file", |path: &str, contents: &str| {
std::fs::write(path, contents).is_ok()
});
@@ -2133,10 +2387,11 @@ impl DistroBox {
#[derive(Clone)]
struct Prompt {
action: Option<ToolMessage>,
responses: Vec<String>,
responses: Vec<PromptResponse>,
execute_command: String,
num_responses: usize,
prompts: Vec<String>,
prompts: Vec<PromptQuery>,
last_prompted: Option<PromptQuery>,
}
impl Prompt {
@@ -2146,5 +2401,18 @@ impl Prompt {
self.execute_command.clear();
self.num_responses = 0;
self.prompts.clear();
self.last_prompted = None;
}
}
#[derive(Clone)]
struct PromptResponse {
query: PromptQuery,
response: String,
}
#[derive(Clone)]
struct PromptQuery {
id: usize,
query: String,
}
+21 -4
View File
@@ -1,7 +1,10 @@
use clap::Parser;
use rand::RngExt;
use rand::distr::Alphanumeric;
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::thread::{sleep, spawn};
use std::time::Duration;
use std::{env, path::PathBuf, process::exit};
use sysinfo::*;
use tetanus::funcs::*;
@@ -138,8 +141,8 @@ async fn main() {
);
exit(1);
}
let (mut appstate, rx) = AppState::new();
let config_load = appstate.load_config(client_config_path.clone());
let (mut appstate) = AppState::new();
let config_load = appstate.load_config(client_config_path.clone(), true);
if config_load.is_err() {
eprintln!("error loading config!");
exit(1);
@@ -148,9 +151,23 @@ async fn main() {
banner.lines().for_each(|line| {
appstate.output.push(line.to_string());
});
appstate.initialize_modules();
let handles = Arc::new(Mutex::new(Vec::new()));
let handles_clone = handles.clone();
println!("entering tui...");
let _res = run_tui(appstate, rx).unwrap();
let primary_handle = spawn(move || {
let _ = startup(appstate, false, handles_clone);
});
if let Ok(mut lock) = handles.lock() {
lock.push(primary_handle);
}
loop {
sleep(Duration::from_secs(3));
if let Ok(lock) = handles.lock() {
if lock.iter().all(|handle| handle.is_finished()) {
break;
}
}
}
} else if args.server {
let mut address = "127.0.0.1:31337".to_string();
let mut certificate_path = config_path.clone();
+40
View File
@@ -1,4 +1,6 @@
use crate::*;
use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce, aead::Aead};
use rand::{self, Rng, TryRng};
use rayon::spawn;
pub fn generate_code(
@@ -46,6 +48,44 @@ pub fn generate_code(
return Ok(());
}
/*fn generate_obfuscated_code(
address: String,
password: String,
config_file: PathBuf,
files: PathBuf,
target: String,
tx: Sender<ToolMessage>,
cert_text: String,
) -> Result<(), Box<dyn Error>> {
let mut source_path = config_file.clone();
source_path.pop();
source_path.push("victim/src/main.rs");
let mut source = read_to_string(&source_path)?;
source = source
.replace("|||SERVER|||", &address)
.replace("|||PASSWORD|||", &password)
.replace("|||CERT|||", &cert_text);
files_base.pop();
files_base.pop();
files_base.push("Cargo.toml");
let cargo_string = read_to_string(source_path)?;
let mut files_base = files.clone();
files_base.push("victim/src");
create_dir_all(&files_base)?;
files_base.push("main.rs");
let mut src_file = File::create(&files_base)?;
let mut key = [0u8; 32];
rand::rng().fill_bytes(&mut key);
let mut iv = [0u8; 16];
rand::rng().fill_bytes(&mut iv);
let cipher = Aes256Gcm::new_from_slice(&key)?;
let nonce = Nonce::try_from(&iv[..])?;
let mut ciphertext = cipher.encrypt(&nonce, source.as_bytes())?;
Ok(())
}*/
fn compile_victim(mut cargo_command: Command, tx: Sender<ToolMessage>) {
let mut out = "Error compiling output!".to_string();
if let Ok(output) = cargo_command.output() {