started work on writing the tool, its not quite ready yet, but we're

getting close.
This commit is contained in:
pyro57000
2025-11-14 16:53:06 -06:00
parent 47d6ed5556
commit 1a72bcee98
30 changed files with 2248 additions and 0 deletions

149
src/cli.rs Normal file
View 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
View 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(&notes_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
View 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
View File

229
src/install.rs Normal file
View 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(&note_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
View 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(&notes_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
View 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
View 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;
}