Files
pentest_tool/pentest_tool/src/enumeration.rs

362 lines
16 KiB
Rust

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 dns_lookup::lookup_host;
use crate::get_user_input;
use crate::Project;
use crate::open_append;
#[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!");
println!("{}", error);
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);
return;
}
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");
}
println!("From DNS Enumeration Thread: Finished gathering data for {} writing to notes...", domain);
write!(enumeration_file, "{}", &out_data).unwrap();
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();
println!("error opening enumeration notes file!");
println!("{}", error);
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!");
println!("{}", error);
return;
}
println!("sleeping for 10 seconds to allow for sudo password input.");
sleep(Duration::from_secs(10));
let gobuser_output = gobuster_cmd_res.unwrap().stdout;
println!("From Gobuster Thread: Sudomain enumeration Done!");
let gobuster_string = String::from_utf8_lossy(&gobuser_output);
let mut domain_names = Vec::new();
let lines: Vec<&str> = gobuster_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!");
println!("{}", error);
return;
}
write_res.unwrap();
});
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");
println!("{}", error);
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!");
println!("{}", error);
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!");
println!("{}", error);
return;
}
println!("sleeping for 10 seconds to allow for sudo password input.");
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();
}
}
}
});
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!");
println!("{}", error);
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?");
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!");
println!("{}", error);
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();
}
}
});
return Some(all_dns_handle);
}