started work on writing the tool, its not quite ready yet, but we're
getting close.
This commit is contained in:
149
src/cli.rs
Normal file
149
src/cli.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use tokio::sync::mpsc::{Receiver, Sender, channel};
|
||||
|
||||
use crate::Message;
|
||||
use crate::commands;
|
||||
use crate::commands::ToolArgument;
|
||||
use crate::commands::ToolCommand;
|
||||
use crate::load_projects;
|
||||
use crate::load_settings;
|
||||
use crate::network;
|
||||
use crate::print_error;
|
||||
use crate::{Destination, Project, get_user_input, lib::Table, print_success};
|
||||
use tokio;
|
||||
|
||||
pub async fn rec_message(mut rx: Receiver<Message>) {
|
||||
let mut display = true;
|
||||
loop {
|
||||
let rx_res = rx.try_recv();
|
||||
if rx_res.is_ok() {
|
||||
let message = rx_res.unwrap();
|
||||
println!("{}", message.content);
|
||||
display = true;
|
||||
}
|
||||
if display {
|
||||
println!("command?");
|
||||
display = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn cli(
|
||||
mut projects: Vec<Project>,
|
||||
main_tx: Sender<Message>,
|
||||
tool_tx: Sender<Message>,
|
||||
server_address: String,
|
||||
config_path: PathBuf,
|
||||
) {
|
||||
print_success("started the CLI!");
|
||||
let mut commands = commands::build_tools();
|
||||
loop {
|
||||
let settings = load_settings(&config_path, false);
|
||||
projects = load_projects(&config_path, false);
|
||||
if tool_tx.is_closed() {
|
||||
println!("tool tx closed at the start of this loop!");
|
||||
}
|
||||
let command = get_user_input("");
|
||||
let mut command_name = String::new();
|
||||
let mut args = Vec::new();
|
||||
if command.contains(" ") {
|
||||
let mut command_vec: Vec<&str> = command.split(" ").collect();
|
||||
command_name = command_vec[0].to_string();
|
||||
for arg in &mut command_vec[1..] {
|
||||
args.push(arg.to_string());
|
||||
}
|
||||
} else {
|
||||
command_name = command;
|
||||
}
|
||||
if command_name == String::from("exit") {
|
||||
let message = Message {
|
||||
source: Destination::Console,
|
||||
destination: Destination::Control,
|
||||
content: String::from("exit"),
|
||||
};
|
||||
main_tx.send(message).await.unwrap();
|
||||
}
|
||||
let mut valid_command = false;
|
||||
for toolcommand in &mut commands {
|
||||
let mut ready = true;
|
||||
if toolcommand.name == command_name {
|
||||
valid_command = true;
|
||||
if toolcommand.req_args.len() > 0 {
|
||||
let mut args_vec = Vec::new();
|
||||
for req_arg in toolcommand.req_args.clone() {
|
||||
let mut new_arg = ToolArgument::default();
|
||||
new_arg.name = req_arg;
|
||||
args_vec.push(new_arg);
|
||||
}
|
||||
println!("Required args:");
|
||||
if args.len() < toolcommand.user_args.len() {
|
||||
ready = false;
|
||||
}
|
||||
if ready {
|
||||
let mut position_count = 0;
|
||||
for user_arg in toolcommand.user_args.clone() {
|
||||
println!("enough args supplied, building arguments list");
|
||||
let mut new_arg = ToolArgument::default();
|
||||
new_arg.name = user_arg;
|
||||
new_arg.user_supplied = true;
|
||||
new_arg.position = Some(position_count);
|
||||
args_vec.push(new_arg);
|
||||
position_count += 1;
|
||||
}
|
||||
}
|
||||
if ready {
|
||||
for arg in &mut args_vec {
|
||||
let iargs = args.clone();
|
||||
if arg.user_supplied {
|
||||
arg.string = Some(iargs[arg.position.unwrap()].clone());
|
||||
} else {
|
||||
match arg.name.as_str() {
|
||||
"projects" => arg.projects = Some(projects.clone()),
|
||||
"upcoming_files" => {
|
||||
arg.path =
|
||||
Some(PathBuf::from(settings["upcoming_files"].clone()))
|
||||
}
|
||||
"upcoming_notes" => {
|
||||
arg.path =
|
||||
Some(PathBuf::from(settings["upcoming_notes"].clone()))
|
||||
}
|
||||
"template" => {
|
||||
arg.string =
|
||||
Some(String::from(settings["templatebox"].clone()))
|
||||
}
|
||||
"config" => arg.path = Some(config_path.clone()),
|
||||
_ => print_error(
|
||||
&format!("unkown arg requested! {}", arg.name),
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
toolcommand.args = Some(args_vec);
|
||||
}
|
||||
println!("args parsed!");
|
||||
}
|
||||
let mut message = Message {
|
||||
source: Destination::Console,
|
||||
destination: Destination::Console,
|
||||
content: String::new(),
|
||||
};
|
||||
if ready {
|
||||
message.content = toolcommand.execute();
|
||||
} else {
|
||||
message.content = String::from("error in command!");
|
||||
}
|
||||
tool_tx.send(message).await.unwrap();
|
||||
}
|
||||
}
|
||||
if !valid_command {
|
||||
let message = Message {
|
||||
source: Destination::Console,
|
||||
destination: Destination::Console,
|
||||
content: String::from("command not found!"),
|
||||
};
|
||||
tool_tx.send(message).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
186
src/commands.rs
Normal file
186
src/commands.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
use crate::Destination;
|
||||
use crate::Message;
|
||||
use crate::Project;
|
||||
use crate::lib::Table;
|
||||
use crate::print_error;
|
||||
use crate::print_success;
|
||||
use crate::save_project;
|
||||
use std::fs::create_dir;
|
||||
use std::fs::create_dir_all;
|
||||
use std::fs::{File, OpenOptions, ReadDir, read_to_string};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use tokio;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToolCommand {
|
||||
pub name: String,
|
||||
pub help: String,
|
||||
pub req_args: Vec<String>,
|
||||
pub user_args: Vec<String>,
|
||||
pub args: Option<Vec<ToolArgument>>,
|
||||
pub func: fn(Option<Vec<ToolArgument>>) -> String,
|
||||
}
|
||||
|
||||
impl ToolCommand {
|
||||
pub fn new(name: String, help: String, func: fn(Option<Vec<ToolArgument>>) -> String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
help,
|
||||
req_args: Vec::new(),
|
||||
user_args: Vec::new(),
|
||||
args: None,
|
||||
func,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&self) -> String {
|
||||
if self.req_args.len() > 0 {
|
||||
if self.args.is_none() {
|
||||
return String::from("Error: no arguments given, but arguments are required!");
|
||||
} else if self.args.clone().unwrap().len() != self.req_args.len() + self.user_args.len()
|
||||
{
|
||||
return String::from("Error: the wrong number of args were supplied!");
|
||||
}
|
||||
}
|
||||
return (self.func)(self.args.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ToolArgument {
|
||||
pub name: String,
|
||||
pub user_supplied: bool,
|
||||
pub position: Option<usize>,
|
||||
pub path: Option<PathBuf>,
|
||||
pub string: Option<String>,
|
||||
pub project: Option<Project>,
|
||||
pub projects: Option<Vec<Project>>,
|
||||
pub boolean: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn build_tools() -> Vec<ToolCommand> {
|
||||
let mut tool_commands = Vec::new();
|
||||
let mut listproject = ToolCommand::new(
|
||||
"list_projects".to_string(),
|
||||
"coming soon".to_string(),
|
||||
list_projects,
|
||||
);
|
||||
listproject.req_args = vec![String::from("projects")];
|
||||
tool_commands.push(listproject);
|
||||
let mut createproject = ToolCommand::new(
|
||||
"create_project".to_string(),
|
||||
"creates a new project and saves it to the projects file to be reloaded on the next loop
|
||||
usage:
|
||||
create_project project_name"
|
||||
.to_string(),
|
||||
new_project,
|
||||
);
|
||||
createproject.req_args = vec![
|
||||
String::from("config"),
|
||||
String::from("upcoming_files"),
|
||||
String::from("upcoming_notes"),
|
||||
String::from("template"),
|
||||
];
|
||||
createproject.user_args = vec![String::from("name")];
|
||||
tool_commands.push(createproject);
|
||||
return tool_commands;
|
||||
}
|
||||
|
||||
pub fn list_projects(args: Option<Vec<ToolArgument>>) -> String {
|
||||
let given_args = args.unwrap();
|
||||
let mut lines = vec![String::from("name|stage|boxname")];
|
||||
for arg in given_args {
|
||||
if arg.name == String::from("projects") {
|
||||
for project in arg.projects.unwrap() {
|
||||
if project.current {
|
||||
let line = format!("{}|{}|{}", project.name, "current", project.boxname);
|
||||
lines.push(line);
|
||||
} else {
|
||||
let line = format!("{}|{}|{}", project.name, "upcomming", project.boxname);
|
||||
lines.push(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut table = Table::default();
|
||||
table.build(lines);
|
||||
return table.get_table();
|
||||
}
|
||||
|
||||
pub fn new_project(args: Option<Vec<ToolArgument>>) -> String {
|
||||
println!("reached the new_project function");
|
||||
let given_args = args.unwrap();
|
||||
let mut config_path = PathBuf::new();
|
||||
let mut name = String::new();
|
||||
let mut files_path = PathBuf::new();
|
||||
let mut notes_path = PathBuf::new();
|
||||
let mut template_box = String::new();
|
||||
for arg in given_args {
|
||||
match arg.name.as_str() {
|
||||
"config" => {
|
||||
config_path = arg.path.unwrap();
|
||||
}
|
||||
"name" => {
|
||||
name = arg.string.unwrap();
|
||||
}
|
||||
"upcoming_files" => {
|
||||
files_path = arg.path.unwrap();
|
||||
}
|
||||
"upcoming_notes" => {
|
||||
notes_path = arg.path.unwrap();
|
||||
}
|
||||
"template" => {
|
||||
template_box = arg.string.unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if name.len() == 0 {
|
||||
return String::from(
|
||||
"usage: newproject projectname\nprevious command was missing project name!",
|
||||
);
|
||||
}
|
||||
println!("gatered the data! name: {}", name);
|
||||
print_success("arguments parsed correctly!");
|
||||
println!("setting up files...");
|
||||
let mut project_path = config_path.clone();
|
||||
project_path.pop();
|
||||
project_path.push("projects");
|
||||
project_path.push(format!("{}.conf", &name));
|
||||
let file_create_res = File::create(&project_path);
|
||||
if file_create_res.is_err() {
|
||||
return format!(
|
||||
"Error failure to create project config file!\n{}\n{}",
|
||||
file_create_res.err().unwrap(),
|
||||
&project_path.display().to_string()
|
||||
);
|
||||
}
|
||||
let conf_file = file_create_res.unwrap();
|
||||
files_path.push(&name);
|
||||
notes_path.push(&name);
|
||||
let files_dir_res = create_dir_all(&files_path);
|
||||
let notes_dir_res = create_dir_all(¬es_path);
|
||||
if files_dir_res.is_err() {
|
||||
return (format!(
|
||||
"Error failure to create project files folder!\n{}",
|
||||
files_dir_res.err().unwrap()
|
||||
));
|
||||
}
|
||||
if notes_dir_res.is_err() {
|
||||
return (format!(
|
||||
"Error failure to create project files folder!\n{}",
|
||||
files_dir_res.err().unwrap()
|
||||
));
|
||||
}
|
||||
let mut new_project = Project::default();
|
||||
new_project.name = name.clone();
|
||||
new_project.files = files_path;
|
||||
new_project.notes = notes_path;
|
||||
new_project.current = false;
|
||||
new_project.boxname = format!("{}_{}", template_box, name);
|
||||
save_project(&new_project, &project_path);
|
||||
return String::from("Success!");
|
||||
}
|
||||
25
src/crytpo.rs
Normal file
25
src/crytpo.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use chacha20poly1305::aead::generic_array::typenum::Unsigned;
|
||||
use chacha20poly1305::aead::generic_array::GenericArray;
|
||||
use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng};
|
||||
use chacha20poly1305::ChaCha20Poly1305;
|
||||
|
||||
pub fn generate_key() -> Vec<u8> {
|
||||
ChaCha20Poly1305::generate_key(&mut OsRng).to_vec()
|
||||
}
|
||||
|
||||
pub fn encrypt(cleartext: &str, key: &[u8]) -> Vec<u8> {
|
||||
let cipher = ChaCha20Poly1305::new(GenericArray::from_slice(key));
|
||||
let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
|
||||
let mut obsf = cipher.encrypt(&nonce, cleartext.as_bytes()).unwrap();
|
||||
obsf.splice(..0, nonce.iter().copied());
|
||||
obsf
|
||||
}
|
||||
|
||||
pub fn decrypt(obsf: &[u8], key: &[u8]) -> String {
|
||||
type NonceSize = <ChaCha20Poly1305 as AeadCore>::NonceSize;
|
||||
let cipher = ChaCha20Poly1305::new(GenericArray::from_slice(key));
|
||||
let (nonce, ciphertext) = obsf.split_at(NonceSize::to_usize());
|
||||
let nonce = GenericArray::from_slice(nonce);
|
||||
let plaintext = cipher.decrypt(nonce, ciphertext).unwrap();
|
||||
String::from_utf8(plaintext).unwrap()
|
||||
}
|
||||
0
src/gui.rs
Normal file
0
src/gui.rs
Normal file
229
src/install.rs
Normal file
229
src/install.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{File, create_dir_all, read_to_string, remove_file};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::get_user_input;
|
||||
use crate::print_success;
|
||||
use crate::{crytpo, print_error};
|
||||
|
||||
pub fn install(config: &PathBuf) -> bool {
|
||||
let mut new = true;
|
||||
let mut config_folder = config.clone();
|
||||
config_folder.pop();
|
||||
let mut config_file = config_folder.clone();
|
||||
config_file.push("config.conf");
|
||||
let mut project_config = config_folder.clone();
|
||||
let mut note_templates_path = config_folder.clone();
|
||||
project_config.push("projects");
|
||||
note_templates_path.push("note_templates");
|
||||
let mut settings = HashMap::new();
|
||||
if !config_folder.exists() {
|
||||
let dir_create_res = create_dir_all(&config_folder);
|
||||
if dir_create_res.is_err() {
|
||||
print_error(
|
||||
"error creating configuration directory!",
|
||||
Some(dir_create_res.err().unwrap().to_string()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
print_success("configuration folder created successfully!");
|
||||
} else {
|
||||
print_success("configuration folder already exists!");
|
||||
}
|
||||
if config_file.exists() {
|
||||
if get_user_input("the config file already exists, would you like to delete it and create a full new one?").to_lowercase().contains("y"){
|
||||
let remove_res = remove_file(&config_file);
|
||||
if remove_res.is_err(){
|
||||
print_error("error removing file, please manually delete and try again...", Some(remove_res.err().unwrap().to_string()));
|
||||
return false;
|
||||
}
|
||||
if project_config.exists(){
|
||||
let remove_res = remove_file(&project_config);
|
||||
if remove_res.is_err(){
|
||||
print_error("error removing projects file", Some(remove_res.err().unwrap().to_string()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
new = false;
|
||||
let config_read_res = read_to_string(&config_file);
|
||||
if config_read_res.is_err(){
|
||||
print_error("error reading config file!", Some(config_read_res.err().unwrap().to_string()));
|
||||
return false;
|
||||
}
|
||||
let config_string = config_read_res.unwrap();
|
||||
for line in config_string.lines(){
|
||||
if line.contains(":"){
|
||||
let line_vec: Vec<&str> = line.split(":").collect();
|
||||
if settings.contains_key(line_vec[0]){
|
||||
settings.remove(line_vec[0]);
|
||||
settings.insert(line_vec[0].to_string(), line_vec[1].to_string());
|
||||
}
|
||||
else{
|
||||
settings.insert(line_vec[0].to_string(), line_vec[1].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
print_success("read existing config file successfully!");
|
||||
println!("entering edit loop...");
|
||||
loop{
|
||||
println!("{}", config_string);
|
||||
let setting = get_user_input("which setting would you like to change? (ENTER DONE IN ALL CAPS WHEN YOU'RE FINISHED");
|
||||
let value = get_user_input("what would you like to change it to?");
|
||||
if setting.contains("DONE"){
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if settings.contains_key(&setting){
|
||||
settings.remove(&setting);
|
||||
settings.insert(setting, value);
|
||||
}
|
||||
else{
|
||||
settings.insert(setting, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !project_config.exists() {
|
||||
let projects_create_res = create_dir_all(&project_config);
|
||||
if projects_create_res.is_err() {
|
||||
print_error(
|
||||
"error creating projects directory!",
|
||||
Some(projects_create_res.err().unwrap().to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
if !note_templates_path.exists() {
|
||||
let note_template_create_res = create_dir_all(¬e_templates_path);
|
||||
if note_template_create_res.is_err() {
|
||||
print_error(
|
||||
"error createing note_templates directory!",
|
||||
Some(note_template_create_res.err().unwrap().to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
if new {
|
||||
println!("server_address|127.0.0.1:31337");
|
||||
println!("key_file|{}/key", &config_folder.display());
|
||||
println!("distrobox|yes");
|
||||
if !get_user_input("are these defaults ok?")
|
||||
.to_lowercase()
|
||||
.contains("y")
|
||||
{
|
||||
let server_address = get_user_input("what is your server address then?");
|
||||
let key_file = get_user_input("what is your key file then?");
|
||||
if get_user_input("will you be using distrobox for your attack environments?")
|
||||
.to_lowercase()
|
||||
.contains("y")
|
||||
{
|
||||
settings.insert("distrobox".to_string(), "yes".to_string());
|
||||
settings.insert(
|
||||
"templatebox".to_string(),
|
||||
get_user_input("name of your tempalte distrobox?"),
|
||||
);
|
||||
} else {
|
||||
settings.insert("distrobox".to_string(), "no".to_string());
|
||||
}
|
||||
settings.insert("server_address".to_string(), server_address);
|
||||
settings.insert("key_file".to_string(), key_file);
|
||||
} else {
|
||||
settings.insert("server_address".to_string(), "127.0.0.1:31337".to_string());
|
||||
settings.insert(
|
||||
"key_file".to_string(),
|
||||
format!("{}/key", config_folder.display()),
|
||||
);
|
||||
settings.insert("distrobox".to_string(), "yes".to_string());
|
||||
settings.insert(
|
||||
"templatebox".to_string(),
|
||||
get_user_input("name of the distrobox you will use?"),
|
||||
);
|
||||
}
|
||||
settings.insert("current_files".to_string(), get_user_input("full path to where you want your current project's files stored? example: /home/pyro/projects/current"));
|
||||
settings.insert("current_notes".to_string(), get_user_input("full path to where you want your current project's notes stored example: /home/pyro/notes/current"));
|
||||
settings.insert("upcoming_files".to_string(),get_user_input("full path to where you want your upcoming project's files stored example: /home/pyro/projects/upcoming"));
|
||||
settings.insert("upcoming_notes".to_string(), get_user_input("full path to where you want your upcoming project's notes stored exmple: /home/pyro/notes/upcoming"));
|
||||
print_success("sweet, we have all we need, writing config file...");
|
||||
let out_file_res = File::create_new(&config_file);
|
||||
if out_file_res.is_err() {
|
||||
print_error(
|
||||
"error creating config file!",
|
||||
Some(out_file_res.err().unwrap().to_string()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
let mut out_file = out_file_res.unwrap();
|
||||
for setting in settings.keys() {
|
||||
let outline = format!("{}|{}\n", setting, settings[setting]);
|
||||
let write_res = out_file.write(outline.as_bytes());
|
||||
if write_res.is_err() {
|
||||
print_error(
|
||||
"error writing to config file",
|
||||
Some(write_res.err().unwrap().to_string()),
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
write_res.unwrap();
|
||||
}
|
||||
}
|
||||
print_success("excellent we have created the client's config file!");
|
||||
println!("creating projects config file and adding the default project...");
|
||||
project_config.push("default.conf");
|
||||
let projects_file_res = File::create_new(project_config);
|
||||
if projects_file_res.is_err() {
|
||||
print_error(
|
||||
"error creating project config file!",
|
||||
Some(projects_file_res.err().unwrap().to_string()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
for key in settings.keys() {
|
||||
println!("{} : {}", key, settings[key]);
|
||||
}
|
||||
let mut project_file = projects_file_res.unwrap();
|
||||
let mut out_line = format!(
|
||||
"name|default\nstage|current\nfiles|{}\nnotes|{}",
|
||||
settings["current_files"], settings["current_notes"]
|
||||
);
|
||||
if settings["distrobox"] == "yes".to_string() {
|
||||
out_line = format!("{}\nboxname|{}", out_line, settings["templatebox"]);
|
||||
}
|
||||
let write_res = project_file.write(out_line.as_bytes());
|
||||
if write_res.is_err() {
|
||||
print_error(
|
||||
"error writing to projects config file!",
|
||||
Some(write_res.err().unwrap().to_string()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
println!("generating a new key for encryption...");
|
||||
let key = crytpo::generate_key();
|
||||
let mut key_path = config_folder.clone();
|
||||
key_path.push("key");
|
||||
if key_path.exists() {
|
||||
let remove_res = remove_file(&key_path);
|
||||
if remove_res.is_err() {
|
||||
print_error(
|
||||
"error removing keyfile",
|
||||
Some(remove_res.err().unwrap().to_string()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let key_file_res = File::create_new(key_path);
|
||||
if key_file_res.is_err() {
|
||||
print_error(
|
||||
"error making key file!",
|
||||
Some(key_file_res.err().unwrap().to_string()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
let mut key_file = key_file_res.unwrap();
|
||||
key_file.write(&key).unwrap();
|
||||
print_success("client successfully installed!");
|
||||
print_success("please re-run this tool to use it!");
|
||||
return true;
|
||||
}
|
||||
180
src/lib.rs
Normal file
180
src/lib.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
use std::{
|
||||
fs::{copy, create_dir_all},
|
||||
path::PathBuf,
|
||||
};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Table {
|
||||
columns: Vec<usize>,
|
||||
headers: String,
|
||||
data: Vec<String>,
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub fn build(&mut self, data: Vec<String>) -> Table {
|
||||
self.headers = data[0].clone();
|
||||
self.data = data[1..].to_vec();
|
||||
let header_vec: Vec<&str> = self.headers.split("|").collect();
|
||||
for header in header_vec {
|
||||
self.columns.push(header.len());
|
||||
}
|
||||
for data in &self.data {
|
||||
let data_vec: Vec<&str> = data.split("|").collect();
|
||||
for id in 0..self.columns.len() {
|
||||
if data_vec[id].len() > self.columns[id] {
|
||||
self.columns[id] = data_vec[id].len();
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in 0..self.columns.len() {
|
||||
if self.columns[id] % 2 != 0 {
|
||||
self.columns[id] += 1;
|
||||
}
|
||||
}
|
||||
return self.clone();
|
||||
}
|
||||
pub fn get_table(&self) -> String {
|
||||
let mut output = String::new();
|
||||
let mut spacer = String::new();
|
||||
let header_vec: Vec<&str> = self.headers.split("|").collect();
|
||||
for id in 0..self.columns.len() {
|
||||
spacer.push('|');
|
||||
let mut cell = String::new();
|
||||
let dashes = "-".repeat(self.columns[id]);
|
||||
spacer.push_str(&dashes);
|
||||
if header_vec[id].len() < self.columns[id] {
|
||||
let mut padding_needed = self.columns[id] - header_vec[id].len();
|
||||
if padding_needed % 2 != 0 {
|
||||
padding_needed += 1;
|
||||
}
|
||||
let padding = padding_needed / 2;
|
||||
cell = format!(
|
||||
"|{}{}{}",
|
||||
" ".repeat(padding),
|
||||
header_vec[id],
|
||||
" ".repeat(padding)
|
||||
);
|
||||
while cell.len() != self.columns[id] {
|
||||
if cell.len() > self.columns[id] + 1 {
|
||||
cell.pop();
|
||||
} else if cell.len() > self.columns[id] + 1 {
|
||||
cell.push(' ');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
output.push_str(&cell);
|
||||
} else {
|
||||
cell = format!("|{}", header_vec[id]);
|
||||
output.push_str(&cell);
|
||||
}
|
||||
}
|
||||
output.push_str("|\n");
|
||||
spacer.push_str("|\n");
|
||||
output.push_str(&spacer);
|
||||
output.push_str(&spacer);
|
||||
for data_line in self.data.clone() {
|
||||
let line_vec: Vec<&str> = data_line.split("|").collect();
|
||||
for id in 0..self.columns.len() {
|
||||
let mut cell = String::new();
|
||||
if line_vec[id].len() < self.columns[id] {
|
||||
let mut padding_needed = self.columns[id] - line_vec[id].len();
|
||||
if padding_needed % 2 != 0 {
|
||||
padding_needed += 1;
|
||||
}
|
||||
let padding = padding_needed / 2;
|
||||
cell = format!(
|
||||
"|{}{}{}",
|
||||
" ".repeat(padding),
|
||||
line_vec[id],
|
||||
" ".repeat(padding)
|
||||
);
|
||||
while cell.len() != self.columns[id] + 1 {
|
||||
if cell.len() > self.columns[id] + 1 {
|
||||
cell.pop();
|
||||
} else if cell.len() < self.columns[id] + 1 {
|
||||
cell.push(' ');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cell = format!("|{}", line_vec[id]);
|
||||
}
|
||||
output.push_str(&cell);
|
||||
}
|
||||
output.push_str("|\n");
|
||||
output.push_str(&spacer);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
address: String,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Message {
|
||||
source: Destination,
|
||||
destination: Destination,
|
||||
content: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Destination {
|
||||
Console,
|
||||
Server,
|
||||
Control,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Project {
|
||||
name: String,
|
||||
files: PathBuf,
|
||||
notes: PathBuf,
|
||||
current: bool,
|
||||
boxname: String,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn generate_default_notes(&self, config_folder: &PathBuf) -> String {
|
||||
let mut notes_template = config_folder.clone();
|
||||
notes_template.pop();
|
||||
notes_template.push("note_templates");
|
||||
if self.name.contains("external") {
|
||||
notes_template.push("external");
|
||||
} else if self.name.contains("internal") {
|
||||
notes_template.push("internal");
|
||||
} else if self.name.contains("vishing") {
|
||||
notes_template.push("vishing");
|
||||
} else if self.name.contains("phishing") {
|
||||
notes_template.push("phishing");
|
||||
} else if self.name.contains("webapp") {
|
||||
notes_template.push("webapp");
|
||||
}
|
||||
let walkdir = WalkDir::new(¬es_template);
|
||||
for res in walkdir {
|
||||
if res.is_ok() {
|
||||
let entry = res.unwrap();
|
||||
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||
if file_name.contains(".md") {
|
||||
let mut temp_path = self.notes.clone();
|
||||
temp_path.push(&file_name);
|
||||
let copy_res = copy(entry.path(), &temp_path);
|
||||
if copy_res.is_err() {
|
||||
return (format!(
|
||||
"Error copying note file {} to {}",
|
||||
file_name,
|
||||
temp_path.display()
|
||||
));
|
||||
}
|
||||
copy_res.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
return String::from("Success!");
|
||||
}
|
||||
}
|
||||
334
src/main.rs
Normal file
334
src/main.rs
Normal file
@@ -0,0 +1,334 @@
|
||||
use chacha20poly1305::aead::generic_array::GenericArray;
|
||||
use chacha20poly1305::aead::generic_array::typenum::Unsigned;
|
||||
use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng};
|
||||
use chacha20poly1305::{ChaCha20Poly1305, Key};
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{self, File, OpenOptions, read_dir, read_to_string};
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::net;
|
||||
use std::path::{Display, PathBuf};
|
||||
use std::process::{Output, exit};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use std::{thread, time};
|
||||
use tokio;
|
||||
use tokio::sync::mpsc::{Receiver, Sender, channel};
|
||||
|
||||
mod cli;
|
||||
mod commands;
|
||||
mod crytpo;
|
||||
mod install;
|
||||
mod lib;
|
||||
mod network;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(
|
||||
version,
|
||||
about,
|
||||
long_about = "The server part of tetanus! It will read the config and load up any known clients and stuff."
|
||||
)]
|
||||
struct Args {
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "the server to connect to, defaults to 127.0.0.1:31337"
|
||||
)]
|
||||
server: Option<String>,
|
||||
|
||||
#[arg(short, long, help = "launch in gui mode")]
|
||||
gui: bool,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "a path to a custom config file, defaults to ~/.config/tetanus/clients/main_attacker.conf"
|
||||
)]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
#[arg(short, long, help = "generate or re-generate the config file")]
|
||||
install: bool,
|
||||
|
||||
#[arg(short, long, help = "custom name to give this client.")]
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
pub fn print_success(text: &str) {
|
||||
println!("{}", text.green());
|
||||
}
|
||||
|
||||
pub fn print_error(text: &str, error: Option<String>) {
|
||||
println!("{}", text.red());
|
||||
if error.is_some() {
|
||||
println!("{}", error.unwrap().red());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_user_input(prompt: &str) -> String {
|
||||
let mut response = String::new();
|
||||
loop {
|
||||
println!("{}", prompt);
|
||||
let res = std::io::stdin().read_line(&mut response);
|
||||
if res.is_err() {
|
||||
print_error("we need input here dummy, try again...", None);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response.trim().to_string();
|
||||
}
|
||||
|
||||
pub fn load_projects(path: &PathBuf, display: bool) -> Vec<Project> {
|
||||
let mut projects_path = path.clone();
|
||||
projects_path.pop();
|
||||
projects_path.push("projects");
|
||||
let project_dir_res = read_dir(projects_path);
|
||||
if project_dir_res.is_err() {
|
||||
print_error(
|
||||
"error reading projects directory!",
|
||||
Some(project_dir_res.err().unwrap().to_string()),
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
let project_dir = project_dir_res.unwrap();
|
||||
let mut projects = Vec::new();
|
||||
for res in project_dir {
|
||||
if res.is_ok() {
|
||||
let mut new_project = Project::default();
|
||||
let entry = res.unwrap();
|
||||
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||
if file_name.contains(".conf") {
|
||||
let conf_string_res = read_to_string(entry.path());
|
||||
if conf_string_res.is_ok() {
|
||||
let conf_string = conf_string_res.unwrap();
|
||||
for line in conf_string.lines() {
|
||||
let line_vec: Vec<&str> = line.split("|").collect();
|
||||
match line_vec[0] {
|
||||
"name" => {
|
||||
new_project.name = line_vec[1].trim().to_string();
|
||||
}
|
||||
"stage" => {
|
||||
if line_vec[1].contains("current") {
|
||||
new_project.current = true;
|
||||
} else {
|
||||
new_project.current = false;
|
||||
}
|
||||
}
|
||||
"files" => {
|
||||
new_project.files = PathBuf::from(line_vec[1]);
|
||||
}
|
||||
"notes" => {
|
||||
new_project.notes = PathBuf::from(line_vec[1]);
|
||||
}
|
||||
"boxname" => new_project.boxname = String::from(line_vec[1]),
|
||||
_ => {
|
||||
print_error(
|
||||
"unknown setting discoverd in project config file!",
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if new_project.boxname.len() > 0 {
|
||||
projects.push(new_project);
|
||||
} else {
|
||||
new_project.boxname = String::from("none");
|
||||
projects.push(new_project);
|
||||
}
|
||||
if display {
|
||||
print_success(
|
||||
format!(
|
||||
"{} successfully loaded!",
|
||||
entry.file_name().to_string_lossy()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return projects;
|
||||
}
|
||||
|
||||
pub fn save_project(project: &Project, config_path: &PathBuf) {
|
||||
let mut conf_open_options = OpenOptions::new();
|
||||
if config_path.exists() {
|
||||
conf_open_options.append(true);
|
||||
} else {
|
||||
conf_open_options.create(true);
|
||||
}
|
||||
let conf_open_create_res = conf_open_options.open(config_path);
|
||||
if conf_open_create_res.is_err() {
|
||||
print_error(
|
||||
"error opening project config path!",
|
||||
Some(format!("{}", conf_open_create_res.err().unwrap())),
|
||||
);
|
||||
return;
|
||||
}
|
||||
let mut conf_file = conf_open_create_res.unwrap();
|
||||
let config_string = format!(
|
||||
"name|{}\nstage|upcoming\nfiles|{}\nnotes|{}\nboxname|{}",
|
||||
project.name,
|
||||
project.files.display(),
|
||||
project.notes.display(),
|
||||
project.boxname
|
||||
);
|
||||
write!(conf_file, "{}", config_string).unwrap();
|
||||
print_success("project saved!");
|
||||
}
|
||||
|
||||
pub fn load_settings(config_path: &PathBuf, display: bool) -> HashMap<String, String> {
|
||||
let mut settings = HashMap::new();
|
||||
let conf_read_res = read_to_string(&config_path);
|
||||
if conf_read_res.is_err() {
|
||||
print_error(
|
||||
"error reading config file!",
|
||||
Some(conf_read_res.err().unwrap().to_string()),
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
let conf_string = conf_read_res.unwrap();
|
||||
if display {
|
||||
println!("loading settings from config line...");
|
||||
}
|
||||
for line in conf_string.lines() {
|
||||
if line.contains("|") {
|
||||
let line_vec: Vec<&str> = line.split("|").collect();
|
||||
settings.insert(line_vec[0].to_string(), line_vec[1].to_string());
|
||||
if display {
|
||||
print_success(format!("{} {} LOADED!", line_vec[0], line_vec[1]).as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let args = Args::parse();
|
||||
let mut settings = HashMap::new();
|
||||
let mut config_path = PathBuf::new();
|
||||
let mut server_address = String::from("127.0.0.1:31337");
|
||||
if args.config.is_some() {
|
||||
config_path = args.config.unwrap();
|
||||
} else {
|
||||
let home_res = std::env::home_dir();
|
||||
if home_res.is_some() {
|
||||
config_path = home_res.unwrap();
|
||||
config_path.push(".config/tetanus/clients/main_attacker/config.conf");
|
||||
} else {
|
||||
print_error(
|
||||
"error finding config file!\nplease re-run while specifying a config file",
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
if args.install {
|
||||
let res = install::install(&config_path);
|
||||
if res {
|
||||
print_success("client successfully installed!");
|
||||
print_success("please re-run this tool to use it!");
|
||||
exit(0);
|
||||
}
|
||||
} else if !config_path.exists() {
|
||||
println!("ooof no config file exitst at {}", config_path.display());
|
||||
if get_user_input("would you like to create one?")
|
||||
.to_lowercase()
|
||||
.contains("y")
|
||||
{
|
||||
let status = install::install(&config_path);
|
||||
if !status {
|
||||
print_error("error installing...", None);
|
||||
exit(1);
|
||||
} else {
|
||||
print_success("client successfully installed!");
|
||||
print_success("please re-run this tool to use it!");
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
print_error("config file does not exist", None);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
let conf_read_res = read_to_string(&config_path);
|
||||
if conf_read_res.is_err() {
|
||||
print_error(
|
||||
"error reading config file!",
|
||||
Some(conf_read_res.err().unwrap().to_string()),
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
let conf_string = conf_read_res.unwrap();
|
||||
println!("loading settings from config line...");
|
||||
for line in conf_string.lines() {
|
||||
if line.contains("|") {
|
||||
let line_vec: Vec<&str> = line.split("|").collect();
|
||||
settings.insert(line_vec[0].to_string(), line_vec[1].to_string());
|
||||
print_success(format!("{} {} LOADED!", line_vec[0], line_vec[1]).as_str());
|
||||
}
|
||||
}
|
||||
let key_path = PathBuf::from(settings["key_file"].clone());
|
||||
let mut key_vec = Vec::new();
|
||||
let key_open_res = OpenOptions::new().read(true).open(key_path);
|
||||
if key_open_res.is_err() {
|
||||
print_error(
|
||||
"error opening key file!",
|
||||
Some(key_open_res.err().unwrap().to_string()),
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
let mut key_file = key_open_res.unwrap();
|
||||
let key_read_res = key_file.read(&mut key_vec);
|
||||
if key_read_res.is_err() {
|
||||
print_error(
|
||||
"error reading key",
|
||||
Some(key_read_res.err().unwrap().to_string()),
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
key_read_res.unwrap();
|
||||
|
||||
let projects = load_projects(&config_path, true);
|
||||
let mut server_address = String::from("127.0.0.1:31337");
|
||||
if args.server.is_some() {
|
||||
server_address = args.server.unwrap();
|
||||
}
|
||||
let (main_tx, mut main_rx) = channel(1024);
|
||||
let (console_tx, console_rx) = channel(1024);
|
||||
if !args.gui {
|
||||
let input_handle = tokio::spawn(cli::cli(
|
||||
projects,
|
||||
main_tx.clone(),
|
||||
console_tx.clone(),
|
||||
server_address,
|
||||
config_path,
|
||||
));
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let output_handle = tokio::spawn(cli::rec_message(console_rx));
|
||||
loop {
|
||||
sleep(Duration::from_secs(1));
|
||||
let rx_rex = main_rx.try_recv();
|
||||
if rx_rex.is_ok() {
|
||||
let message = rx_rex.unwrap();
|
||||
if message.destination == Destination::Control {
|
||||
match message.content.as_str() {
|
||||
"exit" => {
|
||||
input_handle.abort();
|
||||
output_handle.abort();
|
||||
exit(0);
|
||||
}
|
||||
_ => {
|
||||
println!("unknown message recieved!");
|
||||
println!("{}", message.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("gui coming soon!");
|
||||
}
|
||||
}
|
||||
17
src/network.rs
Normal file
17
src/network.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use tokio;
|
||||
use std::{io::Write, net::TcpStream};
|
||||
|
||||
use crate::print_error;
|
||||
|
||||
|
||||
pub async fn send_to_server(input: String, address: String) -> Option<String>{
|
||||
let connect_res = TcpStream::connect(address);
|
||||
if connect_res.is_err(){
|
||||
print_error("error connection to server", Some(connect_res.err().unwrap().to_string()));
|
||||
return Some(String::from("failed to connect to server!"));
|
||||
}
|
||||
let mut stream = connect_res.unwrap();
|
||||
let server_send_line = format!("1|||command|||0|||{}", input);
|
||||
stream.write(server_send_line.as_bytes()).unwrap();
|
||||
return None;
|
||||
}
|
||||
Reference in New Issue
Block a user