76 Commits
2.1 ... 3.1.9

Author SHA1 Message Date
Pyro57000
1a7e7d4326 added the start of the gui re-write
not ready yet, but its getting there.
2025-06-24 16:02:29 -05:00
Pyro57000
98800e3f3f started work on the gui rewrite, its not ready
yet but we're getting close!
2025-06-24 16:00:42 -05:00
Pyro57000
637fd5417f re-did how settings are handled and updated how to load the settings, modify them, and save them so that when you separate work and play it all works right this time! 2025-06-05 15:49:31 -05:00
Pyro57000
9fa1800337 added final success message 2025-06-04 09:55:06 -05:00
Pyro57000
e211433772 udated the exit operations to only prompt for
work vs personal if work and personal configs
exist.
2025-06-04 09:50:13 -05:00
Pyro57000
6d92032d06 added some functions to switch between managing
personal projects and managing work projects
as well as the ability to separate your current
project list into work and personal.
2025-06-02 12:18:59 -05:00
Pyro57000
925c3fd471 removed some warnings. 2025-05-30 10:26:40 -05:00
Pyro57000
21d8da25e3 Added a tablize function and did more
re coloring.

The tablize function isn't quite 100% there yet.
It mostly works, but I need to figure out word
wrapping for cells.
2025-05-30 10:24:45 -05:00
Pyro57000
f8ec8de137 removed more debugging print statements 2025-05-28 15:36:18 -05:00
Pyro57000
a3f42ecb3f removed some debugging print comands 2025-05-28 15:31:17 -05:00
Pyro57000
387c12a247 fixed the nmap scanning function! 2025-05-28 15:29:19 -05:00
Pyro57000
249ecc786d added fun colors!
currently added some green for the initialization
and red for errors, output color will be added
soon!
2025-05-28 12:59:39 -05:00
Pyro57000
6959e77d57 cleaned up all the warnings so it should compile without any! 2025-05-28 09:39:02 -05:00
Pyro57000
33450cb1ba ditto 2025-05-27 12:19:20 -05:00
Pyro57000
55a6faac2d added the ability to run dnstwist scans for
dns enumeration, and added the ability to modify
the tool's configuration.
2025-05-27 12:18:52 -05:00
Pyro57000
737bee37e7 added dns commands to the menu. 2025-05-20 11:52:46 -05:00
Pyro57000
76f153e981 added the default structure for vishing in the
start_pentest function.
2025-05-20 09:45:11 -05:00
Pyro57000
c4bda0022b added a few functions for dns enumeration
including a function that runs both the records
gathering command and the subdomain bruteforce
command and saves all the output to your notes

I also tweaked the gather contact parsing function
in order to save the output to the enumeration.md

This is important because the
print report information function actually works now!
it will read your enumeration notes and print the
data in a format that should paste into word
easily.
2025-05-19 16:57:53 -05:00
Pyro57000
aef65f3b03 added the command to build the nmap portscan
command instead of just running it.
I think running it is broken atm.
2025-05-16 16:16:07 -05:00
pyro57000
9d8154c7a1 added a function to update all your git tools. 2025-05-12 14:53:46 -05:00
pyro57000
4e38749c86 added a function to print the sharpersist
command to add a scheduled task!

also updated it so that it creates a backup
of your projects.conf file incase the working one
gets corrupted somehow.
2025-05-05 13:36:23 -05:00
pyro57000
0913703976 added some fucntion, note the portscan fucntion
is still borked... working on it.
2025-05-01 12:50:50 -05:00
pyro57000
fbd524ae7f added some functions, note portscanning is borked
still working on it.
2025-05-01 12:50:18 -05:00
pyro57000
dabca4c20a added more verbose help features. 2025-04-30 15:37:41 -05:00
pyro57000
7dec1cb0cb added the menu back, and added project specific
submenus that you can drop into!
2025-04-30 14:30:59 -05:00
pyro57000
a0b6065040 jsut comitting becasue rust analyzer brokey 2025-04-30 10:29:33 -05:00
pyro57000
f99e395241 fixed a bug where switching projects wouldn't
switch projects.
2025-04-30 09:59:50 -05:00
pyro57000
0cbe33357a added clear screen function to cli 2025-04-22 12:57:39 -05:00
pyro57000
4bd67a486f added project information to the cli prompt 2025-04-22 12:53:31 -05:00
pyro57000
12e5ab38d2 bumped the version in the cargo.toml file 2025-04-22 12:46:23 -05:00
pyro57000
f2370e463d added help function to cli 2025-04-22 12:42:38 -05:00
pyro57000
92dd9766b8 refactored for CLI!!! 2025-04-22 12:10:45 -05:00
pyro57000
dbbae0eb4e added a function to parse your host notes and
generate the attack notes based off of them.
2025-04-16 17:28:39 -05:00
pyro57000
2346988e23 added an option to parse cs portscann output 2025-04-15 13:08:48 -05:00
pyro57000
5bfa645b3d added a url for the obsidian notes of the current
project, and to get the vault name from the user
at install.
2025-04-09 10:42:00 -05:00
pyro57000
012ba517ab added a function to info_controls that parses
GatherContacts output and prints them to the
console as well as saves them to both a text file
in the /pentest/working directory as well as your
notes in a password_enumeration.md file.
2025-04-08 12:48:07 -05:00
pyro57000
649dad5e79 chaged some working for the menu 2025-04-07 09:03:25 -05:00
pyro57000
2e839adccf Fixed the installer on Kali linux 2025-04-05 17:49:16 -05:00
pyro57000
fc316c2a71 Completely re-wrote the install function.
Trying to get this working on default kali.
2025-04-04 20:17:35 -05:00
pyro57000
31dd862579 fixed logic for configuring distrobox during
install, and fixing some functions in info_controls
They may not be quite ready yet.
2025-04-01 11:41:24 -05:00
pyro57000
b85d0e5d79 added some logic to give you some time to
enter the sudo password when required for
cobalt strike.
2025-03-27 11:49:53 -05:00
pyro57000
477ac6e360 Added logic and settings for enabling fingerprint
authentication within distro box
2025-03-19 14:52:21 -05:00
pyro57000
fe6524016f added a check to make sure cobalt strike exists
before spawning a thread.
2025-03-13 15:27:15 -05:00
pyro57000
967052a4e2 cleaned up un-needed library imports from
box_controls.
2025-03-13 15:03:14 -05:00
pyro57000
2796d31f14 added logic to launch bloodhound with the
distrobox.
2025-03-13 15:02:01 -05:00
pyro57000
b137e0c34d switched the cobalt strike command to be
.output instead of .status to prevent
it from locking you out of returning to the
main menu.
2025-03-12 12:52:02 -05:00
pyro57000
8a1dca30e1 added logic to copy the cobaltstrike
folder into your project directory
and an option to launch that cobaltstrike
instance within the distrobox of your project
this should help keep cobalt strike data separate
also took a stab at cleaning up the left over
folders in the upcomming files and notes directories
the files folder cleans up now, but the notes folder
still needs work.
2025-03-12 12:48:21 -05:00
pyro57000
854feb2e2a added install logic to configure distrobox
to not pull the container image always.
this should save space, and fix cloning on
universal blue distros.
2025-03-12 10:50:37 -05:00
pyro57000
bfa766a2c3 fixed the new folders not being created first 2025-03-12 09:56:54 -05:00
pyro57000
f7bdd0ad8a fixed promotion function to use the project name
folder as well!
2025-03-12 09:49:15 -05:00
pyro57000
16706f8f6d fixed some stuff with box creation. 2025-03-12 09:39:19 -05:00
pyro57000
b6ec849b36 added some ideas for future development 2025-03-06 12:22:54 -06:00
pyro57000
69f5a4bd78 added stopping the template box to ensure
that cloning operations works
2025-03-06 11:53:11 -06:00
pyro57000
cad1f9d51c removed one redundant line 2025-03-06 09:24:33 -06:00
pyro57000
837012d163 fixed note folder creation with new pentest option 2025-03-06 09:22:15 -06:00
pyro57000
4dba68cdcf refactor for upcomming project management 2025-03-05 14:56:17 -06:00
pyro57000
7765640e0e refactor for managing upcoming and current
projects!
2025-03-05 14:55:56 -06:00
pyro57000
d50625167b added --init to distrobox creation
this will allow us to do things like docker
inside of distroboxes
2025-02-20 13:20:02 -06:00
pyro57000
a798e39461 did the distrobox stuff 2025-02-20 13:09:12 -06:00
pyro57000
4e1ab4c30f split making a distrobox into its own function
this will allow us to write more functions
like the newly added one to re-create the
current project's distrobox
2025-02-20 13:08:20 -06:00
pyro57000
ac037a15a9 fixed the start_pentest function 2025-02-13 14:14:00 -06:00
Pyro57000
345124baf1 Update README.md 2025-01-21 10:57:15 -06:00
Pyro57000
a9a451a8cf Update ToDo.md 2025-01-21 10:52:12 -06:00
Pyro57000
7081280247 Update ToDo.md 2025-01-21 10:51:57 -06:00
Pyro57000
2d97e81920 Create ToDo.md 2025-01-21 10:51:28 -06:00
pyro57000
761f71c6c1 modified the install to avoid the newlines
in folder names

added hash cracking function, though this doesn't
work yet...
2025-01-21 10:31:03 -06:00
pyro57000
882afe0f67 added logic to save already sprayed passwords
based on passwordspray markdown file.
2025-01-16 13:43:08 -06:00
pyro57000
1f47ff8229 just cleaned up a mutable 2025-01-15 15:58:11 -06:00
pyro57000
f40c0e31c1 added logic to save and come back to a password
spray.
2025-01-15 13:56:09 -06:00
pyro57000
bf95a375fb added logic to do msolspray as well!
this isn't tested very well, but give it a shot!
2025-01-15 13:28:12 -06:00
pyro57000
00e19bc1b4 added logic to display correct command for
user as pass sprays.
2025-01-15 12:07:41 -06:00
pyro57000
fd64caefc1 forgot to add execute-assembly to one line 2025-01-09 14:13:44 -06:00
pyro57000
e8b557bb4e edited install function to actually work right
the bell file was trying to be written
too soon, this has been fixed.
2025-01-09 14:08:46 -06:00
pyro57000
b2822a614a added password spray help funciton, and a bell.
also added downloading the bell file to the install
2025-01-09 13:55:36 -06:00
Pyro57000
37a942b8b3 Add files via upload 2025-01-09 13:17:52 -06:00
Pyro57000
a56e2f990c Create credits.txt 2025-01-09 13:17:20 -06:00
23 changed files with 10553 additions and 719 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/pentest_tool/target

View File

@@ -53,11 +53,11 @@ Current engagements: client1 internal pentest, client 2 internal pentest
This tool automatically creates the file structure, and if you use the start_pentest option populates the markdown note files with the templates I use. This tool automatically creates the file structure, and if you use the start_pentest option populates the markdown note files with the templates I use.
For example if I get a new internal pentest engagement for client 3 but it hasn't started yet I'll start by creating the folder and note structure using option 4 in the menu system. This would create the folder structure and notes templates in the /home/pyro/pentests/upcomming/client3/internal_pentest and /home/pyro/notes/upcomming/client3/internal_pentest folders respectively. It does not create the distrobox or add it to the tracked project list yet. For example if I get a new internal pentest engagement for client 3 but it hasn't started yet I'll start by creating the folder and note structure using option 4 in the menu system. This would create the folder structure and notes templates in the /home/pyro/pentests/upcomming/client3/internal_pentest and /home/pyro/notes/upcomming/client3/internal_pentest folders respectively. It does not create the distrobox, but it does add it to the list of tracked projects, with a status of upcomming.
Once the engagement starts I'd run option 6 "import new project - and set up new distrobox" from the menu system. It will ask if you have an existing folder structure to copy over, since I created that structure before with option 4 I would answer y, yes, or anything with the letter y in it. Then it prompts you to past the path to the folders to copy over. Here I browse to /home/pyro/pentests/upcomming in dolphin. I right click on the client3 folder and select "copy path" and then paste that into the terminal. I repeat this process for the notes folder. Once the engagement starts I'd run option 9 to promote a project from upcomming status to current status. This will automatically copy the needed files over, and will clean up the empty directory in the upcomming files directory, but the empty folder in the upcomming notes folder is still not cleaning correctly, I'm working on that.
It then attempts to guess the client name and project name based on the folder structure, and asks you to confirm. If its correct the project object will be created, the folders will be copied, and a new distrobox will be set up. If it is not correct it will prompt you to correct it, then copy the files, create a new project object, and setup a new distrobox. At the moment it is not very fault tollerant and will fail if there is an existing folder with the same name. I plan to fix that later, but haven't gotten around to it yet. Once the distro box is set up (this takes a while) it will return you to the main menu. It then will create a new distrobox and ask you if you need to set up cobalt strike for the project. if so it'll ask you for the path to your cobalt strike, it'll copy that into the project files folder so that you can run it if needed.
During the test I use the menu system to spawn new terminals in the distrobox created for the engagement, use the various file generation options to get data in easily usable format for attack operations, etc. During the test I use the menu system to spawn new terminals in the distrobox created for the engagement, use the various file generation options to get data in easily usable format for attack operations, etc.
@@ -75,7 +75,8 @@ Once the project is done and I'm ready to clean up the distrobox I use option 7
1. clone this repository `git clone https://github.com/Pyro57000/pentest_tool.git` 1. clone this repository `git clone https://github.com/Pyro57000/pentest_tool.git`
2. cd into the nested "pentest_tool" folder `cd pentest_tool/pentest_tool` 2. cd into the nested "pentest_tool" folder `cd pentest_tool/pentest_tool`
3. use cargo to build the release binary `cargo build --release` 3. use cargo to build the release binary `cargo build --release`
4. follow the same installation instructions, skipping the step where you download the release binary. 4. copy the compiled binary to a folder on your path `sudo cp ./target/release/pentest_tool /usr/bin/`
5. follow the same installation instructions, skipping the step where you download the release binary.

29
ToDo.md Normal file
View File

@@ -0,0 +1,29 @@
# planned features
1.) finish hash cracking with a dedictated cracking rig code.
2.) cracte hash cracking with current computer code.
4.) create code that moves projects to a "writing" state and folder.
5.) create code that tracks "current, upcomming, and writing" states, maybe automatic zipping of folders after writing is done?
upcoming and current are done, but need to add writing yet...
# Unplanned, but would be cool
1.) create a "server" and "Client" infrastructure that can help manage the distrobox clients and the main server
2.) maybe expand this server client model to interact with cracking rigs and what not.
3.) implment a function to searchsploit and copy wanted exploits to the project folder.
4.) implement a function to execute those copied exploits.
5.) add a "client" model for compromised internal hosts, this would likely need to be a server from a networking perspective that listens on 127.0.0.1 and then have the main interface connect to that over a proxy to run jobs. a good starting point would be to run some of our enumeration commands and scripts and save the output to the notes directly.
6.) add the ability to interact with the cobalt strike beacons... would need to learn agressor script... what a pain...
# NOTE
if you wish to contribute, please do! just fix a bug or implement any of the above features and make a pull request!!
I'll keep plugging away as I have time throughout my days as well.

5409
pentest_tool/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,19 @@
[package] [package]
name = "pentest_tool" name = "pentest_tool"
version = "0.2.0" version = "3.1.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
chrono = "0.4.39"
clearscreen = "3.0.0" clearscreen = "3.0.0"
colored = "3.0.0"
directories = "5.0.1" directories = "5.0.1"
dns-lookup = "2.0.4"
fs_extra = "1.3.0" fs_extra = "1.3.0"
futures-io = { version = "0.2.0-beta" }
iced = "0.13.1"
reqwest = {version = "0.12.12", features = ["blocking", "json"]}
rfd = "0.15.3"
rodio = "0.20.1"
term_size = "0.3.2"
walkdir = "2.5.0"

View File

@@ -0,0 +1,7 @@
default:default:/home/pyro/syncs/work_folder/FRSecure/notes/Hack_Notes/pentest_notes/:/home/pyro/syncs/work_folder/FRSecure/pentests/:no:atarchbox:current
altimate_medical:2025_internal_pentest:/home/pyro/syncs/work_folder/FRSecure/notes/Hack_Notes/pentest_notes/current/altimate_medical:/home/pyro/syncs/work_folder/FRSecure/pentests/current/altimate_medical/2025_internal_pentest:yes:atarchbox_altimate_medical:current
fennemore_law:2025_vishing_test:/home/pyro/syncs/work_folder/FRSecure/notes/Hack_Notes/pentest_notes/current/fennemore_law/2025_vishing_test:/home/pyro/syncs/work_folder/FRSecure/pentests/current/fennemore_law/2025_vishing_test:no:atarchbox_fennemore_law:current
horizontal:2025_internal_pentest:/home/pyro/syncs/work_folder/FRSecure/notes/Hack_Notes/pentest_notes/current/horizontal/2025_internal_pentest:/home/pyro/syncs/work_folder/FRSecure/pentests/current/horizontal/2025_internal_pentest:no:atarchbox_horizontal:current
NDSU_Foundation:2025_internal_pentest:/home/pyro/syncs/work_folder/FRSecure/notes/Hack_Notes/pentest_notes/current/NDSU_Foundation/2025_internal_pentest:/home/pyro/syncs/work_folder/FRSecure/pentests/current/horizontal/2025_internal_pentest:no:atarchbox_NDSU_Foundation:current
offsec:osed:/home/pyro/syncs/personal_folder/2nd-brain:/home/pyro/syncs/personal_folder/tech-hacking-stuff/active_projects/offsec/osed:no:atarchbox_offsec:current
frsecure:2025_external_pentest:/var/home/pyro/syncs/work_notes/Hack_Notes/pentest_notes/current/frsecure/2025_external_pentest:/var/home/pyro/syncs/work_folder/FRSecure/pentests/current/frsecure/2025_external_pentest:no:atarchbox_frsecure:current

Binary file not shown.

View File

@@ -1,5 +1,15 @@
use std::process; use std::os::unix::process::CommandExt;
use crate::Project; use std::process::Command;
use std::{path::PathBuf, process};
use std::env;
use std::fs;
use std::io::Write;
use std::thread::{self, JoinHandle};
use std::time::Duration;
use colored::Colorize;
use iced::theme::palette::Success;
use crate::{get_user_input, Project};
pub fn stop_all_boxes(projects: &Vec<Project>){ pub fn stop_all_boxes(projects: &Vec<Project>){
let mut problem_childs: Vec<Project> = Vec::new(); let mut problem_childs: Vec<Project> = Vec::new();
@@ -31,12 +41,17 @@ pub fn stop_all_boxes(projects: &Vec<Project>){
} }
} }
pub fn project_standalone_terminal(project: Project, mut terminal: String){ pub fn project_standalone_terminal(project: Project, mut terminal: String, display: &bool) -> Option<String>{
let mut return_data = String::new();
terminal = terminal.trim_end().to_owned(); terminal = terminal.trim_end().to_owned();
let mut profile = false; let mut profile = false;
if terminal.contains("profile"){ if terminal.contains("profile"){
profile = true; profile = true;
} }
if terminal.contains("!!!"){
let terminal_launch_cmd = format!("distrobox enter --root {}", &project.boxname);
terminal = terminal.replace("!!!", &terminal_launch_cmd);
}
let terminal_vec:Vec<&str> = terminal.split(" ").collect(); let terminal_vec:Vec<&str> = terminal.split(" ").collect();
let mut terminal_start = process::Command::new(terminal_vec[0]); let mut terminal_start = process::Command::new(terminal_vec[0]);
let mut first = true; let mut first = true;
@@ -54,12 +69,374 @@ pub fn project_standalone_terminal(project: Project, mut terminal: String){
print!("{}", terminal); print!("{}", terminal);
let start = terminal_start.spawn(); let start = terminal_start.spawn();
match start{ match start{
Ok(_child) => println!("New Terminal Started, you can retun to the menu now!"), Ok(_child) => {
Err(_error) => println!("error starting new terminanl, something may be wrong with the terminal part of your config!\nCheck ~/.conf/pyro_pentest_tool/conf for errors") if *display{
println!("New Terminal Started!");
return None;
}
else{
return_data.push_str("string New Terminal Started!");
return Some(return_data);
}},
Err(_error) => {
if *display{
println!("error starting new terminanl, something may be wrong with the terminal part of your config!\nCheck ~/.conf/pyro_pentest_tool/conf for errors");
return None;
}
else{
return_data.push_str("error starting new terminanl, something may be wrong with the terminal part of your config!\nCheck ~/.conf/pyro_pentest_tool/conf for errors");
return Some(return_data);
}}
} }
//process::Command::new("konsole").arg("--profile").arg("attack").arg(project.boxname).spawn().expect("error opeing konsole"); //process::Command::new("konsole").arg("--profile").arg("attack").arg(project.boxname).spawn().expect("error opeing konsole");
} }
pub fn project_inline_terminal(project: Project){ pub fn project_inline_terminal(project: Project, given_command: Option<String>) -> Option<String>{
process::Command::new("distrobox").arg("enter").arg("--root").arg(project.boxname).arg("--").arg("script").arg("-a").arg("-B").arg("/pentest/working/terminal.log").status().expect("error opeing konsole"); let mut return_data = String::new();
if given_command.is_none(){
process::Command::new("distrobox").arg("enter").arg("--root").arg(project.boxname).arg("--").arg("script").arg("-a").arg("-B").arg("/pentest/working/terminal.log").status().expect("error opeing konsole");
return None;
}
else{
let command_string = given_command.unwrap();
let command_vec: Vec<&str> = command_string.split(" ").collect();
let mut command = process::Command::new("distrobox");
command.arg("enter");
command.arg("--root");
command.arg(project.boxname);
command.arg("--");
for arg in command_vec{
command.arg(arg);
}
let com_res = command.output();
if com_res.is_err(){
let error = com_res.err().unwrap().to_string();
return_data.push_str("\nerror running command!");
return_data.push_str(&error);
return Some(return_data);
}
let output = com_res.unwrap();
let success_out = output.stdout;
let fail_out = output.stderr;
if success_out.len() > 0{
let out_string = format!("\n{}", String::from_utf8_lossy(&success_out));
return_data.push_str(&out_string);
}
if fail_out.len() > 0 {
let out_string = format!("\n{}", String::from_utf8_lossy(&fail_out));
return_data.push_str(&out_string);
}
return Some(return_data);
}
}
fn make_box_cli(project: &Project, tools_dir: &PathBuf, boxtemplate: &String, new: bool, fingerprint: bool){
println!("stopping template box to ensure we can clone it!");
let stop_result = Command::new("distrobox").arg("stop").arg("--root").arg(boxtemplate).status();
if stop_result.is_err(){
println!("error stopping template!");
}
if !new{
let _distrobox_stop_status = process::Command::new("distrobox").arg("stop").arg("--root").arg(&project.boxname).status().expect("error stopping distrobox");
let distrobox_rm_status = process::Command::new("distrobox-rm")
.arg("--root")
.arg("-f")
.arg(&project.boxname)
.status().expect("error calling distrobox");
if distrobox_rm_status.success(){
println!("Distrobox Removal Successful!!!");
}
else{
println!("Distrobox Removal Failed, manual removal required!");
}
}
let mut box_name_path = project.files_folder.clone();
let box_name = format!("{}_{}", &boxtemplate, &project.customer);
box_name_path.push("boxname");
let mut box_name_file = fs::File::create(box_name_path).expect("Error creating box name file");
box_name_file.write_all(&box_name.as_bytes()).expect("error writing boxname to box file");
let pentest_volume = format!("{}:/pentest:rw", &project.files_folder.display());
let toold_volume = format!("{}:/tools:rw", tools_dir.display());
let mut distrobox_cmd = String::new();
if fingerprint{
println!("creating box with shared volume for fingerprints... note you will still need to set up fingerprint authentication in your distrobox");
println!("\nfor example, you may need to install fprintd and imagemegick on your template box, and set up the pam files to utilize finger print auth");
println!("\nsee https://wiki.archlinux.org/title/Fprint for more information and instructions");
distrobox_cmd = format!("distrobox create --root --init --clone {} --volume {} --volume {} --volume /var/lib/fprint:/var/lib/fprint:rw --name {}", boxtemplate, toold_volume, pentest_volume, box_name);
}
else {
distrobox_cmd = format!("distrobox create --root --init --clone {} --volume {} --volume {} --name {}", boxtemplate, toold_volume, pentest_volume, box_name);
}
println!("{}", distrobox_cmd);
let distrobox_cmd_vec: Vec<&str> = distrobox_cmd.split_whitespace().collect();
let mut distrobox_cmd_build = process::Command::new("distrobox");
let mut first = true;
for word in &distrobox_cmd_vec{
if first == false{
println!("adding {} as an argument for the creation command...", word);
distrobox_cmd_build.arg(word);
}
else{
first = false;
}
}
let distrobox_result = distrobox_cmd_build.status();
if distrobox_result.is_err(){
println!("oooof we ran into trouble creating your distrobox!!");
println!("try creating it manually!");
println!("{}", distrobox_cmd);
}
else{
if distrobox_result.unwrap().success(){
println!("we made a distrobox oh boy!");
let distrobox_start_result = process::Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(&box_name)
.arg("--")
.arg("sudo")
.arg("-s")
.arg("ln")
.arg("-sf")
.arg("/pentest/boxname")
.arg("/etc/boxname")
.status();
if distrobox_start_result.is_err(){
println!("ooof did not start successfully try entering it yoruself");
println!("distrobox enter --root {} -- sudo -s ln -sf /pentest/boxname /etc/boxname", &box_name);
}
else if distrobox_start_result.unwrap().success(){
println!("distrobox was started as well!!!! good job me!");
}
else {
println!("ooof did not start successfully try entering it yoruself");
println!("distrobox enter --root {} -- sudo -s ln -sf /pentest/boxname /etc/boxname", &box_name);
}
}
}
}
fn make_box_gui(project: &Project, tools_dir: &PathBuf, boxtemplate: &String, new: bool, fingerprint: bool, terminal: String) -> Option<String>{
terminal.replace("!!!", "");
println!("{}", terminal);
let terminal_vec: Vec<&str> = terminal.split(" ").collect();
let terminal_command = terminal_vec[0].to_owned();
println!("term_command = {}", terminal_command);
let mut terminal_args: Vec<String> = Vec::new();
if terminal.contains("konsole"){
terminal_args.push(String::from("-e"));
}
else{
for arg in &terminal_vec[1..]{
terminal_args.push(format!("{}", arg));
}
}
for arg in &terminal_args{
println!("{}", arg);
}
let mut terminal_launch_cmd = process::Command::new(&terminal_command);
for arg in &terminal_args{
terminal_launch_cmd.arg(arg);
}
let _status = terminal_launch_cmd.arg("distrobox").arg("stop").arg("--root").arg(boxtemplate).status().unwrap();
if new{
let mut terminal_launch_cmd = process::Command::new(&terminal_command);
for arg in &terminal_args{
terminal_launch_cmd.arg(arg);
}
let _status = terminal_launch_cmd.arg("distrobox")
.arg("stop").arg("--root")
.arg(&project.boxname)
.arg("&&")
.arg("distrobox")
.arg("rm")
.arg("--root")
.arg("-f")
.arg(&project.boxname)
.status()
.unwrap();
}
let mut new_box_cmd = process::Command::new(&terminal_command);
for arg in &terminal_args{
new_box_cmd.arg(arg);
}
let tools_line = format!("{}:/tools:rw", project.files_folder.display());
let files_line = format!("{}:/pentest:rw", tools_dir.display());
let _status = new_box_cmd.arg("distrobox")
.arg("create")
.arg("--root")
.arg("--clone")
.arg(boxtemplate)
.arg("--volume")
.arg(files_line)
.arg("--volume")
.arg(tools_line)
.arg("--name")
.arg(&project.boxname)
.arg("--init")
.status()
.unwrap();
let mut boxname_cmd = process::Command::new(&terminal_command);
for arg in &terminal_args{
boxname_cmd.arg(arg);
}
let _status = boxname_cmd.arg("distrobox")
.arg("enter")
.arg("--root")
.arg(&project.boxname)
.arg("--")
.arg("echo")
.arg(&project.boxname)
.arg(">")
.arg("/pentest/boxname")
.arg("&&")
.arg("sudo")
.arg("ln")
.arg("ln")
.arg("-sf")
.arg("/pentest/boxname")
.arg("/etc/boxname")
.status()
.unwrap();
return Some(String::from("Distrobox created successfull!"));
}
#[allow(unused)]
pub fn make_box(project: &Project, tools_dir: &PathBuf, boxtemplate: &String, new: bool, fingerprint: bool, terminal: Option<String>, display: &bool) -> Option<String>{
if *display{
make_box_cli(project, tools_dir, boxtemplate, new, fingerprint);
return None;
}
else{
let output = make_box_gui(project, tools_dir, boxtemplate, new, fingerprint, terminal.expect("this should only be called when the terminal is set!"));
return output;
}
}
pub fn clean_unused_boxes(projects: &Vec<Project>, boxtemplate: &String) -> Option<JoinHandle<()>>{
println!("starting template box: {}", boxtemplate);
let template_status = process::Command::new("distrobox").arg("enter").arg("--root").arg(boxtemplate).arg("--").arg("exit").status();
if template_status.is_err(){
let start_error = template_status.err().unwrap();
println!("OOOF issue starting template box, cancelling...");
println!("ERROR: {}", start_error);
return None;
}
println!("starting project boxes...");
for project in projects{
if project.stage.contains("current"){
let start_status = process::Command::new("distrobox").arg("enter").arg("--root").arg(&project.boxname).arg("--").arg("exit").status();
if start_status.is_err(){
let start_error = start_status.err().unwrap();
println!("OOOF issue starting {}, cancelling...", project.boxname);
println!("ERROR: {}", start_error);
return None;
}
}
}
println!("pruning unused containers...");
let handle = thread::spawn(move ||{
let spawn_result = process::Command::new("sudo").arg("podman").arg("system").arg("prune").arg("-a").arg("-f").output();
if spawn_result.is_err(){
let spawn_error = spawn_result.err().unwrap();
println!("oof trouble spawing prune command!, try manually");
println!("ERROR: {}", spawn_error);
}
println!("PRUNING COMPLETE!");
});
thread::sleep(Duration::from_secs(3));
println!("this will take some time, but its running on a different thread so you can continue working!");
return Some(handle);
}
pub fn launch_cobalt_strike(project: Project) -> Option<JoinHandle<()>>{
let mut check_path = PathBuf::new();
check_path.push(&project.files_folder);
check_path.push("cobaltstrike/client");
if check_path.as_path().exists() == false{
println!("looks like you don't have cobalt strike set up on this beacon... try copying your cobalt strike folder to the following path!");
println!("{}", &check_path.display());
return None;
}
println!("you will need to enter your sudo password shortly, when you've done that successfully please type END and hit enter");
let handle = thread::spawn(move ||{
let mut cs_dir = PathBuf::new();
cs_dir.push(project.files_folder);
cs_dir.push("cobaltstrike/client");
let cd_res = env::set_current_dir(&cs_dir);
if cd_res.is_ok(){
let cobalt_strike_launch_result = Command::new("distrobox")
.arg("enter")
.arg("--root").arg(project.boxname)
.arg("--")
.arg("./cobaltstrike")
.output();
if cobalt_strike_launch_result.is_err(){
let error = cobalt_strike_launch_result.err().unwrap();
println!("{}", "error launching cobalt strike!".red());
println!("{}", error.to_string().red());
}
}
});
thread::sleep(Duration::from_secs(5));
loop{
let end = get_user_input("please enter END if the sudo password was enter correctly!");
if end == "END".to_string(){
break;
}
}
return Some(handle);
}
#[allow(unused)]
pub fn launch_bloodhound_gui(project: Project) -> Option<JoinHandle<()>>{
let mut bloodhound_command = String::new();
let version_response = get_user_input("do you want to use a specific bloodhound version?");
if version_response.to_lowercase().contains("y"){
bloodhound_command = get_user_input("path to the bloodhound binary you want to use?");
}
else{
bloodhound_command = "bloodhound".to_owned();
}
let mut neo4j_started = false;
let neo4jstart_res = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(&project.boxname)
.arg("--")
.arg("sudo")
.arg("neo4j")
.arg("start")
.status();
if neo4jstart_res.is_err(){
let error = neo4jstart_res.err().unwrap();
println!("{}","errror starting neo4j...".red());
println!("{}", error.to_string().red());
}
else{
neo4j_started = true;
thread::sleep(Duration::from_secs(3));
}
if neo4j_started == false{
println!("review previous errors, not spawning bloodhound thread...");
return None;
}
let handle = thread::spawn(move ||{
let bloodhound_res = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(&project.boxname)
.arg("--")
.arg(bloodhound_command)
.output();
if bloodhound_res.is_err(){
let error = bloodhound_res.err().unwrap();
println!("{}","error starting bloodhound!".red());
println!("{}", error.to_string().red());
}
});
return Some(handle);
} }

454
pentest_tool/src/cli.rs Normal file
View File

@@ -0,0 +1,454 @@
use std::collections::HashMap;
use std::fs;
use std::process::Termination;
use rfd::FileDialog;
use std::fs::read_to_string;
use std::fs::OpenOptions;
use std::path::PathBuf;
use std::process::exit;
use std::io::Write;
use std::thread::JoinHandle;
use chrono::Datelike;
use clearscreen::clear;
use clearscreen;
use chrono::Local;
use colored::Colorize;
use crate::configuration::load_config;
use crate::print_error;
use crate::print_informational;
use crate::print_success;
use crate::Project;
use crate::project_controls;
use crate::box_controls;
use crate::info_controls;
use crate::start_pentest;
use crate::get_user_input;
use crate::portscan_controls;
use crate::victim_commands;
use crate::enumeration;
use crate:: tool_controls;
use crate::configuration;
use crate::tableize;
fn help(command: Option<String>){
if command.is_some(){
let help_cmd = command.unwrap();
let mut lines = Vec::new();
lines.push("COMMAND||ALIASES||INFO".to_owned());
match help_cmd.as_str(){
"list projects" | "lp" | "listp" | "list p" => lines.push("list projects||lp, listp, list p||This command lists all projects currently tracked by the pentest_tool".to_owned().to_owned()) ,
"switch project" | "swp" | "switch p" | "switchp" => lines.push(" switch project|| swp, switch p, switchp||This command will switch the active project from the current one to a new on of your choosing. It will prompt you to make a selection.".to_owned()) ,
"show active project" | "show active" | "sa" | "show a" => lines.push(" show active project|| sa, show a||This command shows information about the currently active project. NOTE the most useful information is already displayed above the CLI prompt.".to_owned()) ,
"create new project" | "cnp" | "new project" | "np" => lines.push(" create new project|| cnp, new project, np||This command creates a new project and default note structure based on pyro's perferred note structure. It will prompt you for any needed information.".to_owned()) ,
"save projects" | "sp" | "save" | "s" => lines.push(" save projects|| sp save||This command saves all project information to the ~/.config/pyro_pentest_tool/projects.conf file".to_owned()) ,
"import project" | "ip" | "import" => lines.push(" import project|| ip, import||This command will impot an existing project and set up a new distrobox for the project if it is a current project.".to_owned()) ,
"remove project" | "rp" | "remove" | "rmp" => lines.push("remove project||rp, remove, rmp||This command removes a project from the list of projects tracked by the pentest_tool, and will destroy its distrobox. It does not remove any directories used or created by the distrobox. Run this after the report is written.".to_owned()) ,
"show upcoming projects" | "sup" | "show upcoming" => lines.push("show upcoming projects||sup, show upcoming||This command shows a list of upcoming projects. Use this to verify which project you want to promote when the project enters the active phase.".to_owned()) ,
"promote project" | "pp" | "promote" => lines.push("promote project||pp (ha), promote||This command promotes an upcoming project to a current project. It will copy the folders that were created over to the current project space and set up a new distrobox for the project.".to_owned()) ,
"new terminal" | "enter" | "enter terminal" | "nt" | "et" => lines.push("new terminal||enter, enter terminal, nt, et||This command spawns a new terminal window in the active project's distrobox. Use this to interact with your project's distrobox.".to_owned()) ,
"inline terminal" | "it" | "enter inline" | "ei" => lines.push("inline terminal||it enter, inline, ei||This command spawns a terminal in this window using the current active project's distrobox".to_owned()) ,
"cobalt strike" | "cs" => lines.push("cobalt strike||cs||This command opens cobalt strike in the active project's distrobox, and spins it off as a new thread to ensure it doesn't block the rest of this tools operation. NOTE the cobalt strike window will need to be closed before this tool exits sucessfully.".to_owned()) ,
"recreate distrobox" | "rdb" | "ndb" | "new distrobox" => lines.push("recreate distrobox||rdb, ndb, new distrobox||This command destroyes the existing distrobox for the currently active project, and clones a new one based on the current state of the template distrobox.".to_owned()) ,
"generate userpass" | "userpass" | "gup" | "up" => lines.push("generate userpass||userpass, gup, up||This command generates a userpass file based on the active project's notes. The file will be in the username:password format.".to_owned()) ,
"inital enum" | "ie" | "enum" => lines.push("initial enum||ie, enum||This command runs the initial enum script on a nessus csv and saves the output to the active project's notes in the host_notes.md file.".to_owned()) ,
"build attack notes" | "ban" | "attack notes" | "hn" => lines.push("build attack notes||ban, attack notes||This command builds the active project's attack note based on the active project's host notes (for external tests). It is expected that you'd run the initial enum command, then manually fill out the enumeration talbes with correct service names and ports.".to_owned()) ,
"host discovery" | "build host discovery" | "hd" | "bhd" => lines.push("host discovery||build host discovery, hd, bhd||This command prints the host discovery ping command for the active project, based on the scope table in the general.md notes file.".to_owned()) ,
"cobaltstrike port scan" | "cs port scan" | "csps" => lines.push("port scan||cs port scan, cobaltstrike port scan, csps, ps (tell your cat I said that)||This command prints the cobalt strike portscan command based on the active project's scope table in the general.md notes file".to_owned()) ,
"parse port scan" | "pps" | "parse scan" => lines.push("parse port scan||pps, parse scan||This commmand parses a cobalt strike portscan TSV and saves interesting hoests to files to the active project's files folder. The host files are designated with the service that was detected that might be interesting. Use this to generate target lists for specific protocols.".to_owned()) ,
"stop boxes" | "stop distroboxes" | "sdb" => lines.push("stop boxes||stop distroboxes, sdb||This command stops all distroboxes for the tracked projects. Note if the distrobox isn't running you will see errors in the console, you can safely ignore these.".to_owned()) ,
"password spray" | "pass spray" | "pas" => lines.push("password spray||pass spray, pas||This command iterates through the password spray note file and print the command to perform the spray, waiting the proper observation window beteen commands. It prompts you to save if needed. NOTE this will block execution for the rest of the program until it is either finished, or you save and exit the password spray function. I'm working on making this better.".to_owned()) ,
"bloodhound" | "bh" => lines.push("bloodhound||bh||This command launches bloodhound in the active project's distrobox. It will automatically start neo4j before staring bloodhound.".to_owned()) ,
"parse gather contacts" | "pgc" | "parse contacts" | "pc" => lines.push("parse gather contacts||pgc, parse contacts, pc||This command parses output saved from the gather contacts burpsuite extension.".to_owned()) ,
"prune distroboxes" | "pdb" | "prune" => lines.push("prune distroboxes||pdb, prune||this command prunes distroboxes for all projects that are not being tracked by this tool (frees up system storage). This will start all the currently acvtive distorboxes to ensure they don't get pruned, and then will delete all the not-started distrobox volumes and resources.".to_owned()) ,
"clear" | "clear screen" | "cls" => lines.push("clear||clear screen, cls||This command clears the screen of command output.".to_owned()) ,
"exit" => lines.push("exit||(none)\nThis command exits the pentest_tool, it will save all project infomation, and wait for all threads to re-join the main thread before exiting.".to_owned()) ,
"settings" => lines.push("||The settings file is located at ~/.config/pyro_pentest_tool/conf||The format is setting_name:setting_value.||Needed settings are\n project_files - the place to store current project files folders|| projtect_notes - the place to store current project notes|| tools_folder - the place to store custom tools like those downloaded from github|| upcoming_files - the place to store upcoming project files folders|| upcoming_notes - The place to store upcoming project note files|| box_template - the name of the distrobox you want to clone for project boxes|| terminal - the command you use to launch a terminal, while running a specific Ex: konsole -e || cracking_rig - the user and host you use for a personal cracking rig in the openssh formating: Ex: pyro@cracking_rig or pyro@192.168.1.101 if you do not have a cracking rig the default is @n|| rockyou_location - the location on the cracking rig for the rockyou.txt file default is n|| rule_location - the location on the cracking rig for the one rule to rule them all file. Default is n|| pass_file - this is the location where you store your standard password spray file. If you do not have a custom one this tool provides one. The default is ~/.config/pyro_pentest_tool/passwordspary.md|| fingerprint - this is whether you want fingerprint authentication within your distroboxes, takes y/n|| vault_name - the name of your obsidian vault, default is notes||".to_owned()) ,
"parse normal nmap file" | "pnnf" | "parse nmap" | "pn" => lines.push("parse normal nmap file|| pnnf, parse nmap, pn||This command parses the normal output of an nmap scan (like if you just tee'd or >'d it to a file) and outputs in host:port or int the coablt strike tsv format. It will attempt to find the file within the active project's files folder, and if it can't find the file it will prompt you for input.".to_owned()),
"sharpersist command" | "spc" | "sharp scheduled task" | "sst" => lines.push(" sharpersist command|| spc, sharp scheduled task, sst||This comand prints the commands to run to use sharpersist.exe to create a scheduled task that runs hourly called FRPersist.".to_owned()),
"port scan" | "ps" | "nmap" | "nmap scan" | "ns" | "nm" => lines.push(" port scan|| ps, nmap, nmap scan, ns, nm||This command runs an nmap scan against the scope in the active projects notes, and saves the output.".to_owned()),
"show scope" | "ss" | "show s" | "s s" | "scope" => lines.push("show scope||ss, show s, s s, scope||This command displays the current project's scope as just the hosts in the scope table in your notes.".to_owned()),
"port scan command" | "psc" | "nmap command" | "nmc" => lines.push("port scan command||psc,nmap command, nmc||This command will print the nmap command to manually run a scan to the terminal so you can copy paste it.".to_owned()),
"update git tools" | "ugt" | "update git" | "ug" => lines.push(" update git tools|| update git, ugt, ug||This command attempts to update the git tools in your tools directory, it will attempt to update every directory as a git project. If the directory is not a git project it should just error out and continue to the next one.".to_owned()),
"dns records" | "dr" => lines.push("dnsrecords||dr||This command will run dns recon inside of your distrobox and save the results to your enumeration notes.".to_owned()),
"brute force subdomains"| "bsd" | "gobuster dns" | "gd" => lines.push("brute force subdomains||bsd,gobuster dns, gd||this command will run gobuster in the project's distrobox and save the results to your notes.".to_owned()),
"dns enumeration" | "de" | "all dns stuff" | "ads" | "dns stuff" | "ds" => lines.push("dns enumeration||de, all dns stuff, ads, dns stuff, de||This command will perform both dns record enumeration with dnsrecon, and subdomain enumeration using gobster inside of your distrobox and save the output to your notes.".to_owned()),
"modify tool config" | "mtc" => lines.push("modify tool config|| mtc||This command lets you modify the tool's configuration.".to_owned()),
"separate work and personal projects" | "swpp" | "separate projects" | "seppro" => lines.push("separate work and personal||swpp, separate projects, seppro||This command lets you separate work and personal projects into separate config files. This allows you to load personal and work projects separately!".to_owned()),
"switch to personal projects" | "switch personal" => lines.push("switch to personal projects||switch personal||This command lets you switch which config file is loaded to the personal config file.".to_owned()),
"switch to work projects" | "switch work" => lines.push("switch to work projects||switch work||This command lets you switch to load the work projects.".to_owned()),
_ => ()
}
tableize(lines, &true);
}
else{
println!("Welcom to Pyro's pentest command line!");
println!("the pentest_tool uses a configuration file to store your settings. This configuration file is located at ~/.config/pyro_pentest_tool/conf.\nYou can modify this file, but do so cautiously, incorrect formatting will cause this program to have a bruh moment.\nThe correct format is setting_name:setting_value\nExample:\nprojecT_files:/var/home/pyro/pentests/current");
println!("for help configuring the settings file run help settings");
let lines = vec!["command||aliases".to_owned(),
"menu||main menu, mm".to_owned(),
"list projects||lp, listp, list p".to_owned(),
"switch project||sp, switch p, switchp".to_owned(),
"show active project||show active, sa, show a".to_owned(),
"create new project||cnp, new project, np".to_owned(),
"save projects||sp, save, s".to_owned(),
"import project||ip, import".to_owned(),
"remove project||rp, remove, rmp".to_owned(),
"show upcoming project||sup, show upcoming".to_owned(),
"promote project||pp, promote".to_owned(),
"new terminal||enter, enter terminal, nt, et".to_owned(),
"inline terminal||it, enter inline, ei".to_owned(),
"cobalt strike||cs".to_owned(),
"recreate distrobox||rdb, ndb, new distrobox".to_owned(),
"generate userpass||userpass, gup, up".to_owned(),
"inital enum||ie, enum".to_owned(),
"host discovery ||build host discovery, hd, bhd".to_owned(),
"port scan||cs port scan, cobaltstrike port scan, csps, ps".to_owned(),
"parse port scan ||pps, parse scan".to_owned(),
"stop boxes||stop distroboxes, sdb".to_owned(),
"password spray||pass spray, pas".to_owned(),
"bloodhound||bh".to_owned(),
"parse gather contacts||pgc, parse contacts, pc".to_owned(),
"prune distroboxes||pdb, prune".to_owned(),
"clear||clear screen, cls".to_owned(),
"parse nomral nmap file||pnnf, parse nmap, pn".to_owned(),
"show scope||ss, show s, s s, scope".to_owned(),
"sharpersist command||spc, sharp scheduled task".to_owned(),
"port scan||ps, nmap, nmap scan, ns, nm".to_owned(),
"port scan command||psc, nmap command, nmc".to_owned(),
"update git tools||ugt, update git, ug".to_owned(),
"dns records||dr".to_owned(),
"brute force subdomain||bsd, gobuster dns, gd".to_owned(),
"dns enumeration||de, all dns stuff, ads, dns stuff, ds".to_owned(),
"modify tool config||mtc".to_owned(),
"separate work and personal projects||swpp, separate projects, seppro".to_owned(),
"switch to personal projects||switch personal".to_owned(),
"switch to work projects||switch work".to_owned(),
"help||?, -h".to_owned()];
println!("available commands:");
tableize(lines, &true);
}
}
pub fn get_active_project(projects: &Vec<Project>) -> Option<&Project>{
let mut active_project = &projects[0];
for project in projects{
if project.active == true{
active_project = project
}
}
return Some(active_project)
}
pub fn next_project_id(config_path: &PathBuf) -> Option<i32>{
let projects_res = project_controls::get_projects(config_path, false);
if projects_res.is_none(){
println!("{}", "Error loading projects!!".red());
return None;
}
let projects = projects_res.unwrap();
let mut new_id = 0;
for project in projects.clone(){
if project.id > new_id{
new_id = project.id + 1;
}
}
return Some(new_id);
}
#[allow(unused)]
pub fn run_command(cmd: String,
settings: &HashMap<&str, String>,
config_path: &PathBuf,
mut projects: &mut Vec<Project>,
display: bool,
given_data: Option<String>) -> (Option<Option<JoinHandle<()>>>, Option<String>) {
let base_files = PathBuf::from(settings["project_files"].clone());
let base_notes = PathBuf::from(settings["project_notes"].clone());
let tools_dir = PathBuf::from(settings["tools_folder"].clone());
let boxtemplate= settings["box_template"].clone();
let terminal = settings["terminal_command"].clone();
let cracking_rig = settings["cracking_rig"].clone();
let rockyou = settings["rockyou"].clone();
let rule = settings["rule"].clone();
let upcoming_files = PathBuf::from(settings["upcoming_files"].clone());
let upcoming_notes = PathBuf::from(settings["upcoming_notes"].clone());
let password_spray_file = PathBuf::from(settings["pass_spray_file"].clone());
let fingerprint = settings["fingerprint"].to_lowercase().contains("y");
let vault_name = settings["vault_name"].clone();
let new_id_res = next_project_id(&config_path);
if new_id_res.is_none(){
println!("{}", "failed to get new project ID!".red());
return (None, None);
}
let mut new_id = new_id_res.unwrap();
let active_project_res = get_active_project(&projects);
if active_project_res.is_none(){
println!("{}", "failed to get active project!".red());
return (None, None);
}
let active_project = active_project_res.unwrap();
let mut notes_folder_string = format!("{}", &active_project.notes_folder.display());
let mut obsidian_folder_vec = PathBuf::new();
let mut reached_vault_folder = false;
for folder in notes_folder_string.split("/").collect::<Vec<&str>>(){
if !folder.contains(&vault_name){
reached_vault_folder = true;
obsidian_folder_vec.push(folder);
}
else{
if reached_vault_folder{
obsidian_folder_vec.push(folder);
}
}
}
let obsidian_uri = format!("obsidian://open?vault={}&file={}", vault_name, obsidian_folder_vec.display().to_string().replace("/", "%2F"));
let mut response = String::new();
let now = Local::now();
let month = now.month();
let year = now.year();
let mut season = String::new();
let mut lseason = String::new();
match month{
12 | 01 | 02 => {season = "Winter".to_owned(); lseason = "Fall".to_owned()},
03 | 04 | 05 => {season = "Spring".to_owned(); lseason = "Winter".to_owned()},
06 | 07 | 08 => {season = "Summer".to_owned(); lseason = "Spring".to_owned()},
09 | 10 | 11 => {season = "Fall".to_owned(); lseason = "Summer".to_owned()},
_ => {println!("error getting season! Check code..."); exit(1)}
}
if cmd.contains("help"){
if cmd.contains(" "){
let help_with = &cmd.split(" ").collect::<Vec<&str>>()[1].to_owned();
help(Some(help_with.to_owned()));
}
else{
help(None);
}
return (None, None);
}
//change for stuff, one sec.
match cmd.as_str(){
"list projects" | "lp" | "listp" | "list p" => {project_controls::list_projects(&projects, &display); return (None, None)},
"switch project" | "swp" | "switch p" | "switchp" => {project_controls::switch_project(&mut projects); return (None, None)},
"show active project" | "show active" | "sa" | "show a" => {let output = format!("\nclient: {}\n\nproject: {}\n\nbox: {}\n\nproject files: {}\n\nproject notes: {}\n", active_project.customer ,active_project.project_name, active_project.boxname, active_project.files_folder.display(), active_project.notes_folder.display()); return (None, Some(output))},
"create new project" | "cnp" | "new project" | "np" => {new_id = new_id + 1; start_pentest::start_pentest(&config_path, &mut projects, new_id, &upcoming_files, &upcoming_notes, &boxtemplate, &password_spray_file); return (None, None)},
"save projects" | "sp" | "save" | "s" => {project_controls::save_projects(&projects, &config_path, &display); return (None, None)},
"import project" | "ip" | "import" => {new_id = new_id + 1; project_controls::new_project(&mut projects, &base_files, &base_notes, &tools_dir, &boxtemplate, &config_path, new_id, &upcoming_files, &upcoming_notes, fingerprint, Some(terminal), &display); return (None, None)},
"remove project" | "rp" | "remove" | "rmp" => {project_controls::remove_project(&mut projects, &config_path, None); return (None, None)},
"show upcoming projects" | "sup" | "show upcoming" => {project_controls::print_upcoming_projects(&projects, &display); return (None, None)},
"promote project" | "pp" | "promote" => {project_controls::promote_project(&mut projects.clone(), &config_path, &active_project.files_folder, &active_project.notes_folder, &tools_dir, &boxtemplate, fingerprint, given_data, terminal); return (None, None)},
"new terminal" | "enter" | "enter terminal" | "nt" | "et" => {box_controls::project_standalone_terminal(active_project.clone(), terminal.clone(), &display); return (None, None)},
"inline terminal" | "it" | "enter inline" | "ei" => {box_controls::project_inline_terminal(active_project.clone(), given_data); return (None, None)},
//"cobalt strike" | "cs" => {let cs_thread = box_controls::launch_cobalt_strike(active_project.clone(), &display); return (Some(cs_thread), None)},
"recreate distrobox" | "rdb" | "ndb" | "new distrobox" => {box_controls::make_box(&active_project, &tools_dir, &boxtemplate, false, fingerprint, Some(terminal), &display); return (None, None)},
//"generate userpass" | "userpass" | "gup" | "up" => {info_controls::generate_userpass(&active_project, &display); return (None, None)},
//"inital enum" | "ie" | "enum" => {info_controls::run_initial_enum(&active_project, &display); return (None, None)},
//"build attack notes" | "ban" | "attack notes" | "hn" => {portscan_controls::build_cmd_for_host_discovery(&active_project, &display); return (None, None);}
//"host discovery" | "build host discovery" | "hd" | "bhd" => {portscan_controls::build_cmd_for_host_discovery(&active_project, &display); return (None, None)},
//"cobaltstrike port scan" | "cs port scan" | "csps" => {portscan_controls::build_cs_portscan_cmd(&active_project, &display); return (None, None)},
//"parse port scan" | "pps" | "parse scan" => {portscan_controls::parse_csportscan(&active_project, None, None, &display); return (None, None)},
//"stop boxes" | "stop distroboxes" | "sdb" => {box_controls::stop_all_boxes(&projects, &display); return (None, None)},
//"password spray" | "pass spray" | "pas" => {info_controls::password_spray_help(&active_project, season, lseason, year, &tools_dir, &config_path, &display); return (None, None)},
//"bloodhound" | "bh" => {let bloodhound_handle = box_controls::launch_bloodhound_gui(active_project.clone(), &display); return (Some(bloodhound_handle), None);},
//"parse gather contacts" | "pgc" | "parse contacts" | "pc" => {info_controls::partse_gathercontacts(&active_project, &display); return (None, None)},
//"prune distroboxes" | "pdb" | "prune" => {let prune_thread = box_controls::clean_unused_boxes(&projects, &boxtemplate, &display); return (Some(prune_thread), None)},
//"parse normal nmap file" | "pnnf" | "parse nmap" | "pn" => {portscan_controls::parse_normal_nmap_output(&active_project, &display); return (None, None);},
//"show scope" | "ss" | "show s" | "s s" | "scope" => {let scope_res = info_controls::get_scope_entries(&active_project, &display); if scope_res.is_some(){for host in scope_res.unwrap(){println!("{}", host)}}return (None, None)},
//"update git tools" | "ugt" | "update git" | "ug" => {tool_controls::update_git_tools(&tools_dir, &display); return (None, None)},
//"port scan" | "ps" | "nmap" | "nmap scan" | "ns" | "nm" => {portscan_controls::run_nmap_portscan(&active_project, &display); return (None, None);},
//"port scan command" | "psc" | "nmap command" | "nmc" => {portscan_controls::build_nmap_command(&active_project, &display); return (None, None);}
//"sharpersist command" | "spc" | "sharp scheduled task" | "sst" => {victim_commands::sharp_persist_command(&tools_dir, &display); return (None, None);},
//"dns records" | "dr" => {let dns_handle = enumeration::run_dns_enumeration(&active_project, None, true, , &display); return (Some(dns_handle), None);},
//"brute force subdomains"| "bsd" | "gobuster dns" | "gd" => {let gobuster_handle = enumeration::bruteforce_subs(&active_project, None,None, true, &display); return (Some(gobuster_handle), None)},
//"dns enumeration" | "de" | "all dns stuff" | "ads" | "dns stuff" | "ds" => {let all_dns_handle = enumeration::do_all_dns_enumeration(&active_project, &display); return (Some(all_dns_handle), None)},
//"dns squatting scan" | "dnstwist" | "dss" => {let twist_handle = enumeration::dns_squatting(&active_project, None, true, &display); return (Some(twist_handle), None)},
//"print report information" | "pri" => {info_controls::print_report_information(&active_project, &display); return (None, None);},
//"modify tool config" | "mtc" => {configuration::generate_tool_config(&config_path, &display); return (None, None);},
//"separate work and personal projects" | "swpp" | "separate projects" | "seppro" => {project_controls::separate_personal_work_projects(&config_path, &display); return (None, None);}
//"switch to personal projects" | "switch personal" => {let mut project_load_res = project_controls::swith_to_personal(&config_path, &display); if project_load_res.is_some(){projects.clear();for project in project_load_res.unwrap(){projects.push(project);}}; return (None, None);},
//"switch to work projects" | "switch work" => {let mut project_load_res = project_controls::swith_to_work(&config_path, &display); if project_load_res.is_some(){projects.clear();for project in project_load_res.unwrap(){projects.push(project);}}; return (None, None);},
//"show settings" | "print settings" | "shset" | "pset" => {configuration::print_settings(&settings, &display); return (None, None)},
_ => {help(None); println!("\n\n unknown command."); return (None, None);}
}
}
fn print_banner(banner: &str){
print!("{}", banner.custom_color((255,165,0)));
}
#[allow(unused)]
pub fn cli(mut projects: &mut Vec<Project>, config_path: &PathBuf) {
let mut threads = Vec::new();
let mut loopize = true;
let mut work_loaded = true;
let mut workspace_config_path = config_path.clone();
let mut workspace_settings_path = config_path.clone();
workspace_config_path.pop();
workspace_settings_path.pop();
let mut project_conf_path = config_path.clone();
let mut success_message = String::new();
project_conf_path.pop();
project_conf_path.push("projects.work");
let worksapces_in_use = project_conf_path.exists();
if worksapces_in_use{
work_loaded = get_user_input("is thie loading your work projects?").to_lowercase().contains("y");
}
let banner = "
,,,;;::ccccc::;;;::c::;,;::cccccllc::::::;:::;;;;,,;,'',,;,,;;;;;;;:;;;;;,,,,,,,,,,,'''''',,,,,,''''
,;;;::ccccc::::::ccc:;;;:ccccccclc::ccccccc::;;;;;;;;;;,,;;;;;;;;;;;;;;;,,,,,,,,,,,'''''''''',,,,,''
,;;:::ccc:cc:::ccc:::::::ccccclcccllccccllc::::::;;;;;;;;;;;;;;;;;;;;;;,,,,,,,,,,,''''''''...'',,,,'
,;;:::c::ccc::cc::::::::cclollllllolllllccccc::cc:::::;;;;;;;;;;;;;;;;;;,,,,,,,,,,'''''''''''..'',,,
,;::::::ccc::cc::::::ccloodollooooollllcccccc:llc::::::;;;;;;:;;;;;;;;;;;,,,,,,,,,,''''''''''''''',,
,;:::::c:::c::::ccccloddxxddxxddddodollllccclclcccccc:::::::::::::::;;;;;;;;,,,,,,,,,'''''''''''''',
;;:::::::c::c::clllodxxO0OKX0kkOkkxxxxxdooooolcccccccc:::::::cllc::::::::;;;;;,,,,,,,,,,'''''''''',,
;:::::c:cc::cclolclokO0KXNNX00KKK0O0KOxdxxdooccccclllccccccccdkdlcccccllcc::;;;;;;;;,,,,,,,,,,,',,,,
::::::cc::::coxdlllok00KNWNXXX0KXKOKNOkO0kddocccllllllccccclx0Kkodddoodddoollc::;;;;;;;;;,,,,,,,,,,,
:::::::c:::clkkodooxxkO0KX0xookKKkkKNKO0KkdoodolcllllollolldKNNXKKXKKKKKK0Okxdocc:cc:::;;;;;,,,,,,,,
::cc::cc::cldxllolodxxdoddc'.,okkxOXXOdxkxolkOdlllllllldkdokXNNNNNNNNX0kxollcc:::::cclc::;;;;;;,,,,,
:::::::cccldko:,.';cc:;:;....;clllOXOxxOkocoK0xooddollx0Odd0XNNNNNX0Oxdolcccc::;;;;;;:cllc:;;:;,,,,,
;;::c:::ccldkl;'...,''''....',;,';dxdkkdc;cONKxxOOOxddOXOdx0XXNNWNOdddxkOOOOkdllc:;,,,;cool:;;;;;,,;
,;;::;;::llco:,'..''..,.......''.';:ldl;,,:xXNOOXXX0xdkOOddkXNNWWWX00KXNNX0kxddddol:,''';lol:;;:;;,;
,,,;;;;;:coc;;'..;;. .,,;'.......':dxdc;ldc,l00xkXNKxodkkkkk0XNWWMWWWNXKOxdooolooool:;'..,lol::::;;;
',,,;;;;:cllc;,..',. ','. .....;odoo:;co:.'ldldOOx::x0KXX0kk0XNWWWXOxdoooollllllllcc:'..':lc:::;;;
',,,;;;;;:cccc:,. . ..;cccccc:,,''.',,:l:;;:oOXXKOOOkxOXNNNXOxddooooollllllllc,....:c:::;;;
''',,,;;;;;;;cll,.. .. .':lc:c:;,,......,,;:;;:cokkxxO00O0KXNNN0kxkkkxddoollllllll:'...':::::::
.''',,,,,,,,,;:c:,.. ..'. ..','',;;'........',,;:;:::cdxxxddkKXXXKKKKXXXXXX0kdoloolllol;....,;:::::
..'''',,'',,,;;:::;..... ............... .'.....,;,',:ldc;:ldOKKK00KNWWWNNXK0xoooodooooo:'...';;;:;;
....'''''',,;;::cll:,''...... . ..........'...,;;l:,,;oddkOOKNWWWWNX0kdodxxxxdddooc,...',;;;;,
......''''',;::cloodddol;. ...........',.;;,,',:cxdd0KXXKKKXKOkxxkkkxdddooc,...';;,,,,
........''',;:clloddxxdo:'. .. ...........''.'',;c:;cccodddk0KX0OOkxddddddo:...';;;;,,'
..........',;:cclodxxkxdl:,.. ... ......'....'..':c,..'.,c,,,.,cxkO00Okxdddddc'..';:;;;,,'
..........',;;:cloodxkkkdol:'. . ... ...... ......';c'.'...:;',;,'..,lxO00Oxxxo:'...,::;;,,,,
...........',;;:clodxkOOkxdol;. .. .. ... ....',::'.''.',.........'oxdxxxdl;...';::;;;,,''
............',;:clodxkkOOOxddo;. ...... ........',,',................';:clc;,...';::;;,,,'''
............',;:cldxkkOkkkxdddo;. ..... .........,'...........'''','.',,'''....,cc:;;,,'''..
.............';:cldxxkkkkxddddxl,. .... .;c;'...................',;;cc;'...';clolc:;,,'''...
............'';clodxkkkkkxddddddl' ... .:lc;'................. ....',,''';lxkxdlc:;,'''....
........',,;:;coddxkOOOOOkxxddddd:. ... ..,''.................. . ..;cdkkkkxoc:;,'''.....
......',;::cllodxkkOOOOOOkxxxddddc. ... ..,;,'................... .. .':odO0Okdl:;,'''......
.....',;:cloddxxkOOOOOOOkkxxdoooo;. .. ......................... .';cokOOxlc:;,''.......
....,;:clodxxkkOOOkO0OOOOxdlcc;;,...... .';,................. ...',:ldxxxdlc;,''.......
...,:clodooxkkkO0OxO00OOxo:;;,. ........ .''.......... .. .. ..,,,;:codxxdlc:;,'.......
'',;clodolokOkxkOkkO00Oko:;;;. ..... .. .,,........'. .. .. .. ..........;:codocclc:,,'......
___ __ __ ___ __ ___ __
| | |__ | / ` / \\ |\\/| |__ |__| /\\ / ` |__/ |__ |__)
|/\\| |___ |___ \\__, \\__/ | | |___ | | /~~\\ \\__, | \\ |___ | \\
__ ___ ___ __ __
/ _` |__ | |__) | | |\\ | | |\\ | / _`
\\__> |___ | | |/\\| | \\| | | \\| \\__>
";
print_banner(banner);
while loopize{
let settings = configuration::load_config(config_path);
project_controls::save_projects(&projects, &config_path, &true);
let active_project_res = get_active_project(&projects);
if active_project_res.is_none(){
println!("{}", "failed to get active project!".red());
return;
}
let active_project = active_project_res.unwrap();
let current_information = format!("
Active Project: {}, {}
Project Status: {}
Files Folder: {}
Notes Folder: {}
Boxname: {}
Obsidian URI: {}
for help enter help or ?. for information about a specific command enter help (command)
", active_project.customer.green(), active_project.project_name.green(), active_project.stage.green(), active_project.files_folder.display().to_string().green(), active_project.notes_folder.display().to_string().green(), active_project.boxname.green(), "coming soon".red());
load_config(config_path);
let prompt = format!("\n{}:{}\nCommand?", active_project.customer.custom_color((255,165,0)), active_project.project_name.custom_color((255,165,0)));
let command = get_user_input(&prompt);
if command.contains("switch work") || command.contains("switch to work projects"){
work_loaded = true;
}
else if command.contains("switch personal") || command.contains("switch to personal projects"){
work_loaded = false;
}
match command.as_str(){
"exit" => loopize = false,
"print banner" | "banner" => print_banner(banner),
"clear" | "clear screen" | "cls" => {clear().unwrap(); print_banner(banner);},
"list threads" | "threads" | "lst" => println!("There are {} threads still running.", threads.len()),
"info" => println!("{}", current_information),
"test_open" => {let file = FileDialog::new().set_directory(active_project.files_folder.clone()).pick_file(); println!("{}", file.unwrap().display())},
"test_table" => {let mut lines = Vec::new(); lines.push("column1||column2||column3||column4".to_owned()); lines.push("thing1||thing2||thing3||thing4".to_owned()); lines.push("ttttttttttttttttthing5||thing6||thing7||thing8".to_owned()); tableize(lines, &true);}
_ => {let (thread_option, output) = run_command(command, &settings, config_path, &mut projects, true, None); if thread_option.is_some(){threads.push(thread_option.unwrap());} if output.is_some(){print_success(output.unwrap());};},
}
}
project_controls::save_projects(&projects, &config_path, &true);
if get_user_input("do you want to stop all the boxes?").contains("y"){
box_controls::stop_all_boxes(&projects);
}
print_informational("saving workspace projects...");
let worksapces_in_use = project_conf_path.exists();
if worksapces_in_use{
if work_loaded{
print_informational("saving projects.work");
workspace_config_path.push("projects.work");
workspace_settings_path.push("conf.working");
success_message = String::from("projects.conf saved to projects.work\nconf saved to conf.working");
}
else{
print_informational("saving projects.personal");
workspace_config_path.push("projects.personal");
workspace_settings_path.push("conf.personal");
success_message = String::from("projects.conf saved to projects.personal\nconf saved to conf.personal");
}
let open_res = OpenOptions::new().create(true).write(true).open(workspace_config_path);
if open_res.is_err(){
print_error("error opeing workspace config file!", open_res.err().unwrap().to_string());
}
else{
project_conf_path.pop();
project_conf_path.push("projects.conf");
let mut workspace_config_file = open_res.unwrap();
let projects_read_res = read_to_string(project_conf_path);
if projects_read_res.is_ok(){
let project_string = projects_read_res.unwrap();
let write_res = write!(workspace_config_file, "{}", project_string);
if write_res.is_err(){
print_error("error writing workspace config file!", write_res.err().unwrap().to_string());
}
}
else{
print_error("error reading projects config file!", projects_read_res.err().unwrap().to_string());
}
}
let remove_res = fs::remove_file(&workspace_settings_path);
if remove_res.is_err(){
print_error("error removing to save over workspace settings!", remove_res.err().unwrap().to_string());
}
else{
remove_res.unwrap();
let copy_res = fs::copy(config_path, &workspace_settings_path);
if copy_res.is_err(){
print_error("error copying worksapce settings!", copy_res.err().unwrap().to_string());
}
}
print_success(success_message);
}
if threads.len() > 0{
println!("closing threads...");
println!("note this will hang until all threads have completed");
println!("please make sure to close all spawned programs such as cobalt strike and bloodhound.");
for thread in threads{
if thread.is_some(){
thread.unwrap().join().unwrap();
}
}
}
if worksapces_in_use{
print_success("projects saved to projects.conf, workspace projects updated, workspace settings saved, threads finished. pentest_tool OUT!");
}
else{
print_success("projects saved to projects.conf, threads finished. pentest_tool OUT!");
}
}

View File

@@ -0,0 +1,188 @@
use std::{collections::HashMap, path::PathBuf};
use std::fs::read_to_string;
use std::io::Write;
use colored::Colorize;
use crate::{get_user_input, open_overwrite, tableize};
#[allow(unused)]
pub fn generate_tool_config(config_dir: &PathBuf){
let mut config_file_path = config_dir.clone();
println!("{}", config_file_path.display());
let mut current_config = String::new();
let current_config_read_res = read_to_string(&config_file_path);
if current_config_read_res.is_ok(){
current_config = current_config_read_res.unwrap();
println!("current configuration loaded!");
}
print!("{}", current_config);
let mut project_base_folder = PathBuf::new();
let mut project_base_notes = PathBuf::new();
let mut tools_folder = PathBuf::new();
let mut terminal_command = String::new();
let mut box_template = String::new();
let mut cracking_rig = String::new();
let mut rockyou = String::new();
let mut rule = String::new();
let mut upcoming_files = PathBuf::new();
let mut upcoming_notes = PathBuf::new();
let mut pass_spray_file = PathBuf::new();
let mut fingerprint = false;
let mut vault_name = String::new();
let settings: Vec<&str> = current_config.split("\n").collect();
for line in settings{
if line.len() > 1{
let setting_vec: Vec<&str> = line.split(":").collect();
match setting_vec[0]{
"Project_files" => project_base_folder.push(setting_vec[1].trim_end()),
"Project_notes" => project_base_notes.push(setting_vec[1].trim_end()),
"tools_folder" => tools_folder.push(setting_vec[1].trim_end()),
"upcoming_files" => upcoming_files.push(setting_vec[1].trim_end()),
"upcoming_notes" => upcoming_notes.push(setting_vec[1].trim_end()),
"box_template" => box_template = setting_vec[1].trim_end().to_owned(),
"terminal" => terminal_command = setting_vec[1].trim_end().to_owned(),
"cracking_rig" => cracking_rig = setting_vec[1].trim_end().to_owned(),
"rockyou_location" => rockyou = setting_vec[1].trim_end().to_owned(),
"rule_location" => rule = setting_vec[1].trim_end().to_owned(),
"pass_file"=> pass_spray_file.push(setting_vec[1]),
"fingerprint" => {if setting_vec[1].contains("y"){fingerprint = true}},
"vault_name" => vault_name = setting_vec[1].trim_end().to_owned(),
_ => println!("error unknown setting: {}", setting_vec[0])
}
}
}
println!("1.) recreate entire configuration");
println!("2.) modify one setting");
if get_user_input("Selection?").contains("2"){
print!("
1 .) project_files
2 .) project_notes
3 .) tools_folder
4 .) upcoming_files
5 .) upcoming_notes
6 .) box_template
7 .) terminal
8 .) cracking_rig
9 .) rockyou_location
10.) rule_location
11.) pass_file
12.) fingerprint
13.) vault_name
");
match get_user_input("which setting would you like to modify?").as_str(){
"1" => {project_base_folder.clear(); project_base_folder.push(get_user_input("full path to the base project files folder?"));},
"2" => {project_base_notes.clear(); project_base_notes.push(get_user_input("full path to the base project notes folder"));},
"3" => {tools_folder.clear(); tools_folder.push(get_user_input("full path to your custom tools folder?"));},
"4" => {upcoming_files.clear(); upcoming_files.push(get_user_input("full path to your upcoming projects folder"));},
"5" => {upcoming_notes.clear(); upcoming_notes.push(get_user_input("full path to your upcoming project nots folder"));},
"6" => {box_template = get_user_input("name of your distrobox template?")},
"7" => {terminal_command = get_user_input("comand to run your terminal while executing a specific command, ex: konsole -e ")},
"8" => {cracking_rig = get_user_input("username and address of your personal cracking rig, example pyro@crackingrig or pyro@192.168.1.10?")},
"9" => {rockyou = get_user_input("location of rockyou.txt on your cracking rig?")},
"10" => {rule = get_user_input("location of the one rule on your crakcing rig?")},
"11" => {pass_spray_file.clear(); pass_spray_file.push(get_user_input("location of your password spray list file?"));},
"12" => {fingerprint = get_user_input("will you be using fingerprint authentication in your distroboxes?").to_lowercase().contains("y")},
"13" => {vault_name = get_user_input("obsidian vault name?")},
_ => {println!("unknown selection, please try again...");}
};
}
else{
project_base_folder = PathBuf::from(get_user_input("path to store your active projects?"));
project_base_notes = PathBuf::from(get_user_input("path to store your active project notes?"));
upcoming_files = PathBuf::from(get_user_input("path to store your upcomming projects?"));
upcoming_notes = PathBuf::from(get_user_input("path to store your upcomming project notes?"));
tools_folder = PathBuf::from(get_user_input("path where you store your custom tools (like from github and places)?"));
box_template = get_user_input("Name of the distrobox you want to use as a template?");
cracking_rig = String::from("nobody@nothing");
rockyou = String::from("n/a");
rule = String::from("n/a");
let cracking_rig_response = get_user_input("do you have a separate machine to crack passwords on? (not the ambush cracking rig)");
if cracking_rig_response.to_lowercase().contains("y"){
let rig_ip = get_user_input("ip address or hostname of your cracking rig?");
let rig_user = get_user_input("username to log into your cracking rig with?");
rockyou = get_user_input("location of rockyou.txt on the cracking rig?");
rule = get_user_input("location of one rule to rule them all on the cracking rig?");
cracking_rig = format!("{}@{}", rig_user, rig_ip);
}
else{
println!("ok free loader");
}
fingerprint = get_user_input("will you be using fingerprint authentication for your distroboxes?").to_lowercase().contains("y");
terminal_command = get_user_input("command to launch your terminal, while executing a command, for example konsel -e ");
vault_name = get_user_input("the name of the vault you're going to use?");
}
let new_config = format!("
Project_files:{}
Project_notes:{}
tools_folder:{}
upcoming_files:{}
upcoming_notes:{}
box_template:{}
terminal:{}
cracking_rig:{}
rockyou_location:{}
rule_location:{}
pass_file:{}
fingerprint:{}
vault_name:{}
",project_base_folder.display(), project_base_notes.display(), tools_folder.display(), upcoming_files.display(), upcoming_notes.display(), box_template, terminal_command, cracking_rig, rockyou, rule, pass_spray_file.display(), fingerprint, vault_name);
println!("this will be the new config that will be saved:\n");
println!("{}", new_config);
if get_user_input("is this ok?").to_lowercase().contains("y"){
let config_file_res = open_overwrite(&config_file_path);
if config_file_res.is_none(){
println!("failed to open config file in overwrite mode... quitting... nothing was saved.");
return;
}
let mut config_file = config_file_res.unwrap();
let write_res= write!(config_file, "{}", new_config);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","error writing config file!".red());
println!("{}", error.to_string().red());
println!("nothing was saved...");
return;
}
write_res.unwrap();
}
}
pub fn load_config(config: &PathBuf) -> HashMap<&'static str, String>{
let mut settings_map = HashMap::new();
let settings_string = read_to_string(&config).expect("error reading config file");
let settings: Vec<&str> = settings_string.split("\n").collect();
for line in settings{
if line.len() > 1{
let setting_vec: Vec<&str> = line.split(":").collect();
match setting_vec[0]{
"Project_files" => {settings_map.insert("project_files", setting_vec[1].trim_end().to_owned());},
"Project_notes" => {settings_map.insert("project_notes", setting_vec[1].trim_end().to_owned());},
"tools_folder" => {settings_map.insert("tools_folder",setting_vec[1].trim_end().to_owned());},
"upcoming_files" => {settings_map.insert("upcoming_files",setting_vec[1].trim_end().to_owned());},
"upcoming_notes" => {settings_map.insert("upcoming_notes",setting_vec[1].trim_end().to_owned());},
"box_template" => {settings_map.insert("box_template", setting_vec[1].trim_end().to_owned());},
"terminal" => {settings_map.insert("terminal_command", setting_vec[1].trim_end().to_owned());},
"cracking_rig" => {settings_map.insert("cracking_rig", setting_vec[1].trim_end().to_owned());},
"rockyou_location" => {settings_map.insert("rockyou", setting_vec[1].trim_end().to_owned());},
"rule_location" => {settings_map.insert("rule", setting_vec[1].trim_end().to_owned());},
"pass_file"=> {settings_map.insert("pass_spray_file", setting_vec[1].trim_end().to_owned());},
"fingerprint" => {settings_map.insert("fingerprint", setting_vec[1].trim_end().to_owned());},
"vault_name" => {settings_map.insert("vault_name", setting_vec[1].trim_end().to_owned());},
_ => println!("error unknown setting: {}", setting_vec[0])
}
}
}
return settings_map;
}
pub fn print_settings(settings: &HashMap<&str, String>){
let mut lines = Vec::new();
lines.push(String::from("Settings||value"));
for setting in settings.keys(){
let new_line = format!("{}||{}", setting, settings[setting]);
lines.push(new_line);
}
tableize(lines, &true);
}

View File

@@ -0,0 +1,398 @@
use std::fs::{read_to_string, remove_file, OpenOptions};
use std::process::Command;
use std::thread::JoinHandle;
use std::thread::{spawn, sleep};
use std::io::Write;
use std::time::Duration;
use colored::Colorize;
use dns_lookup::lookup_host;
use crate::get_user_input;
use crate::Project;
use crate::open_append;
use crate::print_error;
use crate::print_success;
use crate::print_informational;
#[allow(unused)]
pub fn run_dns_enumeration(project: &Project, given_domains: Option<&Vec<String>>, standalone: bool) -> Option<JoinHandle<()>>{
let notes_folder = project.notes_folder.clone();
let mut enumeration = notes_folder.clone();
enumeration.push("enumeration.md");
let enumeration_file_res = open_append(&enumeration);
if enumeration_file_res.is_none(){
println!("error opening enumeration_file!");
println!("try creating it manually.");
return None;
}
let mut enumeration_file = enumeration_file_res.unwrap();
let mut domaind = Vec::new();
if given_domains.is_none(){
loop{
let domain = get_user_input("domain to add? enter DONE in all caps when you're finsihed");
match domain.as_str(){
"DONE" => break,
_ => domaind.push(domain),
}
}
}
else{
for domain in given_domains.unwrap(){
domaind.push(domain.to_owned());
}
}
let working_project = project.clone();
let dns_handle = spawn(move || {
for domain in &domaind{
let output_res = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(working_project.boxname.to_owned())
.arg("--")
.arg("dnsrecon")
.arg("-d")
.arg(domain)
.arg("-c")
.arg("dns_temp.csv")
.output();
if output_res.is_err(){
let error = output_res.err().unwrap();
println!("{}", "From DNS Enumeration Thread: error running dnsrecon in the project's distrobox!".red());
println!("{}", error.to_string().red());
return;
}
println!("sleping for 10 seconds to allow for sudo password input.");
sleep(Duration::from_secs(10));
let output_string_res = read_to_string("dns_temp.csv");
if output_string_res.is_err(){
let error = output_string_res.err().unwrap();
println!("From DNS Enumeration Thread: error reading output data!");
println!("{}", error.to_string().red());
return;
}
print_success("DNS Enumeration Done, Writing to file...");
let output_string = output_string_res.unwrap();
let lines: Vec<&str> = output_string.split("\n").collect();
let mut out_data = String::new();
if standalone{
out_data.push_str("# DNS Enumeration\n");
out_data.push_str("## DNS Records\n");
}
let mut data_vec = Vec::new();
let mut first_line = true;
for line in lines{
if first_line == true{
first_line = false;
}
else{
if line.len() > 1{
let words: Vec<&str> = line.split(",").collect();
let domain_name = words[2].to_owned();
let domain_type = words[1].to_owned();
let mut data = String::new();
if words[3].len() > 2{
data = words[3].to_owned();
}
else{
data = words[6].to_owned();
}
let data_line = format!("| {} | {} | {} |", domain_name, domain_type, data);
data_vec.push(data_line);
}
}
}
let domain_header = format!("#### {}\n", domain);
out_data.push_str(&domain_header);
if standalone{
out_data.push_str("#### DNS Records\n");
}
out_data.push_str("| Domain name | Record type | data |\n");
out_data.push_str("| ----------- | ----------- | ---- |\n");
for thang in data_vec{
let out_line = format!("{}\n", thang);
out_data.push_str(&out_line);
}
if standalone{
out_data.push_str("\n---\n");
}
write!(enumeration_file, "{}", &out_data).unwrap();
print_success("DNS Records: Gathered | Notes: Written | DNS Record Thread OUT!");
let remove_res = remove_file("dns_temp.csv");
if remove_res.is_err(){
println!("From DNS Enumeration Thread: error removing temporay data file!");
println!("From DNS Enumeration Thread: please manually delete dns_temp.csv");
}
}
});
return Some(dns_handle);
}
#[allow(unused)]
pub fn bruteforce_subs(project: &Project, given_domains: Option<&Vec<String>>, given_wordlist: Option<String>, standalone: bool) -> Option<JoinHandle<()>>{
let mut enumeration_path = project.notes_folder.clone();
enumeration_path.push("enumeration.md");
let enumeration_file_res = OpenOptions::new().append(true).create(true).open(enumeration_path);
if enumeration_file_res.is_err(){
let error = enumeration_file_res.err().unwrap();
print_error("FROM GOBUSTER THREAD: error opening enumeration notes file!", error.to_string());
return None;
}
let mut enumeration_file = enumeration_file_res.unwrap();
let mut domains = Vec::new();
if given_domains.is_none(){
loop{
let domain = get_user_input("Domain to add? Enter DONE in all caps when done.");
if domain == "DONE".to_owned(){
break;
}
else{
domains.push(domain);
}
}
}
else{
for domain in given_domains.unwrap(){
domains.push(domain.to_owned());
};
}
let mut wordlist = String::new();
if given_wordlist.is_none(){
wordlist = get_user_input("path to wordlist?");
}
else{
wordlist = given_wordlist.unwrap();
}
let working_project = project.clone();
let mut out_data = String::new();
if standalone{
out_data.push_str("# DNS Enumeration\n");
out_data.push_str("## Subdomain Enumeration\n");
}
let gobuster_thread = spawn( move ||{
for domain in domains{
if standalone{
out_data.push_str(format!("#### {}\n", &domain).as_str());
}
let gobuster_cmd_res = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(working_project.boxname.to_owned())
.arg("--")
.arg("gobuster")
.arg("dns")
.arg("-d")
.arg(&domain)
.arg("-w")
.arg(wordlist.to_owned())
.output();
if gobuster_cmd_res.is_err(){
let error = gobuster_cmd_res.err().unwrap();
println!("{}","From gobuster thread: Error running gobuster command!".red());
println!("{}", error.to_string().red());
return;
}
print_informational("sleeping for 10 seconds to allow for sudo password input.");
sleep(Duration::from_secs(10));
let gobuser_output = gobuster_cmd_res.unwrap().stdout;
let gobuster_string = String::from_utf8_lossy(&gobuser_output);
let mut final_string = String::new();
if gobuster_string.contains("specify the '--wildcard' switch"){
let gobuster_cmd_res = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(working_project.boxname.to_owned())
.arg("--")
.arg("gobuster")
.arg("dns")
.arg("-d")
.arg(&domain)
.arg("-w")
.arg(wordlist.to_owned())
.arg("--wildcard")
.output();
if gobuster_cmd_res.is_err(){
let error = gobuster_cmd_res.err().unwrap();
println!("{}","From gobuster thread: Error running gobuster command!".red());
println!("{}", error.to_string().red());
return;
}
let new_gobuser_output = gobuster_cmd_res.unwrap().stdout;
let new_gobuser_string = String::from_utf8_lossy(&new_gobuser_output);
final_string = new_gobuser_string.to_string();
}
else{
final_string = gobuster_string.to_string();
}
print_success("Gobuster enumeration Done!");
let mut domain_names = Vec::new();
let lines: Vec<&str> = final_string.split("\n").collect();
for line in lines{
if line.contains("Found:"){
let domain = line.split_whitespace().collect::<Vec<&str>>()[1];
domain_names.push(domain.to_owned());
}
}
out_data.push_str("\n| domain name | ips |\n");
out_data.push_str("| ----------- | --- |\n");
for name in domain_names{
let ips = lookup_host(&name);
if ips.is_ok(){
let mut ip_string = String::new();
for ip in ips.unwrap(){
ip_string = format!("{},{}", ip, ip_string);
}
out_data.push_str(format!("| {} | {} |\n", name, ip_string).as_str());
}
}
}
if standalone{
out_data.push_str("\n---\n");
}
let write_res = write!(enumeration_file, "{}", out_data);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","FROM Gobuster Thread: error writing notes!".red());
println!("{}", error.to_string().red());
return;
}
write_res.unwrap();
print_success("Subdomains: Bruteforced | Enumeration notes: Written | Gobuster thread out!");
});
return Some(gobuster_thread);
}
pub fn dns_squatting(project: &Project, given_domains: Option<&Vec<String>>, standalone: bool) -> Option<JoinHandle<()>>{
let mut enumeration_notes = project.notes_folder.clone();
enumeration_notes.push("enumeration.md");
let open_enumeration_notes_res = OpenOptions::new().append(true).create(true).open(enumeration_notes);
if open_enumeration_notes_res.is_err(){
let error = open_enumeration_notes_res.err().unwrap();
println!("{}","Error opening enumeration notes".red());
println!("{}", error.to_string().red());
return None;
}
let mut enumeration_file = open_enumeration_notes_res.unwrap();
let mut domains = Vec::new();
if given_domains.is_none(){
loop{
let domain = get_user_input("Domain to add? enter DONE in all caps when you're finished");
if domain == "DONE"{
break;
}
else{
domains.push(domain);
}
}
}
else{
domains = given_domains.unwrap().to_owned();
}
let working_project = project.clone();
let squatting_thread = spawn(move || {
let write_res = write!(enumeration_file, "### Domain Squatting\n");
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","error writing to enumeration notes file!".red());
println!("{}", error.to_string().red());
return;
}
write_res.unwrap();
for domain in domains{
if standalone{
write!(enumeration_file, "#### {}\n", domain).unwrap();
}
write!(enumeration_file, "\n| type | domain name | ns servers |\n").unwrap();
write!(enumeration_file, "| ---- | ----------- | ---------- |\n").unwrap();
let twist_output = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(working_project.boxname.to_owned())
.arg("--")
.arg("dnstwist")
.arg("-r")
.arg(domain)
.output();
if twist_output.is_err(){
let error = twist_output.err().unwrap();
println!("{}","From DNSTwist thread: Error running dnstwist command!".red());
println!("{}", error.to_string().red());
return;
}
print_informational("sleeping for 10 seconds to get sudo password.");
sleep(Duration::from_secs(10));
let twist_output_vec = twist_output.unwrap().stdout;
let output_string = String::from_utf8_lossy(&twist_output_vec);
let output_lines = output_string.split("\n");
for line in output_lines{
if line.len() > 0{
let words: Vec<&str> = line.split_whitespace().collect();
let twist_type = words[0];
let name = words[1];
let ns_servers = words[2..].join(" ");
write!(enumeration_file, "| {} | {} | {} |\n", twist_type, name, ns_servers).unwrap();
}
}
}
});
print_success("Domains: Squatted | Notes: Written | DNSTwist thread OUT!");
return Some(squatting_thread);
}
pub fn do_all_dns_enumeration(project: &Project) -> Option<JoinHandle<()>>{
let mut enumeration_path = project.notes_folder.clone();
enumeration_path.push("enumeration.md");
let enumeration_file_res = OpenOptions::new().append(true).create(true).open(enumeration_path);
if enumeration_file_res.is_err(){
let error = enumeration_file_res.err().unwrap();
println!("{}","error opening enumeration notes file!".red());
println!("{}", error.to_string().red());
return None;
}
let mut enumeration_file = enumeration_file_res.unwrap();
let mut domains = Vec::new();
loop{
let domain = get_user_input("Domain to add? enter DONE in all caps when you're finished");
if domain == "DONE"{
break;
}
else{
domains.push(domain);
}
}
let wordlist = get_user_input("path to wordlist for sub domain bruteforcing?");
print_informational("target data gathered. Spawning threads to do enumeration...");
let working_project = project.clone();
let all_dns_handle = spawn(move ||{
let mut write_success = true;
let write_res = write!(enumeration_file, "# DNS Enumeration\n");
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","From All DNS thread: Error writing notes file!".red());
println!("{}", error.to_string().red());
write_success = false;
}
if write_success{
for domain in &domains{
let thread_domain = vec![domain.to_owned()];
write!(enumeration_file, "## {}\n", &domain).unwrap();
write!(enumeration_file, "### DNS Records\n").unwrap();
let dns_enum_thread = run_dns_enumeration(&working_project, Some(&thread_domain), false);
if dns_enum_thread.is_some(){
let _ = dns_enum_thread.unwrap().join();
}
write!(enumeration_file, "### Subdomain Enumeration\n").unwrap();
let gobuster_thread = bruteforce_subs(&working_project, Some(&thread_domain), Some(wordlist.to_owned()), false);
if gobuster_thread.is_some(){
let _ = gobuster_thread.unwrap().join();
}
write!(enumeration_file, "### Domain Squatting\n").unwrap();
let twist_thread = dns_squatting(&working_project, Some(&thread_domain), false);
if twist_thread.is_some(){
let _ = twist_thread.unwrap().join();
}
write!(enumeration_file, "\n---\n").unwrap();
}
print_success("All DNS Enum threads finished, notes have been written. DNS Enumeration Thread OUT!");
}
});
return Some(all_dns_handle);
}

498
pentest_tool/src/gui.rs Normal file
View File

@@ -0,0 +1,498 @@
use iced::Length::Fill;
use iced::Renderer;
use iced::widget::{button, center, column, pick_list, row, scrollable, text, text_input, Column};
use iced::Theme;
use rfd::FileDialog;
use std::collections::HashMap;
use std::time::Duration;
use std::{fs, thread};
use std::path::PathBuf;
use std::process::Command;
use directories::UserDirs;
use std::io::Write;
use crate::{box_controls, open_overwrite};
use crate::cli::{self, get_active_project};
use crate::project_controls;
use crate::Project;
use crate::configuration;
use crate::portscan_controls;
use crate::start_pentest;
#[derive(Default)]
struct CmdResult{
config_path: PathBuf,
output: String,
projects: Vec<Project>,
base_files: PathBuf,
base_notes: PathBuf,
tools_dir: PathBuf,
boxtemplate: String,
terminal: String,
cracking_rig: String,
rockyou: String,
rule: String,
upcoming_files: PathBuf,
upcoming_notes: PathBuf,
password_spray_file: PathBuf,
fingerprint: bool,
vault_name: String,
command_to_run: String,
initialized: bool,
active_project: String,
value_to_set: String,
setting_value: bool,
next_id: i32,
}
#[derive(Debug, Clone)]
enum Message {
Init,
ListProjects,
SwicthProject,
ManualCommand,
CommandInput(String),
UnknownCommand,
NewTerminal,
ClearCommand,
ProjectSelected(String),
ShowActiveProject,
ShowSettings,
CommandSelected(String),
ParseCsPortscan,
InputValue(String),
ClearValue,
PromoteProject,
}
impl CmdResult{
fn set_active_project(&mut self) -> Option<Project>{
let get_active_res = cli::get_active_project(&self.projects);
if get_active_res.is_none(){
self.output = String::from("Failed to get active projects!");
return None;
}
else{
return Some(get_active_res.unwrap().clone());
}
}
fn update(&mut self, message: Message){
let mut opt_active_project = None;
let mut settings = HashMap::new();
if self.initialized{
let acitve_res = self.set_active_project();
if acitve_res.is_some(){
opt_active_project = acitve_res;
}
settings = configuration::load_config(&self.config_path);
}
match message{
Message::Init => {
self.init(true);
}
Message::ShowSettings => {
let out_string = format!("
config_path = {}
base_files = {}
base_notes = {}
tools_dir = {}
boxtemplate = {}
cracking_rig = {}
rockyou = {}
rule = {}
upcoming_files = {}
upcoming_notes = {}
password_spray_file = {}
fingerprint = {}
vault_name = {}
initialized = {}
active_project = {}
", self.config_path.display(), self.base_files.display(), self.base_notes.display(), self.tools_dir.display(), self.boxtemplate, self.cracking_rig, self.rockyou, self.rule, self.upcoming_files.display(), self.upcoming_notes.display(), self.password_spray_file.display(), self.fingerprint, self.vault_name, self.initialized, self.active_project);
self.output.push_str(&out_string);
}
Message::ListProjects =>{
self.output.push_str(format!("\n{}", project_controls::list_projects(&self.projects, &false)).as_str());
}
Message::ShowActiveProject => {
for project in &self.projects{
let project_name = format!("{}:{}", project.customer, project.project_name);
if project_name == self.active_project{
self.output = format!("\nclient: {}\n\nproject: {}\n\nbox: {}\n\nproject files: {}\n\nproject notes: {}\n", project.customer ,project.project_name, project.boxname, project.files_folder.display(), project.notes_folder.display());
}
}
}
Message::ProjectSelected(project) => {
self.active_project = project;
let outline = format!("\nproject {} selected!", self.active_project);
self.output.push_str(&outline);
self.update(Message::SwicthProject);
}
Message::SwicthProject =>{
let mut out_string = String::from("Current Loaded Projects Config File\n");
let print_text = format!("\nswitching to {}", self.active_project);
self.output.push_str(&print_text);
for project in &mut self.projects{
let project_name = format!("{}:{}", project.customer, project.project_name);
let mut _outline = String::new();
if project_name == self.active_project {
_outline = format!("{}:{}:{}:{}:yes:{}:{}\n", project.customer, project.project_name, project.notes_folder.display(), project.files_folder.display(), project.boxname, project.stage);
project.active = true;
out_string.push_str(&_outline);
}
else {
_outline = format!("{}:{}:{}:{}:no:{}:{}\n", project.customer, project.project_name, project.notes_folder.display(), project.files_folder.display(), project.boxname, project.stage);
project.active = false;
out_string.push_str(&_outline);
}
}
let mut projects_path = self.config_path.clone();
projects_path.pop();
projects_path.push("projects.conf");
let projects_file_res = open_overwrite(&projects_path);
if projects_file_res.is_some(){
let mut projects_file = projects_file_res.unwrap();
let write_res = write!(projects_file, "{}", out_string);
if write_res.is_err(){
self.output.push_str("Error writing projects file!");
}
}
self.init(false);
}
Message::CommandInput(cmd) => {
self.command_to_run = cmd;
}
Message::CommandSelected(cmd) => {
self.command_to_run = cmd;
self.update(Message::ManualCommand);
}
Message::ClearCommand => {
self.command_to_run = String::from("");
}
Message::UnknownCommand => {
self.output.push_str("\n");
let words: Vec<&str> = self.command_to_run.split(" ").collect();
let command = words[0];
let mut new_cmd = Command::new(command);
for arg in words[1..].to_owned(){
new_cmd.arg(arg);
}
let cmd_rs = new_cmd.output();
if cmd_rs.is_err(){
self.output.push_str(format!("\n{} is an unknown command!", self.command_to_run).as_str());
return;
}
let output = cmd_rs.unwrap();
let success_out = output.stdout;
let fail_out = output.stderr;
if success_out.len() > 1{
self.output.push_str(format!("\n{}", String::from_utf8_lossy(&success_out).to_string()).as_str());
}
else if fail_out.len() > 0 {
self.output.push_str(format!("\n{}", String::from_utf8_lossy(&fail_out).to_string()).as_str());
}
else{
self.output.push_str(format!("\n{}", String::from("unknown tool or system command!")).as_str());
}
self.update(Message::ClearCommand);
}
Message::NewTerminal => {
self.output.push_str("\n");
self.output.push_str(format!("\n{}", String::from("launching termainl!")).as_str());
box_controls::project_standalone_terminal(opt_active_project.unwrap().clone(), self.terminal.clone(), &false);
}
Message::ManualCommand =>{
self.output.push_str("\n");
if self.setting_value{
match self.command_to_run.as_str(){
"new project" => {
if self.value_to_set.len() > 1{
let value_vec: Vec<&str> = self.value_to_set.split(" ").collect();
let client = String::from(value_vec[0]);
let project = String::from(value_vec[1]);
self.update(Message::ClearValue);
self.new_project(client, project);
self.setting_value = false;
self.update(Message::ClearCommand);
}
}
"promote project" => {
if self.value_to_set.len() > 1{
let id = self.value_to_set.clone();
self.update(Message::ClearCommand);
let settings = configuration::load_config(&self.config_path);
self.output.push_str("promoting project and setting up distrobox please wait...");
self.output.push_str("\nPlease follow terminal prompts");
self.output.push_str("\nNote this window will hang during distrobox creation.");
self.output.push_str("\nyour system may think it is not responding, just wait until the box createion is complete.");
self.view();
let (_handle, output) = cli::run_command(String::from("promote project"), &settings, &self.config_path, &mut self.projects, false, Some(id));
if output.is_some(){
self.output.push_str(&output.unwrap());
}
self.update(Message::ClearValue);
self.setting_value = false;
}
}
_ => self.update(Message::UnknownCommand),
}
}
else{
match self.command_to_run.as_str(){
"list projects" => {self.update(Message::ListProjects); self.update(Message::ClearCommand)},
"new terminal" => {self.update(Message::NewTerminal); self.update(Message::ClearCommand);},
"show active" => {self.update(Message::ShowActiveProject); self.update(Message::ClearCommand);},
"show settings" => {self.update(Message::ShowSettings); self.update(Message::ClearCommand);},
"initialize" => {self.init(true); self.update(Message::ClearCommand);},
"parse cs portscan" => {self.update(Message::ParseCsPortscan); self.update(Message::ClearCommand)},
"create new project" => {
self.setting_value = true;
self.output.push_str("Enter the client and project name, space separated:");
self.command_to_run = String::from("new project");
},
"promote project" => {
self.setting_value = true;
for project in &self.projects{
let select_line = format!("\n{}.) {} {}", project.id, project.customer, project.project_name);
self.output.push_str(&select_line);
self.command_to_run = String::from("promote project");
}
},
"recreate distrobox" => {
let (_handle, output) = cli::run_command(String::from("recreate distrobox"), &settings, &self.config_path, &mut self.projects, false, None);
if output.is_some(){
let outline = format!("\n{}", output.unwrap());
self.output.push_str(&outline);
}
self.output.push_str("Distrobox recreation successful!");
}
_ => self.update(Message::UnknownCommand),
};
if !self.setting_value {
self.update(Message::ClearCommand);
self.update(Message::ClearValue);
}
}
}
Message::ParseCsPortscan => {
self.output.push_str("\n");
let projec = opt_active_project.unwrap();
let open_file = FileDialog::new().set_directory(&projec.files_folder).pick_file();
let save_folder = FileDialog::new().set_directory(&projec.files_folder).pick_folder();
portscan_controls::parse_csportscan(&projec, open_file.clone(), save_folder.clone());
let out_string = format!("parsing cobalt strike port scan file: {}\nand saving to {}", open_file.unwrap().display(), save_folder.unwrap().display());
self.output.push_str(&out_string);
}
Message::InputValue(value) => {
self.value_to_set = value;
}
Message::ClearValue => {
self.value_to_set.clear();
}
Message::PromoteProject => {
let settings = configuration::load_config(&self.config_path);
let (handle, output) = cli::run_command(String::from("promote project"), &settings, &self.config_path, &mut self.projects, false, Some(self.value_to_set.clone()));
}
}
}
fn init(&mut self, display: bool){
let mut config_path = PathBuf::new();
let user_dirs_res = UserDirs::new();
if user_dirs_res.is_none(){
println!("oof couldn't get the config directory the right way, falling back to the stpuid way...");
let get_home_res = Command::new("echo").arg("$HOME").output();
if get_home_res.is_err(){
println!("ooof the stupid way didn't work... this is a bruh moment, gotta fix your environment variabls!!!");
return;
}
let get_home_output = get_home_res.unwrap().stdout;
let home_string = String::from_utf8_lossy(&get_home_output);
config_path.push(home_string.trim());
}
else{
let user_dirs = user_dirs_res.unwrap();
config_path.push(user_dirs.home_dir());
}
config_path.push(".config/pyro_pentest_tool/conf");
let settings = configuration::load_config(&config_path);
self.config_path = config_path.clone();
self.base_files = PathBuf::from(settings["project_files"].clone());
self.base_notes = PathBuf::from(settings["project_notes"].clone());
self.tools_dir = PathBuf::from(settings["tools_folder"].clone());
self.boxtemplate= settings["box_template"].clone();
self.terminal = settings["terminal_command"].clone();
self.cracking_rig = settings["cracking_rig"].clone();
self.rockyou = settings["rockyou"].clone();
self.rule = settings["rule"].clone();
self.upcoming_files = PathBuf::from(settings["upcoming_files"].clone());
self.upcoming_notes = PathBuf::from(settings["upcoming_notes"].clone());
self.password_spray_file = PathBuf::from(settings["pass_spray_file"].clone());
self.fingerprint = settings["fingerprint"].to_lowercase().contains("y");
self.vault_name = settings["vault_name"].clone();
self.projects = project_controls::get_projects(&config_path, false).unwrap();
self.setting_value = false;
self.initialized = true;
let active = get_active_project(&self.projects);
if active.is_some(){
let act_project = active.unwrap();
self.active_project = format!("{}:{}", act_project.customer, act_project.project_name);
}
self.next_id = 0;
for project in &self.projects{
let id = project.id.clone();
if id > self.next_id{
self.next_id = id;
self.next_id += 1;
}
}
let display_string = String::from("
*@@@##%@@@=
+@@#- =@@#
-@@@@@@# -@@%
:@@@@@@=%* -@@:
-@@@@%: #%*= +@@@@%- #@:
:@@@@@@@@#- %@@@@@=@@:#@:
%@ :@@%+%% =@@@@@@::@-%%:
%@@%%#+-:: %@@#- :#-=@#
:%@- :--+#@@@@%#=::--=: :@@+
#@= ---=- +@@
*@* @@+
=@# #@@:
:@@ =- -@@:
:@# =@-:@+ %@- :==:
:@+ #% *@- +@+ -@%::#@:
:@= @--@+ :@% :%@+ =@:
:@= +# %# *@+ *@#: -@:
:@= @-#@ #@#%@@@@@@@*- -@#: *%:
:@* =*=@: *@: *#: :*@@% +@#
@% -% +# :-=###*+#%: -@@+
+@= -+: :-% :====+*#@@%- -%@#:
:%% =#@-%@#%: : :@@# -@*
*@# :#+ ** #@* :###*
+@%: = =@% -@:
#@%: += -@#: %=
=: :@@%@#- :%- -@%+####-
-#=@- :@# :%#%@@@*-: -: +@%:
:@: *@@- ## :+%%@@@: ++=-::::=%@*
+* *@: +*+:#@+ +@:-+%@@%=
+#:*@- :@%#@@- @-
:%= *@= @=
*@= #*
=@@@+
LOADING SETTINGS...
");
if display{
self.output.push_str(&display_string);
self.update(Message::ShowSettings);
self.output.push_str("SETTINGS LOADED!");
}
}
fn view(&self) -> Column<Message>{
let mut project_name_vec = Vec::new();
for project in &self.projects{
let project_line = format!("{}:{}", project.customer, project.project_name);
project_name_vec.push(project_line);
}
let tool_commands = vec![
String::from("list projects"),
String::from("new terminal"),
String::from("show active"),
String::from("show settings"),
String::from("parse cs portscan"),
String::from("promote project"),
];
let command_list = pick_list(tool_commands, Some(&self.command_to_run), Message::CommandSelected);
let project_list = pick_list(project_name_vec, Some(&self.active_project), Message::ProjectSelected).placeholder("select the active project");
let intialize_button = button("Get Hacking☢").on_press(Message::Init);
let command_list_title = text("Select a command");
let output_box = scrollable(text(&self.output)).height(300).width(Fill).anchor_bottom();
let input_box = text_input::<_, Theme, Renderer>("Command to run", &self.command_to_run).on_input(Message::CommandInput).on_submit(Message::ManualCommand);
let value_input_box = text_input::<_, Theme, Renderer>("Command to run", &self.value_to_set).on_input(Message::InputValue).on_submit(Message::ManualCommand);
let project_list_title = text("Current Active Project");
let project_select_column = column![project_list_title, project_list];
let command_select_column = column![command_list_title, command_list];
if self.setting_value{
let interface = column![output_box, value_input_box].spacing(10).padding(10);
return interface;
}
let main_row = row![command_select_column, project_select_column].spacing(10).padding(10);
if self.initialized{
let interface = column![main_row, output_box, input_box].spacing(30);
return interface;
}
else{
let interface = column![center(intialize_button)];
return interface;
}
}
fn new_project(&mut self, client: String, project: String){
let out_string = format!("\nClient:{}\nProject:{}", client, project);
self.output.push_str(&out_string);
let customer_name = client;
let project_name = project;
let mut starting_project_files = self.upcoming_files.clone();
starting_project_files.push(&customer_name);
starting_project_files.push(&project_name);
let mut starting_notes_files = self.upcoming_notes.clone();
starting_notes_files.push(&customer_name);
starting_notes_files.push(&project_name);
fs::create_dir_all(&starting_project_files).expect("error creating directory! the file dialog will let you create it manually");
fs::create_dir_all(&starting_notes_files).expect("error creating notes directory! the file dialog will let you create it manually.");
thread::sleep(Duration::from_secs(2));
let selected_files = FileDialog::new().set_directory(starting_project_files.clone()).set_can_create_directories(true).set_title("Project Files Folder").pick_folder();
if selected_files.is_none(){
self.output = String::from("error picking files folder");
return;
}
let project_files = selected_files.unwrap();
if &project_files != &starting_project_files{
fs::remove_dir_all(&starting_project_files).expect("error removing default directory!");
starting_project_files.pop();
fs::remove_dir_all(&starting_project_files).expect("error removing default directory!");
}
let selected_notes = FileDialog::new().set_directory(starting_notes_files.clone()).set_can_create_directories(true).set_title("Project Notes Folder").pick_folder();
if selected_notes.is_none(){
self.output = String::from("error picking notes folder");
}
let project_notes = selected_notes.unwrap();
if &project_notes != &starting_notes_files{
fs::remove_dir_all(&starting_notes_files).expect("error removing default folder");
starting_notes_files.pop();
fs::remove_dir_all(&starting_notes_files).expect("error removing default folder!");
}
let project_boxname = format!("{}_{}", self.boxtemplate, customer_name);
let id = self.next_id.clone();
self.next_id += 1;
let new_prject = Project{customer:customer_name.clone(), project_name:project_name.clone(), notes_folder:project_notes.clone(), files_folder:project_files.clone(),active:false, boxname:project_boxname.clone(),stage:"upcoming".to_owned(), id};
if project_name.to_lowercase().contains("external"){
start_pentest::external(&self.password_spray_file, &new_prject);
}
else if project_name.to_lowercase().contains("internal"){
start_pentest::internal(&self.password_spray_file, &new_prject);
}
else if project_name.to_lowercase().contains("vishing"){
start_pentest::vishing(&new_prject);
}
self.projects.push(new_prject);
project_controls::save_projects(&self.projects, &self.config_path, &false);
println!("project created and saved to the projects config file!");
}
}
fn theme(_state: &CmdResult) -> Theme{
return Theme::Dark;
}
pub fn gui() -> iced::Result{
let app = iced::application("Pyro Pentest Tool", CmdResult::update, CmdResult::view);
let res = app.theme(theme).run();
return res;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,130 +1,305 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs::{create_dir_all, remove_dir_all, File};
use std::io::Read;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::env;
use std::process; use std::io::copy;
use std::process::Command;
use std::time::Duration;
use reqwest::blocking::get;
use std::{path::Path, path::PathBuf};
use std::thread;
use std::process::exit;
use colored::Colorize;
use crate::{get_user_input, print_informational, print_success};
#[allow(unused)]
pub fn install(config_path: &PathBuf){ pub fn install(config_path: &PathBuf){
let mut _terminal_commands = HashMap::from([ let mut _terminal_commands = HashMap::from([
("konsole", "konsole -e !!!"), ("kde", "konsole -e !!!"),
("plasma", "konsole -e !!!"),
("gnome", "gnome-terminal -- bash !!!"), ("gnome", "gnome-terminal -- bash !!!"),
("xfce", "xfce4-terminal --execute '!!!'"), ("xfce", "xfce4-terminal --execute '!!!'"),
("alacritty", "alacritty -e !!!"), ("alacritty", "alacritty -e !!!"),
]); ]);
let mut folder_creation = true;
let current_projects = PathBuf::from(get_user_input("path to store your active projects?"));
let current_notes = PathBuf::from(get_user_input("path to store your active project notes?"));
let upcoming_projects = PathBuf::from(get_user_input("path to store your upcomming projects?"));
let upcoming_notes = PathBuf::from(get_user_input("path to store your upcomming project notes?"));
let tools = PathBuf::from(get_user_input("path where you store your custom tools (like from github and places)?"));
let mut vault_name = String::new();
let folders = vec![&current_projects, &current_notes, &upcoming_projects, &upcoming_notes, &tools];
let mut config_folder_path: PathBuf = config_path.clone(); let mut config_folder_path: PathBuf = config_path.clone();
config_folder_path.pop(); config_folder_path.pop();
let mut password_path = config_folder_path.clone();
password_path.push("password_spray.md");
let mut projects_conf_path = config_folder_path.clone(); let mut projects_conf_path = config_folder_path.clone();
let mut bell_file_path = config_folder_path.clone();
let del_on_fail = config_folder_path.clone(); let del_on_fail = config_folder_path.clone();
projects_conf_path.push("projects.conf"); projects_conf_path.push("projects.conf");
fs::create_dir_all(&config_folder_path).expect("error creating config dir"); create_dir_all(&config_folder_path).expect("error creating config dir");
let mut config_file = fs::File::create(config_path).expect("error creating file"); bell_file_path.push("bell.mp3");
let mut projects_conf_file = fs::File::create(projects_conf_path).expect("error creating projects config file"); let bell_sound_url = "https://github.com/Pyro57000/pentest_tool/raw/refs/heads/main/resources/bell.mp3";
projects_conf_file.write_all(b"customer:name:notes:files:active:box_name\n").expect("error writing default project info"); let response = get(bell_sound_url).unwrap();
let mut terminal_response = String::new(); let response_length = response.content_length().unwrap_or(0);
let mut notes_response = String::new(); let mut bell_file = File::create(bell_file_path).unwrap();
let mut files_response = String::new(); copy(&mut response.take(response_length), &mut bell_file).unwrap();
let mut tools_response = String::new(); println!("bell notification tone sucessfully downloaded!");
let mut template_name = String::new(); println!("creating new folders if needed...");
let mut have_template = String::new(); for folder in &folders{
println!("terminal you use? (example: konsole, xfce, gnome, etc)"); if !Path::exists(folder){
std::io::stdin().read_line(&mut terminal_response).unwrap(); let create_res = create_dir_all(folder);
let mut _terminal_command = String::new(); if create_res.is_err(){
if terminal_response.contains("konsole"){ let error = create_res.err().unwrap();
let mut response_buffer = String::new(); println!("{}{}","error creating folder ".red(), folder.display().to_string().red());
println!("do you already have a custom profile setup?"); println!("{}", error.to_string().red());
println!("this is pretty specific to pyro's setup, I do some special sauce for my konsole stuff"); println!("you'll need to create this manually after the install is done!");
std::io::stdin().read_line(&mut response_buffer).unwrap(); folder_creation = false;
if response_buffer.to_lowercase().contains("y"){ }
let mut profile = String::new(); }
println!("konsole profile name?"); }
std::io::stdin().read_line(&mut profile).unwrap(); let tool_volume = format!("{}:/tools:rw", &tools.display());
_terminal_command = format!("konsole --profile {}", profile); println!("sweet, now let's get some distrobox stuff out of the way!");
let distrobox_run_res = Command::new("distrobox").arg("list").arg("--root").output();
if distrobox_run_res.is_err(){
let error = distrobox_run_res.err().unwrap();
println!("{}","Distrobox file was not found!".red());
println!("{}","This usually means that distrobox is not installed.".red());
println!("{}","please install distrobox, if you're on kali run `sudo apt install distrobox`".red());
println!("{}", error.to_string().red());
println!("{}","this project heavily relies on distrobox, as its primarily a distrobox mangement tool.".red());
println!("{}","cleaning up up configuration folders, please install distrobox and re-run this program.".red());
let cleanup = remove_dir_all(&del_on_fail);
if cleanup.is_err(){
println!("error cleaning up configuration folder!");
println!("please manually delete {} before re-running this install.", &del_on_fail.display());
exit(1);
}
cleanup.unwrap();
exit(1);
}
let distrobox_output = distrobox_run_res.unwrap().stdout;
let distrobox_string = String::from_utf8_lossy(&distrobox_output);
let mut distrobox_names = Vec::new();
let distrobox_output_lines: Vec<&str> = distrobox_string.split("\n").collect();
for line in distrobox_output_lines{
if line.len() > 1{
let name = line.split(" | ").collect::<Vec<&str>>()[1];
distrobox_names.push(name.to_owned());
}
}
let mut have_box = false;
for name in distrobox_names{
if !name.contains("NAME"){
have_box = true;
println!("{}", name);
}
}
let mut template_box_name = String::new();
if have_box{
template_box_name = get_user_input("Name of the distrobox you want to use as a template?");
}
else{
println!("no exiting distroboxes created!");
println!("let's make one!");
template_box_name = get_user_input("name of the distrobox you want to create?");
println!("review images listed here: https://distrobox.it/compatibility/#containers-distros");
let box_image = get_user_input("image you want to base the template box on?");
let template_box_create_res = Command::new("distrobox")
.arg("create")
.arg("--root")
.arg("--image")
.arg(&box_image)
.arg("--name")
.arg(&template_box_name)
.arg("--init")
.arg("--volume")
.arg(&tool_volume)
.status();
if template_box_create_res.is_err(){
let error = template_box_create_res.err().unwrap();
println!("{}","error creating template box!".red());
println!("{}", error.to_string().red());
println!("{}","\n\n\nplese create it yourself with the following command.".red());
println!("\n\ndistrobox create --root --image {}, --name {}, --init --volume {}", &box_image, &template_box_name, &tool_volume);
}
else{
template_box_create_res.unwrap();
}
}
let mut cracking_rig = String::from("nobody@nothing");
let mut rockyou = String::from("n/a");
let mut rule = String::from("n/a");
let cracking_rig_response = get_user_input("do you have a separate machine to crack passwords on? (not the ambush cracking rig)");
if cracking_rig_response.to_lowercase().contains("y"){
let rig_ip = get_user_input("ip address or hostname of your cracking rig?");
let rig_user = get_user_input("username to log into your cracking rig with?");
rockyou = get_user_input("location of rockyou.txt on the cracking rig?");
rule = get_user_input("location of one rule to rule them all on the cracking rig?");
cracking_rig = format!("{}@{}", rig_user, rig_ip);
}
else{
println!("ok free loader");
}
let fingerprint = get_user_input("will you be using fingerprint authentication for your distroboxes?").to_lowercase();
let mut terminal = String::new();
for desktop in _terminal_commands.keys(){
println!("{}", desktop);
}
let desktop_response = get_user_input("do you use any of these desktops?").to_lowercase();
if desktop_response.contains("y"){
let default_response = get_user_input("do you use the default terminal for your desktop?").to_lowercase();
if default_response.contains("y"){
let de = get_user_input("which desktop do you use?");
terminal = _terminal_commands[&de.as_str()].to_owned();
} }
} }
else{ else{
_terminal_command = _terminal_commands[terminal_response.trim_end()].to_owned(); println!("OK, then please enter the command you'd use to launch a new terminal and run a command inside of it, replacing the command with three !s");
println!("for example for konsole you'd enter");
println!("konsole -e !!!");
terminal = get_user_input("");
} }
println!("path to save project notes?"); if terminal.contains("konsole"){
std::io::stdin().read_line(&mut notes_response).unwrap(); println!("do you use a specific profile for your attack boxes?");
println!("path to save project files?"); println!("this is pretty specific to Pyro's setup, so you probably don't");
std::io::stdin().read_line(&mut files_response).unwrap(); if get_user_input("").to_lowercase().contains("y"){
println!("path to folder with your custom tools?"); let profile_name = get_user_input("what is the name of your profile?");
std::io::stdin().read_line(&mut tools_response).unwrap(); terminal = format!("konsole --profile {}", profile_name);
print!(" }
This tool is mainly to handle distrobox creation and usage. }
It's expecting you to have a distrobox that you will use as a template. println!("many of the fuctions of this tool assume you're using obsidian or some other markdown editor for note taking");
Do you have a distrobox set up to function as your template for all new projects? let obsidian_used = get_user_input("do you use obsidian for your notes?");
"); if obsidian_used.to_lowercase().contains("y"){
std::io::stdin().read_line(&mut have_template).unwrap(); vault_name = get_user_input("the name of the vault you're going to use?");
if have_template.contains("n"){ }
println!("please set up a distrobox with root as a template and re-run this tool"); let configuration_string = format!("
println!("example distrobox setup command:"); Project_files:{}
println!("distrobox create --root --image archlinux --name template"); Project_notes:{}
println!("then enter that distrobox and install all the tools you want and do what ever setup you need"); tools_folder:{}
println!("and re-run this tool."); upcoming_files:{}
process::Command::new("rm").arg(del_on_fail).spawn().expect("ERROR deleting config folder, please manually clean up"); upcoming_notes:{}
std::process::exit(1); box_template:{}
terminal:{}
cracking_rig:{}
rockyou_location:{}
rule_location:{}
pass_file:{}
fingerprint:{}
vault_name:{}"
, &current_projects.display(), &current_notes.display(), &tools.display(), &upcoming_projects.display(), &upcoming_notes.display(), &template_box_name, &terminal, cracking_rig, rockyou, rule, &password_path.display(), fingerprint, &vault_name);
println!("cool everything, all folders and settings have been entered, now let's save this to a config file...");
thread::sleep(Duration::from_secs(3));
let mut config_file_res = File::create_new(config_path);
if config_file_res.is_err(){
println!("ooof error creating configuration file...");
println!("try creating it manually.");
println!("copy the following configuration and save it to {}", config_path.display());
print!("{}\n", configuration_string);
exit(1);
}
let mut config_file = config_file_res.unwrap();
let config_write_res =write!(config_file, "{}", &configuration_string);
if config_write_res.is_err(){
println!("error writing configuration string to the configuration file!");
println!(" try creating it manually.");
println!("copy the following configuration and save it to {}", &configuration_string);
exit(1);
}
config_write_res.unwrap();
println!("nice the configuration file was written correctly!");
println!("creating project configuration file, and poplulating it with the default project...");
let project_conf_res = File::create_new(&projects_conf_path);
if project_conf_res.is_err(){
let error = project_conf_res.err().unwrap();
println!("{}","ooof error creating the projects configuration file.".red());
println!("{}","try creating it manually!".red());
println!("copy the following configuration and save it to {}", &projects_conf_path.display());
println!("customer:name:notes:files:active:time:box_name:stage");
println!("default:default:{}:{}:yes:{}:current", &current_notes.display(), &current_projects.display(), &template_box_name);
exit(1);
}
let mut project_conf_file = project_conf_res.unwrap();
let project_write_res = write!(project_conf_file, "Projects Config File");
if project_write_res.is_err(){
println!("error writing project config file.");
exit(1);
}
project_write_res.unwrap();
let password_file_res = File::create_new(password_path);
if password_file_res.is_err(){
println!("error creating password spray file");
exit(1);
}
let mut password_file = password_file_res.unwrap();
let password_write_res = write!(password_file, "
- [ ] useraspass
- [ ] Seasonyear!
- [ ] Service123!
- [ ] admin
- [ ] Admin
- [ ] Admin123!
- [ ] admin123
- [ ] admin1
- [ ] 1234567
- [ ] Seasonyear
- [ ] seasonyear!
- [ ] seasonyear
- [ ] COMPANYYEAR!
- [ ] COMPANYYEAR
- [ ] November2024!
- [ ] September2024!
- [ ] October2024!
- [ ] COMPANYfoundingyear!
- [ ] COMPANYfoundingyear
- [ ] COMPANYstreetnumber!
- [ ] COMPANYstreetnumber
- [ ] Password
- [ ] P@ssw0rd
- [ ] Password1!
- [ ] Password123!
- [ ] Passwordyear!
- [ ] P@55w0rd
- [ ] Service
- [ ] Service!
- [ ] Serviceyear!
");
if password_write_res.is_err(){
println!("error writing password file");
exit(1);
}
password_write_res.unwrap();
let current_path_res = env::current_exe();
let mut copied_to_path = false;
if current_path_res.is_ok(){
let current_path = current_path_res.unwrap();
let mut new_path = PathBuf::from("/usr/bin/pentest_tool");
print_informational("We ar going to try and copy this executable to a location on your $PATH");
print_informational("the default location is /usr/bin/pentest_tool");
if get_user_input("is this ok?").contains("n"){
new_path.clear();
new_path.push(get_user_input("ooof, ok. Please enter where you'd like to save it on your $PATH"));
if !new_path.display().to_string().contains("pentest_tool"){
new_path.push("pentest_tool");
}
}
let copy_cmd_res = Command::new("sudo").arg("cp").arg(current_path.display().to_string()).arg(new_path.display().to_string()).status();
if copy_cmd_res.is_ok(){
let copy_cmd_status = copy_cmd_res.unwrap();
if copy_cmd_status.success(){
copied_to_path = true;
}
}
}
if !copied_to_path{
print_informational("we could not copy this executable to your path!");
print_informational("please copy this file to somewhere on your $PATH.");
print_informational("for example `cp pentest_tool /usr/bin/pentest_tool");
print_success("otherwise this tool is ready to go, you would just need to launch it from this folder.");
}
else{
print_success("install completed successfully!");
} }
let _list = process::Command::new("distrobox").arg("list").arg("--root").status();
println!("distrobox template name?");
std::io::stdin().read_line(&mut template_name).unwrap();
let config_string = format!("Project_files:{}\nProject_notes:{}\ntools_folder:{}\nterminal:{}", files_response.trim_end(), notes_response.trim_end(), tools_response.trim_end(), _terminal_command.trim_end());
config_file.write_all(config_string.as_bytes()).expect("error writing to config file");
let default_projectline = format!("default:default:{}:{}:yes:{}", &notes_response.trim_end(), &files_response.trim_end(), &template_name.trim_end());
projects_conf_file.write_all(default_projectline.as_bytes()).expect("error writing default project line");
println!("config file generated and saved to {}\n", config_path.display());
println!("please rerun the program");
let config_path = &config_folder_path.clone();
let mut install_path = config_path.clone();
let mut password_spray_template_path = install_path.clone();
install_path.push("new_projects.conf");
password_spray_template_path.push("passwordspray.md");
let password_spray_template_path = install_path.clone();
let mut conf_file = fs::File::create(install_path).expect("error creating config file");
let mut project_folder_path = String::new();
let mut porject_note_path = String::new();
println!("path to the project folders directory?");
std::io::stdin().read_line(&mut project_folder_path).expect("error reading project folder from stdin");
println!("path to the project notes directory");
std::io::stdin().read_line(&mut porject_note_path).expect("error reading project note folder form stdin");
write!(conf_file, "project_folder_path:{}
project_notes_path:{}
", project_folder_path.trim_end(), porject_note_path.trim_end()).expect("error writing config file");
let mut passpray_file = fs::File::create(password_spray_template_path).expect("error creating passwordspray file");
write!(passpray_file, "
- [ ] useraspass
- [ ] Seasonyear!
- [ ] Service123!
- [ ] admin
- [ ] Admin
- [ ] Admin123!
- [ ] admin123
- [ ] admin1
- [ ] 1234567
- [ ] Seasonyear
- [ ] seasonyear!
- [ ] seasonyear
- [ ] COMPANYYEAR!
- [ ] COMPANYYEAR
- [ ] November2024!
- [ ] September2024!
- [ ] October2024!
- [ ] COMPANYfoundingyear!
- [ ] COMPANYfoundingyear
- [ ] COMPANYstreetnumber!
- [ ] COMPANYstreetnumber
- [ ] Password
- [ ] P@ssw0rd
- [ ] Password1!
- [ ] Password123!
- [ ] Passwordyear!
- [ ] P@55w0rd
- [ ] Service
- [ ] Service!
- [ ] Serviceyear!").expect("error writing password spray template");
std::process::exit(0);
} }

View File

@@ -1,8 +1,12 @@
use std::path::PathBuf; use std::collections::HashMap;
use std::env::args;
use std::{io::stdin, path::PathBuf, process::Command};
use directories::UserDirs; use directories::UserDirs;
use std::fs; use std::process::exit;
use std::fs::{self, File};
use colored::Colorize;
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct Project{ pub struct Project{
pub customer: String, pub customer: String,
pub project_name: String, pub project_name: String,
@@ -10,19 +14,199 @@ pub struct Project{
pub files_folder: PathBuf, pub files_folder: PathBuf,
pub active: bool, pub active: bool,
pub boxname: String, pub boxname: String,
pub stage: String,
pub id: i32, pub id: i32,
} }
mod install; mod install;
mod menu;
mod project_controls; mod project_controls;
mod box_controls; mod box_controls;
mod info_controls; mod info_controls;
mod start_pentest; mod start_pentest;
mod cli;
mod portscan_controls;
mod victim_commands;
mod enumeration;
mod tool_controls;
mod configuration;
mod gui;
pub fn print_error(message: &str ,error: String){
println!("{}", message.red());
println!("{}", error.red());
}
pub fn print_success<T>(message: T) where T: AsRef<str> {
println!("{}", message.as_ref().green());
}
pub fn print_informational<T>(message: T) where T: AsRef<str>{
println!("{}", message.as_ref().custom_color((255,165,0)));
}
pub fn tableize(given_lines: Vec<String>, display: &bool) -> String{
let column_count = given_lines[0].split("||").collect::<Vec<&str>>().len();
// the following code just adds a junk line in at index 1 because that gets eaten for some reason...
// I want to try to figure out why its getten eaten, but just needed to get it working for now.
let mut lines = Vec::new();
let mut junk_line = format!("{}", "junk||".repeat(column_count));
junk_line.pop();
junk_line.pop();
for line in given_lines{
lines.push(line);
}
lines.insert(1,junk_line);
// end of junk line logic.
let (terminal_width, _terminal_height) = term_size::dimensions().unwrap();
let mut column_sizes:HashMap<usize, usize> = HashMap::new();
for column_number in 0..column_count{
column_sizes.insert(column_number, 0);
}
let mut longest_column = 0;
for line in &lines{
let cells: Vec<&str> = line.split("||").collect();
let mut current_cell: usize = 0;
for cell in cells{
let cell_length = cell.len() + 2;
if column_sizes[&current_cell] < cell_length{
column_sizes.insert(current_cell, cell_length);
}
if cell_length > longest_column{
longest_column = current_cell;
}
current_cell += 1;
}
if *display{
let longest_line = (column_count*2) - 1;
if longest_line > terminal_width{
let difference = longest_line - terminal_width - 3;
column_sizes.insert(longest_column, column_sizes[&longest_column] - difference);
}
}
}
let mut header = String::new();
let mut outdata = String::new();
let mut count = 0;
let mut separater = String::new();
let mut ending = String::new();
if *display{
ending = format!("{}\n", "|".green());
}
else{
ending = format!("{}\n", "|");
}
for line in lines{
count += 1;
//println!("{}", line);
let mut add_ending = false;
let data_vec: Vec<&str> = line.split("||").collect();
let mut column_numbers: Vec<&usize> = column_sizes.keys().collect();
column_numbers.sort_by(|a, b| a.cmp(b));
for column in column_numbers{
let mut data_to_add = String::new();
let mut data_vec_len = data_vec[*column].len();
if *display{
if data_vec_len > column_sizes[&longest_column]{
while data_vec_len > column_sizes[&longest_column]{
data_vec_len = data_vec_len / 2;
let (data_vec_start, data_vec_end) = data_vec[*column].split_at(data_vec_len);
data_to_add = format!("{}\n{}", data_vec_start, data_vec_end);
}
}
}
else{
data_to_add = String::from(data_vec[*column]);
}
let padding = column_sizes[column] - data_vec_len;
if *display{
if count == 1{
let formatted_cell = format!("{} {}{}", "|".green(), data_vec[*column].bold().green(), " ".repeat(padding));
header.push_str(&formatted_cell);
}
else if count == 2 {
let formatted_cell = format!("{} {} ", "|".green(), "-".repeat(column_sizes[column] - 1).green());
separater.push_str(&formatted_cell);
}
else {
let formatted_cell = format!("{} {}{}", "|".green(), data_to_add, " ".repeat(padding));
outdata.push_str(&formatted_cell);
add_ending = true;
}
}
else{
println!("no display");
if count == 1{
let formatted_cell = format!("{} {}{}", "|", data_vec[*column], " ".repeat(padding));
header.push_str(&formatted_cell);
}
else if count == 2 {
let formatted_cell = format!("{} {} ", "|", "-".repeat(column_sizes[column] - 1));
separater.push_str(&formatted_cell);
}
else {
let formatted_cell = format!("{} {}{}", "|", data_to_add, " ".repeat(padding));
outdata.push_str(&formatted_cell);
add_ending = true;
}
}
}
if add_ending{
outdata.push_str(&ending)
}
}
outdata.push_str(&separater);
outdata.push_str(&ending);
let print_output = format!("{}{}{}{}{}", header, ending, separater, ending, outdata);
return print_output;
}
pub fn open_overwrite(path: &PathBuf) -> Option<File>{
let file_create_res = fs::OpenOptions::new().create(true).write(true).truncate(true).open(path);
if file_create_res.is_err(){
let error = file_create_res.err().unwrap();
println!("{} {} {}","error opening".red(), path.display().to_string().red(), " file".red());
println!("{}", error.to_string().red());
return None;
}
else {
let file = file_create_res.unwrap();
return Some(file);
}
}
pub fn open_append(path: &PathBuf) -> Option<File>{
let file_create_res = fs::OpenOptions::new().create(true).append(true).open(path);
if file_create_res.is_err(){
let error = file_create_res.err().unwrap();
println!("{} {} {}","error opening".red(), path.display().to_string().red(), " file".red());
println!("{}", error.to_string().red());
return None;
}
else {
let file = file_create_res.unwrap();
return Some(file);
}
}
pub fn get_user_input(prompt: &str) -> String{
let mut response = String::new();
loop{
println!("{}", prompt);
let result = stdin().read_line(&mut response);
if result.is_err(){
println!("we need input here dummy! try again...");
}
else{
result.unwrap();
break;
}
}
return response.trim_end().to_owned();
}
fn main() { fn main() {
print!(" print!("{}","
⠀⠀⠀⣠⠶⠚⠛⠛⠛⠲⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⣠⠶⠚⠛⠛⠛⠲⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⣠⣾⣷⣄⠀⠀⠀⢀⣠⣤⣤⡀⠀⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⣠⣾⣷⣄⠀⠀⠀⢀⣠⣤⣤⡀⠀⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
@@ -41,45 +225,60 @@ fn main() {
⠀⠀⠀⠀⣀⡀⠀⣰⠇⣾⠀⠀⠈⣩⣥⣄⣿⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⣀⡀⠀⣰⠇⣾⠀⠀⠈⣩⣥⣄⣿⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢿⡉⠳⡟⣸⠃⠀⠀⠀⠘⢷⣌⠉⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⢿⡉⠳⡟⣸⠃⠀⠀⠀⠘⢷⣌⠉⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠙⢦⣴⠏⠀⠀⠀⠀⠀⠀⠉⠳⠶⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠙⢦⣴⠏⠀⠀⠀⠀⠀⠀⠉⠳⠶⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"); ".red());
let user_dirs = UserDirs::new().expect("error getting user directories"); let mut config_path = PathBuf::new();
let mut config_path = user_dirs.home_dir().to_path_buf(); let user_dirs_res = UserDirs::new();
if user_dirs_res.is_none(){
println!("oof couldn't get the config directory the right way, falling back to the stpuid way...");
let get_home_res = Command::new("echo").arg("$HOME").output();
if get_home_res.is_err(){
println!("ooof the stupid way didn't work... this is a bruh moment, gotta fix your environment variabls!!!");
exit(1);
}
let get_home_output = get_home_res.unwrap().stdout;
let home_string = String::from_utf8_lossy(&get_home_output);
config_path.push(home_string.trim());
}
else{
let user_dirs = user_dirs_res.unwrap();
config_path.push(user_dirs.home_dir());
}
config_path.push(".config/pyro_pentest_tool/conf"); config_path.push(".config/pyro_pentest_tool/conf");
if config_path.as_path().exists() == false{ if config_path.as_path().exists() == false{
install::install(&config_path); install::install(&config_path);
} }
let mut project_base_folder = PathBuf::new();
let mut project_base_notes = PathBuf::new();
let mut tools_folder = PathBuf::new();
let mut terminal_command = String::new();
let mut box_template = String::new();
println!("\nconfig already generated\nloading config file...\n"); println!("\nconfig already generated\nloading config file...\n");
let settings_string = fs::read_to_string(&config_path).expect("error reading config file"); let settings = configuration::load_config(&config_path);
let settings: Vec<&str> = settings_string.split("\n").collect(); for setting in settings.keys(){
for line in settings{ println!("{}: {}", setting, settings[setting].green());
if line.len() > 1{ }
let setting_vec: Vec<&str> = line.split(":").collect(); println!("{}","loading project configs...".green());
match setting_vec[0]{ let projects_res = project_controls::get_projects(&config_path, true);
"Project_files" => project_base_folder.push(setting_vec[1].trim_end()), if projects_res.is_none(){
"Project_notes" => project_base_notes.push(setting_vec[1].trim_end()), println!("{}", "ERROR NO PROJECTS LOADED!!!".red());
"tools_folder" => tools_folder.push(setting_vec[1].trim_end()), println!("{}", "CHECK YOUR PROJECTS.CONF FILE".red());
"box_template" => box_template = setting_vec[1].trim_end().to_owned(), return;
"terminal" => terminal_command = setting_vec[1].trim_end().to_owned(), }
_ => println!("error unknown setting: {}", setting_vec[0]) let mut projects = projects_res.unwrap();
} let help_text = format!("
pentest_tool -> launch cli
pentest_tool gui -> launch gui
pentest_tool anything else -> display this help
");
let args: Vec<String> = args().collect();
if args.len() == 1{
cli::cli(&mut projects, &config_path);
}
else if args[1] == String::from("gui"){
let res = gui::gui();
if res.is_err(){
print_error("error lanching gui!", res.err().unwrap().to_string());
}
else{
res.unwrap();
} }
} }
print!(" else {
Project Folders: {} println!("{}", help_text);
Note Folders: {} }
Tools Folder: {}
distrobox template: {}
terminal_command: {}\n
", project_base_folder.display(), project_base_notes.display(), tools_folder.display(), box_template, terminal_command);
println!("loading project configs...");
let projects = project_controls::get_projects(&config_path);
println!("Enter to start main menu");
let mut enter = String::new();
std::io::stdin().read_line(&mut enter).unwrap();
menu::main_menu(projects, config_path, &project_base_folder, &project_base_notes, &tools_folder, box_template, terminal_command);
} }

View File

@@ -1,149 +0,0 @@
use std::path::PathBuf;
use clearscreen::clear;
use clearscreen;
use crate::Project;
use crate::project_controls;
use crate::box_controls;
use crate::info_controls;
use crate::start_pentest;
fn next_project_id(config_path: &PathBuf) -> i32{
let projects = project_controls::get_projects(config_path);
let mut new_id = 0;
for project in projects.clone(){
if project.id > new_id{
new_id = project.id + 1;
}
}
return new_id;
}
fn get_active_project(projects: &Vec<Project>) -> &Project{
let mut active_project = &projects[0];
for project in projects{
if project.active == true{
active_project = project
}
}
return active_project
}
pub fn main_menu(mut projects: Vec<Project>, config_path: PathBuf, base_files: &PathBuf, base_notes: &PathBuf, tools_dir: &PathBuf, boxtemplate: String, terminal: String){
let mut loopize = true;
let mut new_id = next_project_id(&config_path);
loop {
let active_project = get_active_project(&projects);
let mut response = String::new();
clear().expect("error clearing screen");
print!("
,,,;;::ccccc::;;;::c::;,;::cccccllc::::::;:::;;;;,,;,'',,;,,;;;;;;;:;;;;;,,,,,,,,,,,'''''',,,,,,''''
,;;;::ccccc::::::ccc:;;;:ccccccclc::ccccccc::;;;;;;;;;;,,;;;;;;;;;;;;;;;,,,,,,,,,,,'''''''''',,,,,''
,;;:::ccc:cc:::ccc:::::::ccccclcccllccccllc::::::;;;;;;;;;;;;;;;;;;;;;;,,,,,,,,,,,''''''''...'',,,,'
,;;:::c::ccc::cc::::::::cclollllllolllllccccc::cc:::::;;;;;;;;;;;;;;;;;;,,,,,,,,,,'''''''''''..'',,,
,;::::::ccc::cc::::::ccloodollooooollllcccccc:llc::::::;;;;;;:;;;;;;;;;;;,,,,,,,,,,''''''''''''''',,
,;:::::c:::c::::ccccloddxxddxxddddodollllccclclcccccc:::::::::::::::;;;;;;;;,,,,,,,,,'''''''''''''',
;;:::::::c::c::clllodxxO0OKX0kkOkkxxxxxdooooolcccccccc:::::::cllc::::::::;;;;;,,,,,,,,,,'''''''''',,
;:::::c:cc::cclolclokO0KXNNX00KKK0O0KOxdxxdooccccclllccccccccdkdlcccccllcc::;;;;;;;;,,,,,,,,,,,',,,,
::::::cc::::coxdlllok00KNWNXXX0KXKOKNOkO0kddocccllllllccccclx0Kkodddoodddoollc::;;;;;;;;;,,,,,,,,,,,
:::::::c:::clkkodooxxkO0KX0xookKKkkKNKO0KkdoodolcllllollolldKNNXKKXKKKKKK0Okxdocc:cc:::;;;;;,,,,,,,,
::cc::cc::cldxllolodxxdoddc'.,okkxOXXOdxkxolkOdlllllllldkdokXNNNNNNNNX0kxollcc:::::cclc::;;;;;;,,,,,
:::::::cccldko:,.';cc:;:;....;clllOXOxxOkocoK0xooddollx0Odd0XNNNNNX0Oxdolcccc::;;;;;;:cllc:;;:;,,,,,
;;::c:::ccldkl;'...,''''....',;,';dxdkkdc;cONKxxOOOxddOXOdx0XXNNWNOdddxkOOOOkdllc:;,,,;cool:;;;;;,,;
,;;::;;::llco:,'..''..,.......''.';:ldl;,,:xXNOOXXX0xdkOOddkXNNWWWX00KXNNX0kxddddol:,''';lol:;;:;;,;
,,,;;;;;:coc;;'..;;. .,,;'.......':dxdc;ldc,l00xkXNKxodkkkkk0XNWWMWWWNXKOxdooolooool:;'..,lol::::;;;
',,,;;;;:cllc;,..',. ','. .....;odoo:;co:.'ldldOOx::x0KXX0kk0XNWWWXOxdoooollllllllcc:'..':lc:::;;;
',,,;;;;;:cccc:,. . ..;cccccc:,,''.',,:l:;;:oOXXKOOOkxOXNNNXOxddooooollllllllc,....:c:::;;;
''',,,;;;;;;;cll,.. .. .':lc:c:;,,......,,;:;;:cokkxxO00O0KXNNN0kxkkkxddoollllllll:'...':::::::
.''',,,,,,,,,;:c:,.. ..'. ..','',;;'........',,;:;:::cdxxxddkKXXXKKKKXXXXXX0kdoloolllol;....,;:::::
..'''',,'',,,;;:::;..... ............... .'.....,;,',:ldc;:ldOKKK00KNWWWNNXK0xoooodooooo:'...';;;:;;
....'''''',,;;::cll:,''...... . ..........'...,;;l:,,;oddkOOKNWWWWNX0kdodxxxxdddooc,...',;;;;,
......''''',;::cloodddol;. ...........',.;;,,',:cxdd0KXXKKKXKOkxxkkkxdddooc,...';;,,,,
........''',;:clloddxxdo:'. .. ...........''.'',;c:;cccodddk0KX0OOkxddddddo:...';;;;,,'
..........',;:cclodxxkxdl:,.. ... ......'....'..':c,..'.,c,,,.,cxkO00Okxdddddc'..';:;;;,,'
..........',;;:cloodxkkkdol:'. . ... ...... ......';c'.'...:;',;,'..,lxO00Oxxxo:'...,::;;,,,,
...........',;;:clodxkOOkxdol;. .. .. ... ....',::'.''.',.........'oxdxxxdl;...';::;;;,,''
............',;:clodxkkOOOxddo;. ...... ........',,',................';:clc;,...';::;;,,,'''
............',;:cldxkkOkkkxdddo;. ..... .........,'...........'''','.',,'''....,cc:;;,,'''..
.............';:cldxxkkkkxddddxl,. .... .;c;'...................',;;cc;'...';clolc:;,,'''...
............'';clodxkkkkkxddddddl' ... .:lc;'................. ....',,''';lxkxdlc:;,'''....
........',,;:;coddxkOOOOOkxxddddd:. ... ..,''.................. . ..;cdkkkkxoc:;,'''.....
......',;::cllodxkkOOOOOOkxxxddddc. ... ..,;,'................... .. .':odO0Okdl:;,'''......
.....',;:cloddxxkOOOOOOOkkxxdoooo;. .. ......................... .';cokOOxlc:;,''.......
....,;:clodxxkkOOOkO0OOOOxdlcc;;,...... .';,................. ...',:ldxxxdlc;,''.......
...,:clodooxkkkO0OxO00OOxo:;;,. ........ .''.......... .. .. ..,,,;:codxxdlc:;,'.......
'',;clodolokOkxkOkkO00Oko:;;;. ..... .. .,,........'. .. .. .. ..........;:codocclc:,,'......
___ __ __ ___ __ ___ __
| | |__ | / ` / \\ |\\/| |__ |__| /\\ / ` |__/ |__ |__)
|/\\| |___ |___ \\__, \\__/ | | |___ | | /~~\\ \\__, | \\ |___ | \\
__ ___ ___ __ __
/ _` |__ | |__) | | |\\ | | |\\ | / _`
\\__> |___ | | |/\\| | \\| | | \\| \\__>
NOTE OPTION 18 WILL SAVE YOUR PROJECTS BEFORE QUITTING
Current Project: {} {}
Main Menu:
1 .) Show Active Project
2 .) List Projects
3 .) Switch Active Project
4 .) create new project with Pyro's default tool
5 .) Save Project Information
6 .) Import New Project - and setup new Distrobox
7 .) Remove Project
8 .) Open A New Terminal in Current Active Project
9 .) Open A Terminal In this windows for the current active project
10.) Open Project Files Folder In Dolphin
11.) Open Project Notes Folder In Dolphin
12.) generate userpass file from your obsidian notes
13.) run pyro's initail enum script on a nessus csv for the current project
14.) Print Project Info For Report
15.) Build host discovery cmd command from scope in notes
16.) build portscan command from scope in notes
17.) Stop All Distroboxes
18.) Quit Application
\n", active_project.customer, active_project.project_name);
std::io::stdin().read_line(&mut response).expect("error getting menu input");
clear().expect("error clearing screen");
match response.as_str().trim_end(){
"1" => println!("\nclient: {}\n\nproject: {}\n\nbox: {}\n\nproject files: {}\n\nproject notes: {}\n", active_project.customer ,active_project.project_name, active_project.boxname, active_project.files_folder.display(), active_project.notes_folder.display()),
"2" => {println!("+++++++++++++++++++++");
for project in &projects{
println!("++{}|{}++",project.customer ,project.project_name)}
println!("++++++++++++++++++++")},
"3" => project_controls::switch_project(&mut projects),
"4" => start_pentest::start_pentest(&config_path),
"5" => project_controls::save_projects(&projects, &config_path),
"6" => {new_id = new_id + 1; project_controls::new_project(&mut projects, &base_files, &base_notes, &tools_dir, &boxtemplate, &config_path, new_id)},
"7" => project_controls::remove_project(&mut projects, &config_path),
"8" => box_controls::project_standalone_terminal(active_project.clone(), terminal.clone()),
"9" => box_controls::project_inline_terminal(active_project.clone()),
"10" => info_controls::open_in_dolphin("files", active_project.clone()),
"11" => info_controls::open_in_dolphin("notes", active_project.clone()),
"12" => info_controls::generate_userpass(&active_project),
"13" => info_controls::run_initial_enum(&active_project),
"14" =>info_controls::print_report_information(active_project.clone()),
"15" => info_controls::build_cmd_for_host_discovery(&active_project),
"16" => info_controls::build_cs_portscan_cmd(&active_project),
"17" => box_controls::stop_all_boxes(&projects),
"18" => {project_controls::save_projects(&projects, &config_path);
let mut stop = String::new();
println!("stop all boxes?\ny/n");
std::io::stdin().read_line(&mut stop).unwrap();
if stop.contains("y"){
box_controls::stop_all_boxes(&projects);
}
loopize = false},
_ => println!("uknonwn selection")
}
if loopize == false{
break
}
println!("\n\n\npress enter to return to the menu");
let mut enter = String::new();
std::io::stdin().read_line(&mut enter).unwrap();
}
}

View File

@@ -0,0 +1,854 @@
use std::collections::HashMap;
use std::{default, fs};
use std::fs::{read_to_string, create_dir_all};
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
use std::thread::{sleep, spawn, JoinHandle};
use std::time::Duration;
use colored::Colorize;
use walkdir::WalkDir;
use crate::{get_user_input, print_error, print_informational, print_success};
use crate::Project;
use crate::open_overwrite;
use crate::open_append;
use crate::info_controls;
pub fn parse_normal_nmap_output(project: &Project){
let mut found_file = false;
let mut found_working = false;
let mut file_to_parse = PathBuf::new();
let mut save_path = PathBuf::new();
let files_folder = project.files_folder.clone();
println!("scanning files folder for an nmap file...");
let walkdir_result = WalkDir::new(files_folder);
for entry_res in walkdir_result{
if entry_res.is_ok(){
let entry = entry_res.unwrap();
let entry_name = entry.path().display().to_string();
if entry_name.contains("nmap") || entry_name.contains("port scan") || entry_name.contains("proxychains_output"){
file_to_parse = entry.into_path();
}
else if entry.file_type().is_dir(){
if entry.file_name().to_ascii_lowercase().to_string_lossy().contains("working"){
save_path.push(entry.path());
found_working = true;
}
}
}
}
if !found_working{
save_path.push(&project.files_folder);
}
if file_to_parse.display().to_string().len() > 1{
println!("we found {}", file_to_parse.display());
let res = get_user_input("is the the file you want to parse?");
if res.to_lowercase().contains("y"){
found_file = true;
}
}
if !found_file{
println!("ooof the right file was not found.");
let file_prompt = get_user_input("please enter the full path to the file you want to parse.");
file_to_parse.clear();
file_to_parse.push(file_prompt);
}
let file_read_res = fs::read_to_string(file_to_parse);
if file_read_res.is_err(){
let error = file_read_res.err().unwrap();
println!("{}","ooof error reading the file!".red());
println!("{}", error.to_string().red());
return;
}
let nmap_string = file_read_res.unwrap();
let sections: Vec<&str> = nmap_string.split("Nmap scan report for").collect();
let mut host_ports = Vec::new();
for section in sections{
let mut host = String::new();
for line in section.split("\n").collect::<Vec<&str>>(){
if line.contains("."){
if !line.contains("latency"){
host = line.trim().to_owned();
}
}
else{
if line.contains("open"){
let port = line.split_whitespace().collect::<Vec<&str>>()[0];
host_ports.push(format!("{}:{}", host, port));
}
}
}
}
println!("done parsing.");
if save_path.display().to_string().len() > 1{
save_path.push("parsed_namp.txt");
println!("default save location is {}", save_path.display());
if get_user_input("is this ok?").contains("n"){
let user_path = get_user_input("ok where would you like to save it instead?");
save_path.clear();
save_path.push(user_path);
}
}
else{
let user_path = get_user_input("where would you like to save the file?");
save_path.clear();
save_path.push(user_path);
}
let save_open_res = fs::OpenOptions::new().create(true).append(true).open(&save_path);
if save_open_res.is_err(){
let error = save_open_res.err().unwrap();
println!("{}","oof error opening the save file!".red());
println!("{}", error.to_string().red());
if get_user_input("do you want to print the results to the console instead?").to_lowercase().contains("y"){
for host in &host_ports{
println!("{}", host);
}
}
return;
}
let mut host_port_file = save_open_res.unwrap();
save_path.pop();
save_path.push("parsed_nmap.tsv");
let tsv_open_res = fs::OpenOptions::new().create(true).append(true).open(save_path);
if tsv_open_res.is_err(){
let error = tsv_open_res.err().unwrap();
println!("{}","error opening tsv file!".red());
println!("{}", error.to_string().red());
if get_user_input("do you want to print the results to the console instead?").to_lowercase().contains("y"){
for host in &host_ports{
println!("{}", host);
}
}
return;
}
let mut tsv_file = tsv_open_res.unwrap();
for host in &host_ports{
let outline = format!("{}\n", host);
let split_data: Vec<&str> = host.split(":").collect();
let ip = split_data[0];
let port_split: Vec<&str> = split_data[1].split("/").collect();
let port = port_split[0];
let proto = port_split[1];
let tsv_outline = format!("{}\t{}\t\t{}\n", ip, port, proto);
let host_port_write_res = write!(host_port_file, "{}", outline);
let tsv_write_res = write!(tsv_file, "{}", tsv_outline);
if host_port_write_res.is_err(){
println!("error writing data to file...");
}
if tsv_write_res.is_err(){
println!("error writing data to the tsv file...");
}
}
}
pub fn build_cs_portscan_cmd(project: &Project){
let mut general_note_path = project.notes_folder.clone();
general_note_path.push("general.md");
println!("Reading from: {}", general_note_path.display());
let mut ranges = Vec::new();
let general_note_string = read_to_string(general_note_path);
let mut _note_string = String::new();
match general_note_string{
Err(error) => {println!("error reading file to string!! {}", error); return;},
_=> _note_string = general_note_string.unwrap()
}
let lines: Vec<&str> = _note_string.split("\n").collect();
for line in lines{
if line.contains("|"){
if line.contains("."){
let ip = line.split("|").collect::<Vec<&str>>()[1];
if ip.contains(","){
let ips: Vec<&str> = ip.split(",").collect();
for ip in ips{
ranges.push(ip.trim_end().trim_start());
}
}
else{
ranges.push(ip.trim_end().trim_start());
}
}
}
}
let portscan_cmd = "portscan !!! 1-1024,1433,2222,3306,3389,5000-6000,8000,8080,8443 icmp 512";
let mut _range_command = String::new();
let combined_ranges = ranges.join(",");
let final_command = portscan_cmd.replace("!!!", &combined_ranges);
println!("{}", final_command);
}
pub fn build_cmd_for_host_discovery(project: &Project){
let mut cobalt_strike_response = String::new();
let mut need_shell = false;
println!("will you be running this via cobalt strike? (do you need it to start with \"shell\"?");
let input_result = std::io::stdin().read_line(&mut cobalt_strike_response);
if input_result.is_err(){
println!("error getting user input... not really sure how you did this... just pressing enter would work..... wow");
return;
}
if cobalt_strike_response.to_lowercase().contains("y"){
need_shell = true;
}
let mut general_note_path = project.notes_folder.clone();
general_note_path.push("general.md");
println!("Reading from: {}", general_note_path.display());
let mut ranges = Vec::new();
let general_note_string = read_to_string(general_note_path);
let mut _note_string = String::new();
match general_note_string{
Err(error) => {println!("{} {}", "error reading file to string!!".green(), error.to_string().green()); return;},
_=> _note_string = general_note_string.unwrap()
}
let lines: Vec<&str> = _note_string.split("\n").collect();
for line in lines{
if line.contains("|"){
if line.contains("."){
let ip = line.split("|").collect::<Vec<&str>>()[1];
if ip .contains(","){
let ips: Vec<&str> = ip.split(",").collect();
for ip in ips{
ranges.push(ip.trim_end().trim_start());
}
}
else{
ranges.push(ip.trim_end().trim_start());
}
}
}
}
let mut _discovery_command = "";
_discovery_command = "(for /L %a IN (1,1,254) DO ping /n 1 /w 3 !!!.%a) | find \"Reply\"";
let mut final_command = String::new();
for range in ranges{
let mut network: Vec<&str> = range.split(".").collect();
network.pop();
let network_string = network.join(".");
let mut _range_command = String::new();
if need_shell{
_range_command = format!("{}{}","shell ", _discovery_command.replace("!!!", &network_string));
need_shell = false;
}
else{
_range_command = _discovery_command.replace("!!!", &network_string);
}
final_command.push_str(_range_command.as_str());
final_command.push_str(">> ping_only_replies.txt &");
}
final_command.pop();
print_success(final_command);
}
pub fn parse_csportscan(project: &Project, given_tsv_path: Option<PathBuf>, given_save_path: Option<PathBuf>){
let mut tsv_path = PathBuf::new();
let mut outfile = PathBuf::new();
if given_tsv_path.is_none(){
let mut tsv_path = project.files_folder.clone();
if !get_user_input("do you have the tsv saved in the project folder under working/tsvs/services.tsv?").to_lowercase().contains("y"){
tsv_path.clear();
tsv_path.push(get_user_input("ooof ok, please enter the full path to your tsv file."));
}
tsv_path.push("working/tsvs/services.tsv");
}
else{
tsv_path = given_tsv_path.unwrap();
}
if given_save_path.is_none(){
let mut outfile = tsv_path.clone();
outfile.pop();
outfile.pop();
println!("is {} where you want to save your files?", outfile.display());
if get_user_input("").to_lowercase().contains("n"){
outfile.clear();
outfile.push(get_user_input("ok, please enter the full path to the folder you want to save them to."));
}
}
else{
outfile = given_save_path.unwrap();
}
let mut windows_hosts = Vec::new();
let mut ssh_hosts = Vec::new();
let mut ftp_hosts = Vec::new();
let mut rdp_hosts = Vec::new();
let mut dns_hosts = Vec::new();
let mut snmp_hosts = Vec::new();
let mut web_hosts = Vec::new();
let mut telnet_hosts = Vec::new();
let mut unknown_ports = Vec::new();
let tsv_read_res = read_to_string(tsv_path);
if tsv_read_res.is_err(){
let error = tsv_read_res.err().unwrap();
println!("{}","ooof error reading tsv file!".red());
println!("{}", error.to_string().red());
return;
}
println!("tsv read, parsing lines...");
let tsv_string = tsv_read_res.unwrap();
let lines: Vec<&str> = tsv_string.split("\n").collect();
for line in lines{
let words: Vec<&str> = line.split("\t").collect();
if words.len() > 1{
let host = words[0].to_lowercase().to_owned();
let port = words[1].to_lowercase().to_owned();
let host_entry = format!("{}:{}", &host, &port);
match words[1]{
"135" => {if !windows_hosts.contains(&host){windows_hosts.push(host)}},
"445" => {if !windows_hosts.contains(&host){windows_hosts.push(host)}},
"22" => {if !ssh_hosts.contains(&host){ssh_hosts.push(host);}},
"21" => {if !ftp_hosts.contains(&host){ftp_hosts.push(host);}},
"23" => {if !telnet_hosts.contains(&host){telnet_hosts.push(host)}},
"3389" => {if !rdp_hosts.contains(&host){rdp_hosts.push(host);}},
"80" | "443" | "8080" | "8443" | "4433" | "8000" => {if !web_hosts.contains(&host_entry){web_hosts.push(host_entry);}},
"53" => {if !dns_hosts.contains(&host){dns_hosts.push(host);}},
"161" => {if !snmp_hosts.contains(&host){snmp_hosts.push(host);}},
_ => {
if words.len() == 3{
let banner = words[2].to_lowercase().to_owned();
if words[2].to_lowercase().contains("ssh"){
if !ssh_hosts.contains(&host_entry){
ssh_hosts.push(host_entry);
}
}
else if banner.contains("ftp"){
if !ftp_hosts.contains(&host_entry){
ftp_hosts.push(host_entry);
}
}
else if banner.contains("nginx") || banner.contains("apache"){
if !web_hosts.contains(&host_entry){
web_hosts.push(host_entry);
}
}
else{
continue;
}
}
else if words.len() == 2{
unknown_ports.push(host_entry);
}
}
}
}
}
let host_number_results = format!("
{} Windows hosts found!
{} SSH hosts found!
{} FTP hosts found!
{} Telnet hosts found!
{} SNMP hosts found!
{} DNS hosts found!
{} RDP hosts found!
{} untagged hosts found!
", windows_hosts.len(), ssh_hosts.len(), ftp_hosts.len(), telnet_hosts.len(), snmp_hosts.len(), dns_hosts.len(), rdp_hosts.len(), unknown_ports.len());
print_success(host_number_results);
print_informational("data gathered, writing to notes...");
outfile.push("windows_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut windows_file = file_option.unwrap();
for host in windows_hosts{
let write_res = write!(windows_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing windows_hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("windows hosts file written!");
}
}
}
outfile.pop();
outfile.push("ssh_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut ssh_file = file_option.unwrap();
for host in ssh_hosts{
let write_res = write!(ssh_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing ssh_hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("ssh hosts file written!");
}
}
}
outfile.pop();
outfile.push("telnet_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut telnet_file = file_option.unwrap();
for host in telnet_hosts{
let write_res = write!(telnet_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing _hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("telnet hosts file written!");
}
}
}
outfile.pop();
outfile.push("ftp_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut ftp_file = file_option.unwrap();
for host in ftp_hosts{
let write_res = write!(ftp_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing _hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("fpt hosts file written!");
}
}
}
outfile.pop();
outfile.push("snmp_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut snmp_file = file_option.unwrap();
for host in snmp_hosts{
let write_res = write!(snmp_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing _hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("snmp hosts file written!")
}
}
}
outfile.pop();
outfile.push("dns_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut dns_file = file_option.unwrap();
for host in dns_hosts{
let write_res = write!(dns_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing _hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("dns hosts file written!")
}
}
}
outfile.pop();
outfile.push("rdp_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut rdp_file = file_option.unwrap();
for host in rdp_hosts{
let write_res = write!(rdp_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing _hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("rdp hosts file written!");
}
}
}
outfile.pop();
outfile.push("web_hosts.txt");
let file_option = open_overwrite(&outfile);
if file_option.is_some(){
let mut web_file = file_option.unwrap();
for host in web_hosts{
let write_res = write!(web_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","oooof error writing _hosts.txt!!".red());
println!("{}", error.to_string().red());
}
else{
write_res.unwrap();
print_success("web hosts file written!");
}
}
}
print_informational("tagged port files have been written to, saving untagged ports...");
outfile.pop();
outfile.push("untagged ports");
if !outfile.exists(){
let untagged_res = create_dir_all(&outfile);
if untagged_res.is_err(){
let error = untagged_res.err().unwrap();
println!("{}","ooof error creating untagged folder!".red());
println!("{}", error.to_string().red());
}
else{
untagged_res.unwrap();
print_success("untagged port file written!");
}
}
for line in unknown_ports{
let line_vec:Vec<&str> = line.split(":").collect();
let host = line_vec[0].to_owned();
let port = line_vec[1].to_owned();
let file_name = format!("{}_hosts.txt", port);
outfile.push(file_name);
let write_file_opt = open_append(&outfile);
if write_file_opt.is_some(){
let mut write_file = write_file_opt.unwrap();
let write_res = write!(write_file, "{}\n", host);
if write_res.is_err(){
let error = write_res.err().unwrap();
println!("{}","ooof error writing to file...".red());
println!("{}", error.to_string().red());
}
}
outfile.pop();
}
print_success(format!("DONE all files saved to {}", outfile.display()));
println!("note if no hosts were found for a protocol their files will be empty.");
}
#[allow(unused)]
pub fn run_nmap_portscan(project: &Project) -> Option<JoinHandle<()>>{
let notes_folder = project.notes_folder.clone();
let mut ports_to_scan = vec![String::from("80"), String::from("443"),
String::from("161"),
String::from("22"),
String::from("21"),
String::from("23"),
String::from("8080"),
String::from("8443"),
String::from("4433"),
String::from("135"),
String::from("445"),
String::from("3389"),
String::from("5985"),
String::from("1433"),
String::from("3306"),
String::from("2222"),];
let mut general_notes = notes_folder.clone();
general_notes.push("general.md");
println!("building targets from scope in general notes...");
let targets_res = info_controls::get_scope_entries(project);
if targets_res.is_none(){
return None;
}
let mut targets = targets_res.unwrap();
print_success("Got targets from scope!");
for target in &targets{
println!("{}", target);
}
if get_user_input("does this list look ok?").to_lowercase().contains("n"){
let mut modified_targets = targets.clone();
let mut targets_to_remove = Vec::new();
let mut targets_ready = false;
while !targets_ready{
println!("current target list:");
for target in &modified_targets{
if &targets_to_remove.contains(target) == &false{
println!("{}", target);
}
}
let response = get_user_input("\nc to continue as is\na to add a target to the list\nr to remove a target from the list\nf to start fresh with a blank list of targets");
match response.as_str(){
"c" => targets_ready = true,
"a" => {let new_target = get_user_input("target to add?"); let _ = &mut modified_targets.push(new_target);},
"r" => {let _ = targets_to_remove.push(get_user_input("target to remove?"));},
"f" => {let _ = &mut modified_targets.clear();}
_ => println!("unknown selection"),
}
}
for target in &targets{
println!("{}", target);
}
println!("\nwill be replace with\n");
for target in &modified_targets{
println!("{}", target);
}
if get_user_input("continue?").to_lowercase().contains("n"){
println!("ok exiting this function, feel free to try again...");
return None;
}
targets = modified_targets;
}
println!("the standard ports I scan are:");
for port in &ports_to_scan{
println!("{}", port);
}
if get_user_input("is this ok?").to_lowercase().contains("n"){
let mut ports_ready = false;
ports_to_scan.clear();
while !ports_ready{
let response = get_user_input("enter a port to scan in the nmap format. For example 80-100 or 80,443 Enter END in all caps when you're done.");
match response.as_str(){
"END" => ports_ready = true,
_ => ports_to_scan.push(response),
}
}
}
let proxy = get_user_input("will you be using proxychains for this scan?").to_lowercase().contains("y");
print_success("sweet we have what we need!");
print_informational("Targets:");
for target in &targets{
print_success(format!("{}", target));
}
println!("building portscan command...");
let working_project = project.clone();
let mut save_path = project.files_folder.clone();
save_path.push("working");
if !save_path.exists(){
save_path.clear();
save_path = project.files_folder.clone();
}
save_path.push("services.tsv");
let mut enumeration_notes_path = project.notes_folder.clone();
enumeration_notes_path.push("enumeration.md");
let mut nmap_output = Vec::new();
let nmap_thread = spawn(move || {
if proxy{
let port_scancmd_res = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(working_project.boxname)
.arg("--")
.arg("proxychains")
.arg("nmap")
.arg("-sT")
.arg("-p")
.arg(ports_to_scan.join(","))
.arg(targets.join(" "))
.output();
if port_scancmd_res.is_err(){
let error = port_scancmd_res.err().unwrap();
println!("{}","FROM NMAP THREAD: error running portscan!".red());
println!("{}", error.to_string().red());
return;
}
nmap_output = port_scancmd_res.unwrap().stdout;
}
else{
let port_scancmd_res = Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(working_project.boxname)
.arg("--")
.arg("nmap")
.arg("-sT")
.arg("-p")
.arg(ports_to_scan.join(","))
.arg(targets.join(" "))
.output();
if port_scancmd_res.is_err(){
let error = port_scancmd_res.err().unwrap();
println!("{}","FROM NMAP THREAD: error running portscan!".red());
println!("{}", error.to_string().red());
return;
}
nmap_output = port_scancmd_res.unwrap().stdout;
}
println!("FROM NMAP THREAD: nmap scan finished, parsing the results...");
let nmap_output_string = String::from_utf8_lossy(&nmap_output);
let mut nmap_log_path = save_path.clone();
nmap_log_path.pop();
nmap_log_path.push("nmap.log");
let nmap_log_file = open_append(&nmap_log_path);
if nmap_log_file.is_some(){
let mut nmap_log_file = nmap_log_file.unwrap();
write!(nmap_log_file, "{}", nmap_output_string).unwrap();
print_success("nmap log file written!");
}
let mut host_ports = Vec::new();
let host_sections: Vec<&str> = nmap_output_string.split("Nmap scan report ").collect();
for section in host_sections{
let mut host = String::new();
let mut ports = Vec::new();
let lines: Vec<&str> = section.split("\n").collect();
let mut port_section = false;
for line in lines{
if line.contains("for "){
if !line.contains("(not scanned)"){
host = line.split(" ").collect::<Vec<&str>>()[1..].join(" ").to_owned();
}
}
else if line.contains("Host is up"){
port_section = true;
}
else if port_section{
if line.contains("open"){
let words: Vec<&str> = line.split_whitespace().collect();
if words.len() >= 3{
let port = words [0];
let service = words [2];
let port_entry = format!("{}:{}", port, service);
ports.push(port_entry);
}
}
}
}
for port in ports{
let entry_words: Vec<&str> = port.split(":").collect();
let port = entry_words[0];
let service = entry_words[1];
let host_entry = format!("{}\t{}\t{}", host, port, service);
host_ports.push(host_entry);
}
}
let services_file_open_res = open_overwrite(&save_path);
if services_file_open_res.is_none(){
println!("FROM NMAP THREAD: error opening the services.tsv file to write!");
println!("FROM NMAP THREAD: exiting");
return;
}
let mut services_file = services_file_open_res.unwrap();
let enumeration_open_res = open_append(&enumeration_notes_path);
if enumeration_open_res.is_none(){
print_error("error opening enumeration notes file!", "".to_owned());
}
let services_write_res = write!(services_file, "host\tport\tbanner\tnotes\n");
if services_write_res.is_err(){
let error = services_write_res.err().unwrap();
println!("{}","FROM NMAP THREAD: error writing to the services.tsv file!".red());
println!("{}", error.to_string().red());
return;
}
let mut enumeration_write = false;
if enumeration_open_res.is_some(){
enumeration_write = true;
}
let mut host_all_ports: HashMap<String, Vec<String>> = HashMap::new();
for host in &host_ports{
write!(services_file, "{}\n", host).unwrap();
let host_data: Vec<&str> = host.split("\t").collect();
let address = host_data[0].to_owned();
let port = host_data[1].to_owned();
let service = host_data[2].to_owned();
if host_all_ports.contains_key(&address){
host_all_ports.get_mut(&address).unwrap().push(format!("{}:{}", port, service));
}
else {
host_all_ports.insert(address, vec![format!("{}:{}", port, service)]);
}
}
if enumeration_write{
let mut enumeration_file = enumeration_open_res.unwrap();
let write_res = write!(enumeration_file, "\n# Port Scan\n");
if write_res.is_ok(){
write_res.unwrap();
for host in host_all_ports.keys(){
write!(enumeration_file, "## {}\n| HOST | PORT | SERVICE |\n| ---- | ---- | ------- |\n", host).unwrap();
for port_entry in &host_all_ports[host]{
let parts: Vec<&str> = port_entry.split(":").collect();
let port = parts[0];
let services = parts[1];
write!(enumeration_file, "| {} | {} | {} |\n", host, port, services).unwrap();
}
}
write!(enumeration_file, "\n---\n").unwrap();
}
}
print_success(format!("FROM NMAP THREAD: Parsing done! You're scan results are saved in cobalt strike services.tsv format at {}", save_path.display()));
});
sleep(Duration::from_secs(10));
return Some(nmap_thread);
}
pub fn build_nmap_command(project: &Project){
fn get_targets() -> Vec<String>{
let mut targets = Vec::new();
println!("please enter the ranges/ips to scan one per line, and enter END all caps when done.");
loop {
let response = get_user_input("ip or range to add?");
if response == "END".to_owned(){
break;
}
else{
targets.push(response);
}
}
return targets;
}
let targets_res = info_controls::get_scope_entries(project);
let mut targets = Vec::new();
let mut ports_to_scan = vec![String::from("80"), String::from("443"),
String::from("161"),
String::from("22"),
String::from("21"),
String::from("23"),
String::from("8080"),
String::from("8443"),
String::from("4433"),
String::from("135"),
String::from("445"),
String::from("3389"),
String::from("5985"),
String::from("1433"),
String::from("3306"),
String::from("2222"),];
let mut save_path = project.files_folder.clone();
if targets_res.is_none(){
println!("couldn't get target list from your notes!");
targets = get_targets();
}
else{
println!("got targets from the cope in notes!");
for target in targets_res.unwrap(){
targets.push(target);
}
}
for target in &targets{
println!("{}", target);
}
if get_user_input("is this ok?").to_lowercase().contains("n"){
println!("oooof ok, we'll have you recreate it manually.");
targets = get_targets();
}
println!("These are the ports we're going to scan.");
for port in &ports_to_scan{
println!("{}", port);
}
if get_user_input("is this ok?").to_lowercase().contains("n"){
println!("oof ok, rebuild it manually.");
println!("please enter the ports you want to scan, one per line, enter END in all caps when you're finished.");
ports_to_scan.clear();
loop{
let port = get_user_input("port to add?");
if port.contains("END"){
break;
}
else{
ports_to_scan.push(port);
}
}
}
println!("we are going to save the output to {}/working/nmap_output.txt", save_path.display());
if get_user_input("is this ok").to_lowercase().contains("n"){
println!("oof ok.");
save_path.clear();
save_path.push(get_user_input("full path to where you want to save it then?"));
}
else{
save_path.push("working/nmap_output.txt");
}
let ports_string = ports_to_scan.join(",");
let targets_string = targets.join(" ");
println!("\nYour portscan command is:");
if get_user_input("will you be using proxychains for this scan?").to_lowercase().contains("y"){
print_success(format!("\n\nproxychains nmap -sT -p {} {} -Pn | tee {}", ports_string, targets_string, save_path.display()));
}
else{
print_success(format!("nmap -p {} {} -Pn | tee {}", ports_string, targets_string, save_path.display()));
}
}

View File

@@ -1,13 +1,25 @@
use std::env; use std::env;
use std::fs; use std::fs;
use std::fs::read_to_string;
use std::io::stdin; use std::io::stdin;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use std::process; use std::process;
use std::process::Command;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use std::str::FromStr; use std::str::FromStr;
use colored::Colorize;
use crate::configuration;
use crate::get_user_input;
use crate::open_overwrite;
use crate::print_informational;
use crate::tableize;
use crate::Project; use crate::Project;
use crate::box_controls::make_box;
use crate::print_success;
use crate::print_error;
pub fn switch_project(projects: &mut Vec<Project>){ pub fn switch_project(projects: &mut Vec<Project>){
for project in projects.clone(){ for project in projects.clone(){
@@ -23,53 +35,109 @@ pub fn switch_project(projects: &mut Vec<Project>){
for project in projects{ for project in projects{
if project.id == new_id{ if project.id == new_id{
project.active = true; project.active = true;
println!("project found switching to {} {}", project.customer, project.project_name); print_success(format!("project found switching to {} {}", project.customer, project.project_name));
env::set_var("CURRENT_PROJECT_BOX", project.boxname.clone()); env::set_var("CURRENT_PROJECT_BOX", project.boxname.clone());
} }
else if project.id != new_id{ else if project.id != new_id{
project.active = false; project.active = false;
} }
else{ else{
println!("error unknown project id") print_error("error selecting new project", String::from("unknown project id selected."));
} }
} }
} }
else{ else{
println!("error we need user input here dummy!"); print_error("error selecting new project", String::from("we need input here dummy!"));
} }
} }
pub fn save_projects(projects: &Vec<Project>, config_path: &PathBuf){ pub fn save_projects(projects: &Vec<Project>, config_path: &PathBuf, display: &bool) -> Option<String>{
let mut save_file_path = config_path.clone(); let mut save_file_path = config_path.clone();
let mut active_set = false; let mut active_set = false;
save_file_path.pop(); save_file_path.pop();
save_file_path.push("projects.conf"); save_file_path.push("projects.conf");
let mut save_file = fs::File::create(save_file_path).expect("error creating save_file"); let save_file_res = fs::File::create(save_file_path);
save_file.write_all(b"customer:name:notes:files:active:time:box_name\n").expect("error writing first line to file"); if save_file_res.is_err(){
let error = save_file_res.err().unwrap().to_string();
if *display{
print_error("error creating save file.", error);
}
else{
return Some(format!("Error saving file! {}", error));
}
return None;
}
let mut save_file = save_file_res.unwrap();
save_file.write_all(b"Current Loaded Projects Config File\n").expect("error writing first line to file");
for project in projects{ for project in projects{
let default = format!{"{}:{}:{}:{}:", project.customer, project.project_name, project.notes_folder.display(), project.files_folder.display()}; let default = format!{"{}:{}:{}:{}:", project.customer, project.project_name, project.notes_folder.display(), project.files_folder.display()};
let mut _outline = String::new(); let mut _outline = String::new();
if project.active{ if project.active{
if active_set == false{ if active_set == false{
_outline = format!("{}yes:{}\n", default, project.boxname); _outline = format!("{}yes:{}:{}\n", default, project.boxname, project.stage);
active_set = true; active_set = true;
} }
} }
else{ else{
_outline = format!("{}no:{}\n", default, project.boxname); _outline = format!("{}no:{}:{}\n", default, project.boxname, project.stage);
} }
save_file.write_all(_outline.as_bytes()).expect("error writing outline"); let write_res = save_file.write_all(_outline.as_bytes());
if write_res.is_err(){
let error = write_res.err().unwrap().to_string();
if *display{
print_error("error saving projects.conf file!", error);
}
else{
return Some(format!("error saving projects! {}", error));
}
return None;
}
write_res.unwrap();
} }
return None;
} }
pub fn new_project(projects: &mut Vec<Project>, project_dir: &PathBuf, notes_dir: &PathBuf, tools_dir: &PathBuf, boxtemplate: &String, config_path: &PathBuf, new_id: i32){ #[allow(unused)]
let mut new_project_dir = project_dir.clone(); pub fn new_project(projects: &mut Vec<Project>, project_dir: &PathBuf, notes_dir: &PathBuf, tools_dir: &PathBuf, boxtemplate: &String, config_path: &PathBuf, new_id: i32, upcoming_files: &PathBuf, upcoming_notes: &PathBuf, fingerprint: bool, terminal: Option<String>, display: &bool){
let mut new_note_dir = notes_dir.clone(); let mut new_project_dir = PathBuf::new();
let mut new_note_dir = PathBuf::new();
let mut existing_folders = String::new(); let mut existing_folders = String::new();
let mut customer_name = String::new(); let mut customer_name = String::new();
let mut project_name = String::new(); let mut project_name = String::new();
let mut project_stage = String::new();
loop{
let mut stage_response = String::new();
println!("what stage is this project in?");
print!("
1.) current
2.) upcoming
");
let stage_result = stdin().read_line(&mut stage_response);
if stage_result.is_err(){
println!("we need input here dummy, try again...");
}
else{
match &stage_response.trim_end(){
&"1" => {project_stage = "current".to_owned(); break;},
&"2" => {project_stage = "upcoming".to_owned(); break;},
_ => println!("unknown option, try again...")
}
}
}
if project_stage.contains("current"){
new_project_dir = project_dir.clone();
new_note_dir = notes_dir.clone();
}
else if project_stage.contains("upcoming"){
new_project_dir = upcoming_files.clone();
new_note_dir = upcoming_notes.clone();
}
else{
println!("unknown stage!!")
}
println!("{}", new_project_dir.display());
println!("do you have an existing notes and folder structure to copy over?\ny/n"); println!("do you have an existing notes and folder structure to copy over?\ny/n");
std::io::stdin().read_line(&mut existing_folders).unwrap(); std::io::stdin().read_line(&mut existing_folders).unwrap();
if existing_folders.contains("y") || existing_folders.contains("Y"){ if existing_folders.contains("y") || existing_folders.contains("Y"){
@@ -183,54 +251,6 @@ pub fn new_project(projects: &mut Vec<Project>, project_dir: &PathBuf, notes_dir
} }
thread::sleep(Duration::from_secs(2)); thread::sleep(Duration::from_secs(2));
let box_name = format!("atarchbox_{}", customer_name); let box_name = format!("atarchbox_{}", customer_name);
let mut box_name_path = new_project_dir.clone();
box_name_path.push("boxname");
let mut box_name_file = fs::File::create(box_name_path).expect("Error creating box name file");
box_name_file.write_all(&box_name.as_bytes()).expect("error writing boxname to box file");
let pentest_volume = format!("{}:/pentest:rw", new_project_dir.display());
let toold_volume = format!("{}:/tools:rw", tools_dir.display());
println!("distrobox create --root --clone {} --volume {} --volume {} --name {}", boxtemplate, toold_volume, pentest_volume, box_name);
let distrobox_result = process::Command::new("distrobox")
.arg("create")
.arg("--root")
.arg("--clone")
.arg(boxtemplate)
.arg("--volume")
.arg(&toold_volume)
.arg("--volume")
.arg(&pentest_volume)
.arg("--name")
.arg(&box_name)
.status()
.expect("error getting distrobox status");
if distrobox_result.success(){
println!("we made a distrobox oh boy!");
let distrobox_start_result = process::Command::new("distrobox")
.arg("enter")
.arg("--root")
.arg(&box_name)
.arg("--")
.arg("sudo")
.arg("-s")
.arg("ln")
.arg("-sf")
.arg("/pentest/boxname")
.arg("/etc/boxname")
.status()
.expect("error getting response from distrobox start");
if distrobox_start_result.success(){
println!("distrobox was started as well!!!! good job me!");
}
else{
println!("ooof did not start successfully try entering it yoruself");
println!("distrobox enter --rrot {} -- sudo -s ln -sf /pentest/boxname /etc/boxname", &box_name);
}
}
else{
println!("ooof distrobox did not work.... try creating it yourself");
println!("distrobox create --root --clone {} --volume {} --volume {} --name {}", boxtemplate, &toold_volume, &pentest_volume, &box_name);
println!("distrobox enter --rrot {} -- sudo -s ln -sf /pentest/boxname /etc/boxname", &box_name);
}
let new_project = Project{customer: customer_name.trim_end().to_owned(), let new_project = Project{customer: customer_name.trim_end().to_owned(),
project_name: project_name.trim_end().to_owned(), project_name: project_name.trim_end().to_owned(),
notes_folder: new_note_dir, notes_folder: new_note_dir,
@@ -238,20 +258,29 @@ pub fn new_project(projects: &mut Vec<Project>, project_dir: &PathBuf, notes_dir
active: false, active: false,
id: new_id, id: new_id,
boxname: box_name, boxname: box_name,
stage: project_stage.to_owned()
}; };
if project_stage.contains("current"){
make_box(&new_project, &tools_dir, &boxtemplate, true, fingerprint, terminal, display);
}
projects.push(new_project); projects.push(new_project);
save_projects(projects, config_path); save_projects(projects, config_path, &true);
} }
pub fn remove_project(projects: &mut Vec<Project>, config_path: &PathBuf){ pub fn remove_project(projects: &mut Vec<Project>, config_path: &PathBuf, given_id: Option<String>){
for project in projects.clone(){
println!("{} {} {}", project.id, project.customer, project.project_name);
}
let mut project_to_remove = String::new(); let mut project_to_remove = String::new();
println!("project to remove?"); if given_id.is_none(){
std::io::stdin().read_line(&mut project_to_remove).unwrap(); for project in projects.clone(){
println!("{} {} {}", project.id, project.customer, project.project_name);
}
println!("project to remove?");
std::io::stdin().read_line(&mut project_to_remove).unwrap();
}
else{
project_to_remove = given_id.unwrap();
}
if project_to_remove.len() > 1{ if project_to_remove.len() > 1{
let mut project_to_keep = Vec::new(); let mut project_to_keep = Vec::new();
if project_to_remove.len() > 0{ if project_to_remove.len() > 0{
@@ -282,7 +311,7 @@ pub fn remove_project(projects: &mut Vec<Project>, config_path: &PathBuf){
if project_set{ if project_set{
projects.clear(); projects.clear();
projects.append(&mut project_to_keep); projects.append(&mut project_to_keep);
save_projects(&projects, config_path); save_projects(&projects, config_path, &true);
} }
else{ else{
println!("error no prjects found to remove") println!("error no prjects found to remove")
@@ -290,48 +319,510 @@ pub fn remove_project(projects: &mut Vec<Project>, config_path: &PathBuf){
} }
else{ else{
println!("we need user in put here dummy!!"); println!("no project selected! returning...");
} }
} }
else{ else{
println!("we need input here dummy!"); println!("no project selected! returning...");
} }
} }
pub fn get_projects(config_path: &PathBuf) -> Vec<Project>{ pub fn get_projects(config_path: &PathBuf, show: bool) -> Option<Vec<Project>>{
let mut mut_config_path = config_path.clone(); let mut mut_config_path = config_path.clone();
mut_config_path.pop(); mut_config_path.pop();
mut_config_path.push("projects.conf"); mut_config_path.push("projects.conf");
let mut bkup_config_path = mut_config_path.clone();
bkup_config_path.pop();
bkup_config_path.push("projects.conf.bkup");
let bkup_result = fs::copy(&mut_config_path, &bkup_config_path);
if bkup_result.is_err(){
let error = bkup_result.err().unwrap();
println!("{}","error backing up the projects.conf file!".red());
println!("{}", error.to_string().red());
}
let mut projects = Vec::new(); let mut projects = Vec::new();
let projects_string = fs::read_to_string(mut_config_path).expect("error reading projects file"); let projects_string_res = fs::read_to_string(mut_config_path);
if projects_string_res.is_err(){
let error = projects_string_res.err().unwrap();
println!("{}", "Error Loading Project!".red());
println!("{}", error.to_string().red());
return None;
}
let projects_string = projects_string_res.unwrap();
let project_lines:Vec<&str> = projects_string.split("\n").collect(); let project_lines:Vec<&str> = projects_string.split("\n").collect();
let mut first = 0; let mut first = 0;
let mut already_active = false; let mut already_active = false;
for line in project_lines{ for line in project_lines{
//println!("{}", line);
first = first + 1; first = first + 1;
if first != 1{ if first != 1{
if line.len() > 1{ if line.len() > 1{
let settings: Vec<&str> = line.split(":").collect(); let settings: Vec<&str> = line.split(":").collect();
let customer = settings[0].to_owned(); if settings.len() > 5{
let project = settings[1].to_owned(); // debug config file...
let notes_string = settings[2].to_owned(); /*let mut count = 0;
let folder_string = settings[3].to_owned(); for settin in &settings{
let notes_folder = PathBuf::from_str(&notes_string.trim_end()).expect("error reading notes string"); println!("{}: {}", count, settin);
let project_folder = PathBuf::from_str(&folder_string.trim_end()).expect("error reading folding sering"); count = count + 1;
let mut active = false; }*/
let boxname = settings[5].to_owned(); let customer = settings[0].to_owned();
if settings[4] == "yes"{ let project = settings[1].to_owned();
if already_active == false{ let notes_string = settings[2].to_owned();
env::set_var("CURRENT_PROJECT_BOX", boxname.clone()); let folder_string = settings[3].to_owned();
already_active = true; let notes_folder = PathBuf::from_str(&notes_string.trim_end()).expect("error reading notes string");
active = true; let project_folder = PathBuf::from_str(&folder_string.trim_end()).expect("error reading folding sering");
let mut active = false;
let boxname = settings[5].to_owned();
if settings[4] == "yes"{
if already_active == false{
env::set_var("CURRENT_PROJECT_BOX", boxname.clone());
already_active = true;
active = true;
}
} }
let project_stage = settings[6].to_owned();
let new_project = Project{customer: customer, project_name: project, files_folder: project_folder, notes_folder: notes_folder, active: active, id: first, boxname: boxname, stage: project_stage};
if show{
println!("{} {} {}", &new_project.customer, &new_project.project_name, "LOADED!".green());
}
projects.push(new_project);
} }
let new_project = Project{customer: customer, project_name: project, files_folder: project_folder, notes_folder: notes_folder, active: active, id: first, boxname: boxname};
println!("{} {} LOADED!", &new_project.customer, &new_project.project_name);
projects.push(new_project);
} }
} }
} }
return projects return Some(projects)
}
pub fn print_upcoming_projects(projects: &Vec<Project>, display: &bool) -> Option<String>{
let mut lines = vec![String::from("CUSTOMER||PROJECT")];
for project in projects{
if project.stage.contains("upcoming"){
let new_line = format!("{}||{}", project.customer, project.project_name);
lines.push(new_line);
}
}
let return_string = tableize(lines, &false);
if *display{
println!("{}", return_string);
return None;
}
else{
return Some(return_string);
}
}
pub fn promote_project(projects: &mut Vec<Project>, config_path: &PathBuf, project_dir: &PathBuf, notes_dir: &PathBuf, tools_dir: &PathBuf, boxtemplate: &String, fingerprint: bool, given_data: Option<String>, terminal:String) -> Option<String>{
let working_projects = projects.clone();
let display = given_data.is_none();
let mut return_data = String::new();
let mut selection = String::new();
if display{
for project in &working_projects{
if project.stage.contains("upcoming"){
println!("{}.) {}:{}", project.id, project.customer, project.project_name);
}
}
println!("which project to promote?");
let result = stdin().read_line(&mut selection);
if result.is_err(){
println!("we need input here dummy try again....");
}
result.unwrap();
println!("{}", project_dir.display());
}
else{
selection = given_data.unwrap();
}
let promote_id: i32 = selection.trim_end().parse().unwrap();
let mut projects_to_save = Vec::new();
for project in &working_projects{
if project.id == promote_id{
let mut promoted_project = project.clone();
let mut new_files_dir = project_dir.clone();
let mut new_notes_dir = notes_dir.clone();
new_files_dir.push(&promoted_project.customer);
new_notes_dir.push(&promoted_project.customer);
fs::create_dir_all(&new_files_dir).unwrap();
fs::create_dir_all(&new_notes_dir).unwrap();
let folder_move_success = process::Command::new("mv")
.arg("-i")
.arg(&project.files_folder)
.arg(&new_files_dir.display().to_string())
.status().expect("unable to call the system mv command");
let note_move_success = process::Command::new("mv")
.arg("-i")
.arg(&project.notes_folder)
.arg(&new_notes_dir.display().to_string())
.status().expect("unable to call the system mv command");
if folder_move_success.success(){
if display{
println!("we copied the project folder correctly!!");
}
else{
return_data.push_str("we copied the project folder correctly!!");
}
let mut remove_folder = PathBuf::new();
remove_folder.push(&project.files_folder);
remove_folder.pop();
let remove_files_res = fs::remove_dir_all(remove_folder);
if remove_files_res.is_err(){
if display{
println!("error removing the original files folder form the upcomming folder, manual cleanup required");
}
else{
return_data.push_str("\nerror removing the original files folder form the upcomming folder, manual cleanup required");
}
}
else{
remove_files_res.unwrap();
if display{
println!("upcoming files folder cleanup successful!");
}
else{
return_data.push_str("\nupcoming files folder cleanup successful!");
}
}
}
else{
if display{
println!("failed to copy the project folder, try to move it manually!");
}
else{
return_data.push_str("\nfailed to copy the project folder, try to move it manually!");
}
}
if note_move_success.success(){
if display{
print_success("we copied the notes folder correctly!!");;
}
else{
return_data.push_str("\nwe copied the notes folder correctly!!");
}
print_success("we copied the notes folder correctly!!");
let mut remove_folder = PathBuf::new();
remove_folder.push(&project.files_folder);
remove_folder.pop();
let remove_notes_res = fs::remove_dir_all(remove_folder);
if remove_notes_res.is_err(){
if display{
println!("error removing the original notes folder form the upcomming folder, manual cleanup required");
}
else{
return_data.push_str("\nerror removing the original notes folder form the upcomming folder, manual cleanup required");
}
}
else{
remove_notes_res.unwrap();
if display{
println!("upcoming notes folder cleanup successful!");
}
else{
return_data.push_str("\nupcoming notes folder cleanup successful!");
}
}
}
else{
if display{
println!("failed to copy the notes folder, try to move it manually!");
}
else{
return_data.push_str("\nfailed to copy the notes folder, try to move it manually!");
}
}
new_files_dir.push(&promoted_project.project_name);
new_notes_dir.push(&promoted_project.project_name);
promoted_project.files_folder = new_files_dir;
promoted_project.notes_folder = new_notes_dir;
promoted_project.stage = "current".to_owned();
thread::sleep(Duration::from_secs(3));
make_box(&promoted_project, tools_dir, boxtemplate, true, fingerprint, Some(terminal.clone()), &display);
projects_to_save.push(promoted_project);
}
else{
projects_to_save.push(project.clone());
}
}
projects.clear();
projects.append(&mut projects_to_save);
save_projects(&projects_to_save, config_path, &display);
if display{
print_success("project promoted successfully!");
return None;
}
else{
return_data.push_str("\nproject promoted successfully!");
return Some(return_data);
}
}
pub fn list_projects(projects: &Vec<Project>, display: &bool) -> String{
let mut lines = vec!["customer||project||stage".to_owned()];
for project in projects{
let new_line = format!("{}||{}||{}", project.customer, project.project_name, project.stage);
lines.push(new_line);
}
let out_text= tableize(lines.clone(), display);
return out_text.to_owned();
}
pub fn separate_personal_work_projects(config_path: &PathBuf){
let config = config_path.clone();
let mut config_personal = config_path.clone();
config_personal.pop();
config_personal.push("conf.personal");
let mut config_work = config_path.clone();
config_work.pop();
config_work.push("conf.working");
let mut projects_conf_path = config_path.clone();
projects_conf_path.pop();
projects_conf_path.push("projects.conf");
let mut working_conf_path = config_path.clone();
working_conf_path.pop();
working_conf_path.push("projects.work");
let mut personal_conf_path = config_path.clone();
personal_conf_path.pop();
personal_conf_path.push("projects.personal");
let project_read_res = read_to_string(&projects_conf_path);
if project_read_res.is_err(){
print_error("error reading current projects config file!", project_read_res.err().unwrap().to_string());
return;
}
let project_string = project_read_res.unwrap();
let project_lines: Vec<&str> = project_string.split("\n").collect();
let mut personal_projects = Vec::new();
let mut work_projects = Vec::new();
let mut default = String::new();
print_success("current projects loaded!");
print_informational("we'll now go through project by project, answer yes or no to the following questions...");
for line in project_lines{
if line.contains("default"){
default = line.trim_end().to_owned();
}
else{
let words: Vec<&str> = line.split(":").collect();
if words.len() > 3{
println!("{} {}", words[0], words[1]);
if get_user_input("should this project be added to your personal projects config files?").to_lowercase().contains("y"){
personal_projects.push(line.to_owned());
print_success(format!("{}:{} {}",words[0], words[1], "successfully added to personal config!"));
if get_user_input("Do you also want to keep it in your work projects config?").to_lowercase().contains("y"){
work_projects.push(line.to_owned());
}
}
else{
work_projects.push(line.to_owned());
}
}
}
}
let work_config_open_res = fs::OpenOptions::new().create(true).write(true).open(working_conf_path);
if work_config_open_res.is_err(){
print_error("error opening work projects config file!", work_config_open_res.err().unwrap().to_string());
return;
}
let person_config_open_res = fs::OpenOptions::new().create(true).write(true).open(personal_conf_path);
if person_config_open_res.is_err(){
print_error("error opening personal projects config file!", person_config_open_res.err().unwrap().to_string());
return;
}
let mut work_config_file = work_config_open_res.unwrap();
let mut personal_config_file = person_config_open_res.unwrap();
let work_write_success = write!(work_config_file, "Work Config File\n");
let personal_write_success = write!(personal_config_file, "Personal Config File\n");
if work_write_success.is_err(){
print_error("error writing to work config file!", work_write_success.err().unwrap().to_string());
return;
}
else{
work_write_success.unwrap();
}
if personal_write_success.is_err(){
print_error("error writing personal config file!", personal_write_success.err().unwrap().to_string());
return;
}
else{
personal_write_success.unwrap();
}
write!(personal_config_file, "{}\n", default).unwrap();
write!(work_config_file, "{}\n", default).unwrap();
for project in work_projects{
write!(work_config_file, "{}\n", project).unwrap();
}
for project in personal_projects{
write!(personal_config_file, "{}\n", project).unwrap();
}
print_success("projects separated successfully!");
print_informational("now we'll separate the configuration files.");
let current_config_type = get_user_input("is the current settings loaded for work? (not personal)");
if current_config_type.to_lowercase().contains("y"){
let copy_res = fs::copy(&config, &config_work);
if copy_res.is_err(){
print_error("error copying config file to config.work!", copy_res.unwrap().to_string());
let copy_cmd = format!("cp {} {}", &config.display(), &config_work.display());
print_informational(copy_cmd);
}
else{
copy_res.unwrap();
print_success("conf.work successfully created!");
}
print_informational("Next we will create a new config file for work.\nIt will be save as conf.personal");
print_informational("\nplease select create from scratch at the next prompt, and fill out the information for your personal settings");
}
else{
let copy_res = fs::copy(&config, &config_personal);
if copy_res.is_err(){
print_error("error copying config file to config.personal!", copy_res.unwrap().to_string());
let copy_cmd = format!("cp {} {}", &config.display(), &config_personal.display());
print_informational(copy_cmd);
}
else{
copy_res.unwrap();
print_success("conf.personal successfully created!");
}
print_informational("Next we will create a new config file for work.\nIt will be save as conf.working");
print_informational("\nplease select create from scratch at the next prompt, and fill out the information for your work settings");
}
configuration::generate_tool_config(&config);
print_informational("great! now we're going to copy that to the specified new config file!");
if current_config_type.to_lowercase().contains("y"){
let copy_res = fs::copy(&config, &config_personal);
if copy_res.is_err(){
print_error("error copying to conf.personal!", copy_res.err().unwrap().to_string());
let copy_cmd = format!("plase run the following command in your terminal\ncp {} {}", &config.display(), &config_personal.display());
print_informational(copy_cmd);
}
else{
copy_res.unwrap();
print_success("conf.personal successfully created!");
let remove_res = fs::remove_file(&config);
if remove_res.is_err(){
print_error("error restroing original config file!", remove_res.err().unwrap().to_string());
}
else{
remove_res.unwrap();
let copy_res = fs::copy(&config_work, &config);
if copy_res.is_err(){
print_error("error restoring original config file!", copy_res.err().unwrap().to_string());
}
else{
copy_res.unwrap();
print_success("original configuration successfully restored!");
}
}
}
}
else{
let copy_res = fs::copy(&config, &config_work);
if copy_res.is_err(){
print_error("error copying to conf.work!", copy_res.err().unwrap().to_string());
let copy_cmd = format!("plase run the following command in your terminal\ncp {} {}", &config.display(), &config_work.display());
print_informational(copy_cmd);
}
else{
copy_res.unwrap();
print_success("conf.personal successfully created!");
let remove_res = fs::remove_file(&config);
if remove_res.is_err(){
print_error("error restoring original config!", remove_res.err().unwrap().to_string());
}
else{
remove_res.unwrap();
let copy_res = fs::copy(&config_personal, &config);
if copy_res.is_err(){
print_error("error resoring original config!", copy_res.err().unwrap().to_string());
}
else{
copy_res.unwrap();
print_success("original configuration successfully restored!");
}
}
}
}
}
pub fn swith_to_personal(config: &PathBuf) -> Option<Vec<Project>>{
let mut personal_config_path = config.clone();
personal_config_path.pop();
personal_config_path.push("conf.personal");
let mut projects_path = config.clone();
projects_path.pop();
let mut personal_projects = config.clone();
personal_projects.pop();
projects_path.push("projects.conf");
personal_projects.push("projects.personal");
let personal_projects_read_res = read_to_string(&personal_projects);
if personal_projects_read_res.is_err(){
print_error("error reading personal projects!", personal_projects_read_res.err().unwrap().to_string());
return None;
}
let person_projects_string = personal_projects_read_res.unwrap();
let open_res = open_overwrite(&projects_path);
if open_res.is_none(){
print_error("error opening projects.conf file!", "".to_string());
return None;
}
let mut project_conf = open_res.unwrap();
write!(project_conf, "{}", person_projects_string).unwrap();
print_success("projects changed successfully!");
let new_projects = get_projects(config, true);
let remove_res = fs::remove_file(config);
if remove_res.is_ok(){
remove_res.unwrap();
let copy_res = fs::copy(personal_config_path, config);
if copy_res.is_ok(){
copy_res.unwrap();
print_success("settings successfully changed!");
}
else{
print_error("error changing settings!", copy_res.err().unwrap().to_string());
}
}
else{
print_error("error removing current settings!", remove_res.err().unwrap().to_string());
}
return new_projects;
}
pub fn swith_to_work(config: &PathBuf) -> Option<Vec<Project>>{
let mut work_config_path = config.clone();
work_config_path.pop();
work_config_path.push("conf.working");
let mut projects_path = config.clone();
projects_path.pop();
let mut work_projects = config.clone();
work_projects.pop();
projects_path.push("projects.conf");
work_projects.push("projects.work");
let work_projects_read_res = read_to_string(work_projects);
if work_projects_read_res.is_err(){
print_error("error reading personal projects!", work_projects_read_res.err().unwrap().to_string());
return None;
}
let work_projects_string = work_projects_read_res.unwrap();
let open_res = open_overwrite(&projects_path);
if open_res.is_none(){
print_error("", "error opening projects.conf file!".to_string());
return None;
}
let mut project_conf = open_res.unwrap();
write!(project_conf, "{}", work_projects_string).unwrap();
print_success("projects changed successfully!");
let new_projects = get_projects(config, true);
let remove_res = fs::remove_file(config);
if remove_res.is_ok(){
remove_res.unwrap();
let copy_res = fs::copy(work_config_path, config);
if copy_res.is_ok(){
copy_res.unwrap();
print_success("settings successfully changed!");
}
else{
print_error("error changing settings!", copy_res.err().unwrap().to_string());
}
}
else{
print_error("error removing current settings!", remove_res.err().unwrap().to_string());
}
return new_projects;
} }

View File

@@ -1,25 +1,62 @@
use std::fs; use std::fs;
use std::io::stdin; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use colored::Colorize;
fn external(project_folder_path: String,comapny_name: &String, project_name: &String, passtemp: PathBuf){ use crate::Project;
let mut general_notes = fs::File::create(format!("{}/general.md", &project_folder_path)).expect("error creating general notes file"); use crate::project_controls;
let mut attack_notes = fs::File::create(format!("{}/attacks.md", &project_folder_path)).expect("error creating attack notes file"); use crate::get_user_input;
let mut host_notes = fs::File::create(format!("{}/host_notes.md", &project_folder_path)).expect("error creating host notes file");
let mut finding_notes = fs::File::create(format!("{}/findings.md", &project_folder_path)).expect("error creating findings notes file");
// for tagging pub fn create_project_folder(path: &mut PathBuf, folder: &str){
let year = project_name.split("_").collect::<Vec<&str>>()[1]; path.push(folder);
let project_type = "External"; let result = fs::create_dir_all(&path);
writeln!(&mut general_notes, "#{} #{} #{} #general", comapny_name, project_type, year).expect("error writing to general notes file"); if result.is_err(){
writeln!(&mut attack_notes, "#{} #{} #{} #attack", comapny_name, project_type, year).expect("error writing tags on attack notes"); println!("error creating {} directory!", folder);
writeln!(&mut host_notes, "##{} #{} #{} #host_notes", comapny_name, project_type, year).expect("error writing tag lin in host notes"); }
writeln!(&mut finding_notes, "#{} #{} #{} #findings", comapny_name, project_type, year).expect("error writing tag line on findings"); else{
writeln!(&mut general_notes, "# Scope").expect("error writing to general notes file"); result.unwrap();
writeln!(&mut general_notes, "\n| IP | Third Party | Approval |").expect("error writing to general notes file"); }
writeln!(&mut general_notes, "| -- | ----------- | -------- |").expect("error writing to general notes file"); path.pop();
writeln!(&mut general_notes, "# PPC").expect("failed to write general notes"); }
write!(&mut general_notes, "
fn create_note_file(path: &PathBuf) -> Option<File>{
let result = fs::File::create(path);
if result.is_err(){
println!("error creating {} try manually!", path.display());
return None;
}
else{
let file = result.unwrap();
return Some(file);
}
}
pub fn external(passtemp: &PathBuf, project: &Project){
// using a pathbuf to create files.
let mut notes_path = project.notes_folder.clone();
let file_creation_res = fs::create_dir_all(&notes_path);
if file_creation_res.is_err(){
let error = file_creation_res.err().unwrap();
println!("{}","error creating notes folder!".red());
println!("{}", error.to_string().red())
}
else{
file_creation_res.unwrap();
}
notes_path.push("general.md");
let general_notes_result = create_note_file(&notes_path);
if general_notes_result.is_some(){
let mut general_notes = general_notes_result.unwrap();
// for tagging
let project_type = "External";
writeln!(&mut general_notes, "#{} #{} #general", project.customer, project_type).expect("error writing to general notes file");
writeln!(&mut general_notes, "# Scope").expect("error writing to general notes file");
writeln!(&mut general_notes, "\n| IP | Third Party | Approval |").expect("error writing to general notes file");
writeln!(&mut general_notes, "| -- | ----------- | -------- |").expect("error writing to general notes file");
writeln!(&mut general_notes, "# PPC").expect("failed to write general notes");
write!(&mut general_notes, "
Planning call notes: Planning call notes:
* methodolgy * methodolgy
* whole month testing window * whole month testing window
@@ -41,21 +78,16 @@ Planning call notes:
* password attacks * password attacks
* password sprays 3-12 hours * password sprays 3-12 hours
* lock out policy - * lock out policy -
|\n").expect("faile to write pentest notes");
## Enumeration in progress }
notes_path.pop();
### DNS Bruteforcing notes_path.push("attacks.md");
let attack_notes_result = create_note_file(&notes_path);
| domain name | IP | inscope? | if attack_notes_result.is_some(){
| ----------- | -- | -------- | let mut attack_notes = attack_notes_result.unwrap();
writeln!(&mut attack_notes, "#{} #{} #attack", project.customer, "external").expect("error writing tags on attack notes");
# Attack Surface Enumeration write!(&mut attack_notes,"
| IP | host notes | needs? |
| -- | ---------- | ------ |\n").expect("faile to write pentest notes");
// set up the basics for our attack notes
write!(&mut attack_notes,"
# Directory Bruteforcing # Directory Bruteforcing
- [ ] example.com - [ ] example.com
@@ -74,59 +106,81 @@ write!(&mut attack_notes,"
passwords tried: passwords tried:
* password\n * password\n
" ).expect("failed to write attack notes template"); " ).expect("failed to write attack notes template");
let new_spray_path = format!("{}/passwordspray.md", &project_folder_path); }
fs::copy(passtemp,new_spray_path).unwrap(); notes_path.pop();
notes_path.push("password_spray.md");
let pass_result = fs::copy(&passtemp, &notes_path);
if pass_result.is_err(){
println!("error copying password spray file! try manually");
}
else{
pass_result.unwrap();
}
notes_path.pop();
notes_path.push("host_notes.md");
let host_notes_result = create_note_file(&notes_path);
if host_notes_result.is_some(){
let mut host_notes = host_notes_result.unwrap();
writeln!(&mut host_notes, "##{} #{} #host_notes", project.customer, "external").expect("error writing tag lin in host notes");
}
notes_path.pop();
notes_path.push("findings.md");
let findings_notes_result = create_note_file(&notes_path);
if findings_notes_result.is_some(){
let mut finding_notes = findings_notes_result.unwrap();
writeln!(&mut finding_notes, "#{} #{} #findings", project.customer, "external").expect("error writing tag line on findings");
}
} }
fn internal(project_folder_path: String, comapny_name: &String, project_name: &String, passtemp: PathBuf){ pub fn internal(passtemp: &PathBuf, project: &Project){
let loot_folder = format!("{}/l00t", project_folder_path); let mut notes_path = project.notes_folder.clone();
fs::create_dir_all(&loot_folder).expect("error creating loot directory"); let file_creation_res = fs::create_dir_all(&notes_path);
let mut general_notes = fs::File::create(format!("{}/general.md", &project_folder_path)).expect("error creating general notes file"); if file_creation_res.is_err(){
let mut attack_notes = fs::File::create(format!("{}/attacks.md", &project_folder_path)).expect("error creating attack notes file"); let error = file_creation_res.err().unwrap();
let mut finding_notes = fs::File::create(format!("{}/findings.md", &project_folder_path)).expect("error creating findings notes file"); println!("{}","error creating notes folder!".red());
let mut systeminfo = fs::File::create(format!("{}/systeminfo", &project_folder_path)).expect("error creating systeminfo note file"); println!("{}", error.to_string().red())
let mut netsta = fs::File::create(format!("{}/netstat", &project_folder_path)).expect("error creating netstat file"); }
let mut creds_notes = fs::File::create(format!("{}/creds.md", &loot_folder)).expect("error creating creds note"); else{
let mut todo_notes = fs::File::create(format!("{}/todo.md", &project_folder_path)).expect("error creating todo notes"); file_creation_res.unwrap();
let mut cleanup_notes = fs::File::create(format!("{}/cleanup.md", &project_folder_path)).expect("error creating cleanup notes"); }
let mut dump_notes = fs::File::create(format!("{}/dumps.md", &loot_folder)).expect("error creating password spray notes"); notes_path.push("password_spray.md");
let mut enum_notes = fs::File::create(format!("{}/initial_enum.md", &project_folder_path)).expect("error creating password spray notes"); println!("copying from {} to {}", passtemp.display(), &notes_path.display());
let enum_obsidian_path = format!("Hack_Notes/pentest_notes/upcomming/{customer}//initial_enum.md", customer = comapny_name); let pass_result = fs::copy(&passtemp, &notes_path);
// for tagging notes if pass_result.is_err(){
let oyear = project_name.split("_").collect::<Vec<&str>>()[0]; let error = pass_result.err().unwrap();
let year = format!("year-{}", oyear); println!("{}","error copying password spray file, try again manually!".red());
let project_type = "Internal"; println!("{}", error.to_string().red())
writeln!(&mut creds_notes, "#{} #{} #{} #l00t #creds", comapny_name, project_type, year).expect("error writing creds notes"); }
writeln!(&mut dump_notes, "#{} #{} #{} #l00t #dumps", comapny_name, project_type, year).expect("error writing creds notes"); else{
writeln!(&mut cleanup_notes, "#{} #{} #{} #cleanup", comapny_name, project_type, year).expect("error writing to cleanup notes"); pass_result.unwrap();
writeln!(&mut general_notes, "#{} #{} #{} #general", comapny_name, project_type, year).expect("error writing to general notes file"); }
writeln!(&mut attack_notes, "#{} #{} #{} #attack", comapny_name, project_type, year).expect("error writing attack note tags"); notes_path.pop();
writeln!(&mut todo_notes, "#{} #{} #{} #todo", comapny_name, project_type, year).expect("error writing tag line on todo"); notes_path.push("l00t");
writeln!(&mut finding_notes, "#{} #{} #{} #findings", comapny_name, project_type, year).expect("error writing tags line on findings"); fs::create_dir_all(&notes_path).expect("error creating loot directory");
writeln!(&mut systeminfo, "#{} #{} #{} #general", comapny_name, project_type, year).expect("error writing tag line for system info"); notes_path.push("creds.md");
writeln!(&mut systeminfo, "#{} #{} #{} #Password_sprays", comapny_name, project_type, year).expect("error writing tag line for password spraying"); let creds_notes_result = create_note_file(&notes_path);
writeln!(&mut netsta, "#{} #{} #{} #general", comapny_name, project_type, year).expect("error writing tagline in the netstat file"); if creds_notes_result.is_some(){
writeln!(&mut enum_notes, "#{} #{} #{} #enum", comapny_name, project_type, year).expect("error writing tagline in the netstat file"); let mut creds_notes = creds_notes_result.unwrap();
writeln!(&mut creds_notes, "# CREDS").expect("error writing creds notes"); writeln!(&mut creds_notes, "#{} #{} #l00t #creds", project.customer, "internal").expect("error writing creds notes");
writeln!(&mut creds_notes, "\n| System | username | password |").expect("error writing creds notes"); writeln!(&mut creds_notes, "# CREDS").expect("error writing creds notes");
writeln!(&mut creds_notes, "| ------ | -------- | -------- |").expect("error writing creds notes"); writeln!(&mut creds_notes, "\n| System | username | password |").expect("error writing creds notes");
writeln!(&mut creds_notes, "\n\n").expect("error writing creds notes"); writeln!(&mut creds_notes, "| ------ | -------- | -------- |").expect("error writing creds notes");
writeln!(&mut creds_notes, "# HASHES\n\n").expect("error writing creds notes"); writeln!(&mut creds_notes, "\n\n").expect("error writing creds notes");
writeln!(&mut creds_notes, "| TYPE | USER | HASH |").expect("error writing creds notes"); writeln!(&mut creds_notes, "# HASHES\n\n").expect("error writing creds notes");
writeln!(&mut creds_notes, "| ---- | ---- | ---- |").expect("error writing creds notes"); writeln!(&mut creds_notes, "| TYPE | USER | HASH |").expect("error writing creds notes");
writeln!(&mut cleanup_notes, "- [ ] Breach machine C-temp-fr").expect("error writing to cleanup notes"); writeln!(&mut creds_notes, "| ---- | ---- | ---- |").expect("error writing creds notes");
writeln!(&mut cleanup_notes, "- [ ] (continue to add as needed").expect("error writing ot cleanup notes"); }
writeln!(&mut general_notes, "# Scope\n").expect("error writing to general notes file"); notes_path.pop();
writeln!(&mut general_notes, "PASTE SCOPE FROM EXCELL HERE (THE EXCEL TO MARKDOWN TABLE PLUGIN WILL FORMAT FOR YOU").expect("shouldn't ever fail"); notes_path.pop();
writeln!(&mut dump_notes, "# SAM\n\n").expect("shouldn't ever fail"); notes_path.push("general.md");
writeln!(&mut dump_notes, "## system name\n").expect("shouldn't ever fail"); let general_result = create_note_file(&notes_path);
writeln!(&mut dump_notes, "```").expect("shouldn't ever fail"); if general_result.is_some(){
writeln!(&mut dump_notes, "```\n\n").expect("shouldn't ever fail"); let mut general_notes = general_result.unwrap();
writeln!(&mut dump_notes, "# LSASS\n\n").expect("shouldn't ever fail"); writeln!(&mut general_notes, "#{} #{} #general", project.customer, "internal").expect("error writing to general notes file");
writeln!(&mut dump_notes, "## system name\n").expect("shouldn't ever fail"); writeln!(&mut general_notes, "# Scope\n").expect("error writing to general notes file");
writeln!(&mut dump_notes, "```").expect("shouldn't ever fail"); writeln!(&mut general_notes, "PASTE SCOPE FROM EXCELL HERE (THE EXCEL TO MARKDOWN TABLE PLUGIN WILL FORMAT FOR YOU").expect("shouldn't ever fail");
writeln!(&mut dump_notes, "```").expect("shouldn't ever fail"); write!(&mut general_notes, "
write!(&mut general_notes, "
On the call: On the call:
Introductions Introductions
@@ -151,7 +205,15 @@ Do they have a specific contact
Email any follow-up items from the call to the PM Email any follow-up items from the call to the PM
").expect("error writing PPC text"); ").expect("error writing PPC text");
write!(&mut attack_notes," }
notes_path.pop();
let enum_path = format!("{}/initial_enum.md", notes_path.display());
notes_path.push("attacks.md");
let attack_result = create_note_file(&notes_path);
if attack_result.is_some(){
let mut attack_notes = attack_result.unwrap();
writeln!(&mut attack_notes, "#{} #{} #attack", project.customer, "internal").expect("error writing attack note tags");
write!(&mut attack_notes,"
# current dat (ex: 7/5) # current dat (ex: 7/5)
Got Persistence via (schtasks, bat schtasks, startup folder) Got Persistence via (schtasks, bat schtasks, startup folder)
@@ -206,15 +268,29 @@ powerup.ps1/sharpup.exe notes.
", enum = enum_obsidian_path).expect("error writing to attack notes for internal tests"); ", enum = enum_path).expect("error writing to attack notes for internal tests");
write!(&mut finding_notes, " }
# normal findings notes_path.pop();
notes_path.push("findings.md");
let findings_result = create_note_file(&notes_path);
if findings_result.is_some(){
let mut finding_notes = findings_result.unwrap();
writeln!(&mut finding_notes, "#{} #{} #findings", project.customer, "internal").expect("error writing tags line on findings");
write!(&mut finding_notes, "
# normal findings
# data exfil # data exfil
## [Sarting Username] ## [Sarting Username]
").expect("error writing to findings notes on internal"); ").expect("error writing to findings notes on internal");
write!(&mut todo_notes, " }
notes_path.pop();
notes_path.push("todo.md");
let todo_result = create_note_file(&notes_path);
if todo_result.is_some(){
let mut todo_notes = todo_result.unwrap();
writeln!(&mut todo_notes, "#{} #{} #todo", project.customer, "internal").expect("error writing tag line on todo");
write!(&mut todo_notes, "
- [ ] local checks - [ ] local checks
- [ ] find shares - [ ] find shares
@@ -227,93 +303,134 @@ powerup.ps1/sharpup.exe notes.
- [ ] passwords in AD Descriptions? - [ ] passwords in AD Descriptions?
- [ ] password spray - [ ] password spray
").expect("error writing todo list"); ").expect("error writing todo list");
write!(&mut netsta," }
``` notes_path.pop();
notes_path.push("cleanup.md");
```").expect("error writing code block to system info"); let cleanup_result = create_note_file(&notes_path);
write!(&mut systeminfo," if cleanup_result.is_some(){
``` let mut cleanup_notes = cleanup_result.unwrap();
writeln!(&mut cleanup_notes, "#{} #{} #cleanup", project.customer, "internal").expect("error writing to cleanup notes");
```").expect("error writing code block to system info"); writeln!(&mut cleanup_notes, "- [ ] Breach machine C-temp-fr").expect("error writing to cleanup notes");
let new_pass_path = format!("{}/passwordspray.md", &project_folder_path); writeln!(&mut cleanup_notes, "- [ ] (continue to add as needed").expect("error writing ot cleanup notes");
println!("{} | {}", passtemp.display(), new_pass_path); }
fs::copy(passtemp, new_pass_path).unwrap();
} }
pub fn start_pentest(config_path: &PathBuf) { pub fn vishing(project: &Project){
let mut pentest_notes = String::new(); let mut notes_path = project.notes_folder.clone();
let mut project_files = String::new(); let mknote_folder_res = fs::create_dir_all(&notes_path);
let mut company_name = String::new(); if mknote_folder_res.is_err(){
let mut project_name = String::new(); let error = mknote_folder_res.err().unwrap();
let mut config_file_path_buf = config_path.clone(); println!("{}","Error creating notes folder!".red());
let mut passpray_path = config_file_path_buf.clone(); println!("{}", error.to_string().red());
passpray_path.push("passwordspray.md"); return;
config_file_path_buf.push("new_projects.conf");
let config_string = fs::read_to_string(config_file_path_buf).expect("error reading ");
if config_string.contains("folder_path") && config_string.contains("notes_path"){
let config_string_vec: Vec<&str> = config_string.split("\n").collect();
for line in config_string_vec{
if line.contains("project_folder_path"){
let line_vec: Vec<&str> = line.split(":").collect();
project_files = line_vec[1].to_owned();
}
else if line.contains("project_notes_path"){
let line_vec: Vec<&str> = line.split(":").collect();
pentest_notes = line_vec[1].to_owned();
}
}
} }
println!("Project files path: {}\nProject Notes path: {}", project_files, pentest_notes); notes_path.push("general.md");
println!("Comapny Name?"); let general_notes_res = create_note_file(&notes_path);
match std::io::stdin().read_line(&mut company_name){ if general_notes_res.is_some(){
Ok(_result) => (), let mut general_notes = general_notes_res.unwrap();
Err(_e) => {println!("we need input here dummy!"); return;} write!(general_notes, "# Scope\n\n").unwrap();
} write!(general_notes, "
println!("project Name?"); Introductions
match stdin().read_line(&mut project_name){
Ok(_result) => (),
Err(_e) => {println!("we need input here dummy!"); return;}
}
//remove new lines from input
company_name = company_name.trim_end().to_owned();
project_name = project_name.trim_end().to_owned();
let project_folder_path = format!("{}/{}/{}", pentest_notes, company_name,project_name);
println!("setting folder creation paths...");
let project_files_folder_path = format!("{}/{}/{}", project_files, company_name, project_name);
let working_folder = format!("{}/working", &project_files_folder_path);
let writing_folder = format!("{}/writing", &project_files_folder_path);
let screeenshot_folder = format!("{}/screenshots",&writing_folder);
let delivery_folder = format!("{}/delivery", &project_files_folder_path);
// make the folders for this project's notes and files
println!("creating directory structure for notes and file folders...");
fs::create_dir_all(&project_folder_path).expect("Error creating project folder");
fs::create_dir_all(&project_files_folder_path).expect("Error creating project file folder");
fs::create_dir_all(&working_folder).expect("error creating working directory");
fs::create_dir_all(&writing_folder).expect("Error creating writing direcotry");
fs::create_dir(&screeenshot_folder).expect("error creating screenshots folder");
fs::create_dir_all(&delivery_folder).expect("Error creating delivery direcotry");
if project_name.contains("internal"){
println!("internal pentest type detected, auto populating notes with internal layout...");
internal(project_folder_path,&company_name, &project_name, passpray_path);
}
else if project_name.contains("external") {
println!("external pentest type detected, auto populating notes with external layout...");
external(project_folder_path, &company_name, &project_name, passpray_path);
}
else if project_name.contains("webapp") {
println!("not implemented yet sorry");
println!("default file folder structure used, and empty notes folder created...")
}
else{
println!("unknown project type detected, default folder strucutre and empty note folder created...");
print!("
Known project types:
Internal Penetration Test
External Penetartion Test
If this test is actually one of these types please include the type in the project name parameter, example: 2023_internal_pentest. have they been vished before?
If this test is a common test and you would like a default note structure implemented in this script let Kevin \"Kage\" Gunter know and supply an example markdown note sheet."); if yes ask what the purpose of that vishing was, gain a foothold, or other?
}
ask the purpose of this test, just see if we can get creds or testing a vetting process, or any specific goals for the engagement.
four main aspects
1. verbal confirmation and verification of information
2. run commands on the system they're on
3. go to a specific website
4. join a screen sharing session with us
pretexts:
default is third party it.
Vector -
ask for any questions, comments, or concerns.
").unwrap();
}
notes_path.pop();
notes_path.push("findings.md");
let findings_res = create_note_file(&notes_path);
if findings_res.is_some(){
let mut findings_file = findings_res.unwrap();
write!(findings_file, "# Findings to write up\n\n\n# Delivery Notes\n").unwrap();
}
notes_path.pop();
notes_path.push("pretext.md");
let pretext_res = create_note_file(&notes_path);
if pretext_res.is_some(){
let mut pretext_file = pretext_res.unwrap();
write!(pretext_file, "\n\n\n#Script").unwrap();
write!(pretext_file, "
Hello I'm [name] from [Find Local IT Shop]. We were brought in to help your normal IT guys with some of the menial tasks so they can focus on more import improvement projects. As part of this we're making sure our inventory management system is checking in correctly and up to date, this should only take a minute or two. Is now bad time to talk?
Great I just need to confirm that my inventory report here is accurate.
Are you currently running Windows 11?
can you confirm your user name is [metadata username]?
Your primary browser is firfox?
Oh thats strange it seems our report is wrong then... I don't think our program on your computer is checking in correctly... uhhh I want to make sure you're getting all the windows updates we need to be compliant.
Hold the windows key on your keyboard and press the r button. in the box that opens up type cmd.exe and press enter.
This will open a scary black box, but don't worry I'll walk you through what we need here, it'll be pretty easy.
In that box type systemifo all one word and press enter.
Scroll up through that output and find the section that talks about hotfixes, how many are installed?
That doesn't seem like the right number to me, can you read me the last 3 that are listed there?
yeah you're definitely not getting all of the windows updates. This is going to take a bit of troubleshooting to figure out. Would you mind hopping in a Zoom call with me and sharing your screen so I can check a few things? This should only take a couple of minutes.
(open up the services manager and scroll through it, check some program files folders, and run a few commands in cmd to act like I'm troubleshooting.)
Hmmm everything looks ok on this end. I'm going to do some troubleshooting on the server side and see if we can get to the bottom of this. I don't think we'll need anything else from you to fix this, but if that changes I'll let you know. Thank you for your time.
").unwrap();
}
notes_path.pop();
notes_path.push("calls.md");
create_note_file(&notes_path);
}
pub fn start_pentest(config_path: &PathBuf, projects: &mut Vec<Project>, id: i32, upcoming_files: &PathBuf, upcoming_notes: &PathBuf, boxtemplate: &String, password_spray_file: &PathBuf) {
let mut project_files = upcoming_files.clone();
let mut project_notes = upcoming_notes.clone();
let customer_name = get_user_input("Customer name?");
let project_name = get_user_input("Project Name?");
project_files.push(&customer_name);
project_files.push(&project_name);
project_notes.push(&customer_name);
project_notes.push(&project_name);
println!("Files: {}\nNotes: {}\n\n", project_files.display(), project_notes.display());
let confirm_response = get_user_input("does this look ok?");
if confirm_response.to_lowercase().contains("n"){
println!("oof sorry");
return;
}
let mut working = project_files.clone();
create_project_folder(&mut working, "working");
create_project_folder(&mut working, "writing");
create_project_folder(&mut working, "delivery");
let project_boxname = format!("{}_{}", boxtemplate, customer_name);
let new_prject = Project{customer:customer_name.clone(), project_name:project_name.clone(), notes_folder:project_notes.clone(), files_folder:project_files.clone(),active:false, boxname:project_boxname.clone(),stage:"upcoming".to_owned(), id};
if project_name.to_lowercase().contains("external"){
external(password_spray_file, &new_prject);
}
else if project_name.to_lowercase().contains("internal"){
internal(password_spray_file, &new_prject);
}
else if project_name.to_lowercase().contains("vishing"){
vishing(&new_prject);
}
projects.push(new_prject);
project_controls::save_projects(projects, config_path, &false);
println!("project created and saved to the projects config file!");
} }

View File

@@ -0,0 +1,43 @@
use std::{env::set_current_dir, path::PathBuf};
use std::process::Command;
use colored::Colorize;
use walkdir::WalkDir;
use crate::print_success;
pub fn update_git_tools(tools_dir: &PathBuf){
let mut folders = Vec::new();
for entry in WalkDir::new(tools_dir).max_depth(2){
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir(){
folders.push(path.to_owned());
}
}
for folder in folders{
let cd_res = set_current_dir(&folder);
if cd_res.is_err(){
let error = cd_res.err().unwrap();
println!("{}","error changing directory!".red());
println!("{}", error.to_string().red());
}
else{
let _cd = cd_res.unwrap();
let git_command_res = Command::new("git")
.arg("pull")
.arg("--autostash")
.status();
if git_command_res.is_err(){
let error = git_command_res.err().unwrap();
println!("{}","error running git pull command!".red());
println!("{}", error.to_string().red());
}
else{
let git_command = git_command_res.unwrap();
if git_command.success(){
print_success(format!("successfully updated {}", folder.display()));
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
use std::path::PathBuf;
use colored::Colorize;
use walkdir::WalkDir;
use crate::get_user_input;
use crate::print_success;
pub fn sharp_persist_command(tools_dir: &PathBuf){
let filename = "SharPersist.exe";
let mut binary_path = String::new();
for entry in WalkDir::new(tools_dir){
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() && path.file_name().unwrap() == filename{
binary_path = path.to_str().unwrap().to_string();
}
}
if get_user_input("will you be running this is cobalt strike?").to_lowercase().contains("y"){
let beacon_path = get_user_input("excellent! Please enter the path to your beacon file on the victim machine.");
println!("how will you run it?");
println!("1.) execute-assembly\n2.) inline-execute-assembly\n3.) from disk");
match get_user_input("selection?").as_str(){
"1" => println!("execute-assembly {} -t schtask -c \"{}\" -n FRPersist -m add -o hourly", binary_path, beacon_path),
"2" => println!("inlineExecute-Assembly --dotnetassembly {} --assemblyargs -t schtask -c \"{}\" -n FRPersist -m add -o hourly --etw --amsi", binary_path, beacon_path),
"3" => println!("upload {}\n\nrun .\\SharPersist.exe -t schtask -c \"{}\" -n FRPersist -m add -o hourly", binary_path, beacon_path),
_ => println!("{}","unknown selection".red())
}
return;
}
let file_to_run = get_user_input("path to the file you want to run for persistence?");
print_success("ok then, upload the following file to the machine");
print_success(format!("{}", binary_path));
print_success(format!("C:\\path\\to\\your\\sharpersist.exe -t schtask -c \"{}\" -n FRPersist -m add -o hourly", file_to_run));
return;
}

BIN
resources/bell.mp3 Normal file

Binary file not shown.

1
resources/credits.txt Normal file
View File

@@ -0,0 +1 @@
https://www.soundjay.com/misc/small-bell-ring-01a.mp3