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:
+62
-3
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user