fixed a bug in project promotion where the folder for the project name
would be duplicated, and added rustbuster to the rust tools available!
This commit is contained in:
Generated
+708
-3
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ anyhow = "1.0.103"
|
|||||||
clap = { version = "4.6.1", features = ["derive"] }
|
clap = { version = "4.6.1", features = ["derive"] }
|
||||||
clipboard = "0.5.0"
|
clipboard = "0.5.0"
|
||||||
crossterm = "0.29.0"
|
crossterm = "0.29.0"
|
||||||
|
dns-lookup = "4.0.1"
|
||||||
fs_extra = "1.3.0"
|
fs_extra = "1.3.0"
|
||||||
headless_chrome = "1.0.22"
|
headless_chrome = "1.0.22"
|
||||||
ipnet = "2.12.0"
|
ipnet = "2.12.0"
|
||||||
@@ -17,6 +18,7 @@ rand = "0.10.2"
|
|||||||
ratatui = "0.30.0"
|
ratatui = "0.30.0"
|
||||||
rayon = "1.12.0"
|
rayon = "1.12.0"
|
||||||
rcgen = "0.14.8"
|
rcgen = "0.14.8"
|
||||||
|
reqwest = { version = "0.13.4", features = ["blocking"] }
|
||||||
rhai = { version = "1.24.0", features = ["metadata", "sync"] }
|
rhai = { version = "1.24.0", features = ["metadata", "sync"] }
|
||||||
rustc-hash = "2.1.2"
|
rustc-hash = "2.1.2"
|
||||||
rustls = "0.23.43"
|
rustls = "0.23.43"
|
||||||
@@ -25,4 +27,5 @@ sysinfo = "0.39.6"
|
|||||||
textwrap = "0.16.2"
|
textwrap = "0.16.2"
|
||||||
tokio = { version = "1.52.3", features = ["full"] }
|
tokio = { version = "1.52.3", features = ["full"] }
|
||||||
tokio-rustls = "0.26.4"
|
tokio-rustls = "0.26.4"
|
||||||
|
trust-dns-resolver = "0.23.2"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
|
|||||||
+61
-11
@@ -6,6 +6,7 @@ use crossterm::{
|
|||||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
layout::{Constraint, Layout},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
|
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
|
||||||
};
|
};
|
||||||
@@ -248,19 +249,68 @@ pub fn run_tui(
|
|||||||
state.config.get(setting).unwrap()
|
state.config.get(setting).unwrap()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let info_area_height = top_chunks[1].height.saturating_sub(2) as usize;
|
if state.progress_bars.is_empty() {
|
||||||
if state.info_scroll as usize > info_lines.len().saturating_sub(info_area_height) {
|
let info_area_height = top_chunks[2].height.saturating_sub(2) as usize;
|
||||||
state.info_scroll = info_lines.len().saturating_sub(info_area_height) as u16;
|
if state.info_scroll as usize > info_lines.len().saturating_sub(info_area_height) {
|
||||||
}
|
state.info_scroll = info_lines.len().saturating_sub(info_area_height) as u16;
|
||||||
let info_paragraph = Paragraph::new(info_lines)
|
}
|
||||||
.block(
|
let info_paragraph = Paragraph::new(info_lines)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title("Selected Project Information"),
|
||||||
|
)
|
||||||
|
.scroll((state.info_scroll, 0))
|
||||||
|
.wrap(ratatui::widgets::Wrap { trim: false });
|
||||||
|
f.render_widget(info_paragraph, top_chunks[2]);
|
||||||
|
} else {
|
||||||
|
let progress_section = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Percentage(60), Constraint::Percentage(40)])
|
||||||
|
.split(top_chunks[2]);
|
||||||
|
let info_area_height = top_chunks[2].height.saturating_sub(2) as usize;
|
||||||
|
if state.info_scroll as usize > info_lines.len().saturating_sub(info_area_height) {
|
||||||
|
state.info_scroll = info_lines.len().saturating_sub(info_area_height) as u16;
|
||||||
|
}
|
||||||
|
let info_paragraph = Paragraph::new(info_lines)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title("Selected Project Information"),
|
||||||
|
)
|
||||||
|
.scroll((state.info_scroll, 0))
|
||||||
|
.wrap(ratatui::widgets::Wrap { trim: false });
|
||||||
|
f.render_widget(info_paragraph, progress_section[0]);
|
||||||
|
let mut progress_lines = Vec::new();
|
||||||
|
state.progress_bars.iter().for_each(|progress| {
|
||||||
|
if let Ok(status) = progress.read() {
|
||||||
|
let percentage = status.complete / status.total * 100;
|
||||||
|
if percentage == 100 {
|
||||||
|
progress_lines.push(Line::from(Span::styled(
|
||||||
|
format!(
|
||||||
|
"{} - {}/{} ({}%)",
|
||||||
|
status.name, status.complete, status.total, percentage
|
||||||
|
),
|
||||||
|
Style::default().fg(Color::Green),
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
progress_lines.push(Line::from(Span::styled(
|
||||||
|
format!(
|
||||||
|
"{} - {}/{} ({}%)",
|
||||||
|
status.name, status.complete, status.total, percentage
|
||||||
|
),
|
||||||
|
Style::default().fg(Color::Magenta),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let progress_paragraph = Paragraph::new(progress_lines).block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.title("Selected Project Information"),
|
.title("Operations in progress"),
|
||||||
)
|
);
|
||||||
.scroll((state.info_scroll, 0))
|
f.render_widget(progress_paragraph, progress_section[1]);
|
||||||
.wrap(ratatui::widgets::Wrap { trim: false });
|
}
|
||||||
f.render_widget(info_paragraph, top_chunks[2]);
|
|
||||||
let mut output_lines = Vec::new();
|
let mut output_lines = Vec::new();
|
||||||
state.output.iter().for_each(|text| {
|
state.output.iter().for_each(|text| {
|
||||||
let line = Line::from(Span::raw(text));
|
let line = Line::from(Span::raw(text));
|
||||||
|
|||||||
+304
-4
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::rust_tools::rustwitness;
|
||||||
use clipboard::ClipboardContext;
|
use clipboard::ClipboardContext;
|
||||||
use clipboard::ClipboardProvider;
|
use clipboard::ClipboardProvider;
|
||||||
use fs_extra::dir::{CopyOptions, copy};
|
use fs_extra::dir::{CopyOptions, copy};
|
||||||
@@ -6,24 +7,26 @@ use keyring::Entry;
|
|||||||
use ratatui::crossterm::event;
|
use ratatui::crossterm::event;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use rhai::{AST, Dynamic, Engine, Scope};
|
use rhai::{AST, Dynamic, Engine, Scope};
|
||||||
|
use rust_tools::{BusterTarget, rustbuster};
|
||||||
use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned, pki_types::ServerName};
|
use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned, pki_types::ServerName};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
use std::fs::{self, File, create_dir_all, read_dir, read_to_string, remove_dir, remove_dir_all};
|
use std::fs::{self, File, create_dir_all, read_dir, read_to_string, remove_dir, remove_dir_all};
|
||||||
use std::io::{self, BufRead, BufReader, Read, Write};
|
use std::io::{self, BufRead, BufReader, Read, Write};
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
use std::net::SocketAddr;
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
use std::sync::RwLock;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel};
|
use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::{self, Instant};
|
use std::time::{self, Instant};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::rust_tools::rustwitness;
|
|
||||||
|
|
||||||
pub mod funcs;
|
pub mod funcs;
|
||||||
pub mod rust_tools;
|
pub mod rust_tools;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
@@ -99,6 +102,7 @@ pub struct AppState {
|
|||||||
pub remoting: bool,
|
pub remoting: bool,
|
||||||
pub help: Vec<String>,
|
pub help: Vec<String>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub progress_bars: Vec<Arc<RwLock<ToolProgress>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
@@ -138,6 +142,7 @@ impl AppState {
|
|||||||
remoting: false,
|
remoting: false,
|
||||||
help: Vec::new(),
|
help: Vec::new(),
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
|
progress_bars: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +256,10 @@ impl AppState {
|
|||||||
self.help
|
self.help
|
||||||
.push("stop_db\nrestart this project's distrobox".to_string());
|
.push("stop_db\nrestart this project's distrobox".to_string());
|
||||||
self.help.push("rustwitness\nRun a scan on the hosts in the urls.txt file in yoru project files directory that captures screenshots of those urls, optionally through a proxy.".to_string());
|
self.help.push("rustwitness\nRun a scan on the hosts in the urls.txt file in yoru project files directory that captures screenshots of those urls, optionally through a proxy.".to_string());
|
||||||
|
self.help.push("portscan\nRun a simple TCP portscan on either the scope for the project, or a given list of ips.\nportscan ip,ip,ip,ip port,port,port,port or portscan port,port,port".to_string());
|
||||||
|
self.help.push("rustbuster\nRun the Subdomain/Subdirectory bruteforcetool RustBuster (included in tetanus now!) against a given domain name or url.\nrustbuster target,target,target subs=/path/to/subwordlist dirs=/path/to/dirwordlist".to_string());
|
||||||
|
self.help
|
||||||
|
.push("clear_operations\nClear the progress section of the tool.".to_string());
|
||||||
self.help.push("exit\nquit the tool\n".to_string());
|
self.help.push("exit\nquit the tool\n".to_string());
|
||||||
self.initialize_modules();
|
self.initialize_modules();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -1077,6 +1086,271 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"portscan" => {
|
||||||
|
if let Some(project) = self.projects.get(self.selected_project) {
|
||||||
|
let mut ports = Vec::new();
|
||||||
|
let mut hosts = project.hosts.clone();
|
||||||
|
if let Some(args) = command_args {
|
||||||
|
let args_vec: Vec<&str> = args.split_whitespace().collect();
|
||||||
|
match args_vec.len() {
|
||||||
|
1 => {
|
||||||
|
args_vec[0].split(",").into_iter().for_each(|p| {
|
||||||
|
if let Ok(port) = p.trim().parse::<u16>() {
|
||||||
|
ports.push(port);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
args_vec[0].split(",").into_iter().for_each(|ip| {
|
||||||
|
if let Ok(ip) = ip.parse::<IpAddr>() {
|
||||||
|
let mut new_host = Host::new();
|
||||||
|
new_host.ip = ip.to_string();
|
||||||
|
hosts.push(new_host);
|
||||||
|
} else if let Ok(net) = ip.parse::<IpNet>() {
|
||||||
|
for host_addr in net.hosts() {
|
||||||
|
let mut new_host = Host::new();
|
||||||
|
new_host.ip = host_addr.to_string();
|
||||||
|
hosts.push(new_host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
args_vec[1].split(",").into_iter().for_each(|p| {
|
||||||
|
if p.contains("-") {
|
||||||
|
if let Some((start, end)) = p.split_once("-") {
|
||||||
|
if let Ok(start) = start.parse::<u16>() {
|
||||||
|
if let Ok(end) = end.parse::<u16>() {
|
||||||
|
for range in [start..end] {
|
||||||
|
range.into_iter().for_each(|port| {
|
||||||
|
ports.push(port.clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Ok(port) = p.trim().parse::<u16>() {
|
||||||
|
ports.push(port);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if !ports.is_empty() && !hosts.is_empty() {
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
format!("scanning {} ports on {} hosts", ports.len(), hosts.len()),
|
||||||
|
)));
|
||||||
|
let project_clone = Arc::new(Mutex::new(project.clone()));
|
||||||
|
let id = self.selected_project.clone();
|
||||||
|
let tx_clone = self.main_tx.clone();
|
||||||
|
self.workers.spawn(move || {
|
||||||
|
hosts.par_iter_mut().for_each(|h| {
|
||||||
|
ports.par_iter().for_each(|p| {
|
||||||
|
let mut host = h.clone();
|
||||||
|
match host.check_port(p.clone(), tx_clone.clone()) {
|
||||||
|
Ok(_) => {
|
||||||
|
if let Ok(mut lock) = project_clone.lock() {
|
||||||
|
lock.add_host(host.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if let Ok(lock) = project_clone.lock() {
|
||||||
|
let _ =
|
||||||
|
tx_clone.send(ToolMessage::UpdateProject(id, lock.clone()));
|
||||||
|
let _ = tx_clone.send(ToolMessage::Output((
|
||||||
|
0,
|
||||||
|
"[success] portscan finished!".to_string(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"rustbuster" => {
|
||||||
|
if let Some(args) = command_args {
|
||||||
|
let args: Vec<&str> = args.split(" ").collect();
|
||||||
|
let mut dirs = Vec::new();
|
||||||
|
let mut subs = Vec::new();
|
||||||
|
let mut targets = Vec::new();
|
||||||
|
if args
|
||||||
|
.iter()
|
||||||
|
.any(|a| a.contains("subs=") || a.contains("dirs="))
|
||||||
|
{
|
||||||
|
args.iter().for_each(|arg| {
|
||||||
|
if arg.contains("subs=") {
|
||||||
|
if let Some((_, sub_path)) = arg.split_once("=") {
|
||||||
|
if let Ok(subtext) = read_to_string(sub_path) {
|
||||||
|
subtext.lines().into_iter().for_each(|sub| {
|
||||||
|
subs.push(sub.to_string());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if arg.contains("dirs=") {
|
||||||
|
if let Some((_, dir_path)) = arg.split_once("=") {
|
||||||
|
if let Ok(dir_text) = read_to_string(dir_path) {
|
||||||
|
dir_text.lines().into_iter().for_each(|dir| {
|
||||||
|
dirs.push(dir.to_string());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if arg.contains(",") {
|
||||||
|
arg.split(",").into_iter().for_each(|t| {
|
||||||
|
targets.push(t.to_string());
|
||||||
|
});
|
||||||
|
} else if !arg.contains("=") {
|
||||||
|
targets.push(arg.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut num_dir = 0;
|
||||||
|
let mut num_sub = 0;
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
format!("{} targets found scanning...", num_dir + num_sub),
|
||||||
|
)));
|
||||||
|
let tx_clone = self.main_tx.clone();
|
||||||
|
let out_path = self.projects[self.selected_project].files.clone();
|
||||||
|
let project_clone =
|
||||||
|
Arc::new(Mutex::new(self.projects[self.selected_project].clone()));
|
||||||
|
let mut buster_targets = Vec::new();
|
||||||
|
for target in targets {
|
||||||
|
if target.contains("://") {
|
||||||
|
for dir in &dirs {
|
||||||
|
num_dir += 1;
|
||||||
|
buster_targets
|
||||||
|
.push(BusterTarget::Dir(format!("{}/{}", target, dir)));
|
||||||
|
}
|
||||||
|
} else if target.contains(".") {
|
||||||
|
for sub in &subs {
|
||||||
|
num_sub += 1;
|
||||||
|
buster_targets
|
||||||
|
.push(BusterTarget::Sub(format!("{}.{}", sub, target)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let new_progress = Arc::new(RwLock::new(ToolProgress {
|
||||||
|
name: "rustbuster".to_string(),
|
||||||
|
total: num_dir + num_sub,
|
||||||
|
complete: 0,
|
||||||
|
}));
|
||||||
|
self.progress_bars.push(new_progress.clone());
|
||||||
|
self.workers.spawn(move || {
|
||||||
|
buster_targets.par_iter().for_each(|t| match t {
|
||||||
|
BusterTarget::Dir(_) => {
|
||||||
|
if let Some(res) = rustbuster(t.clone()) {
|
||||||
|
let mut url_path = out_path.clone();
|
||||||
|
url_path.push("urls.txt");
|
||||||
|
if let Ok(mut url_file) = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.append(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&url_path)
|
||||||
|
{
|
||||||
|
if let Err(e) = writeln!(
|
||||||
|
url_file,
|
||||||
|
"{}",
|
||||||
|
format!("[success] {}", res)
|
||||||
|
) {
|
||||||
|
let _ = tx_clone.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
format!(
|
||||||
|
"[error] saving url to {}: {e}",
|
||||||
|
url_path.display()
|
||||||
|
),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = tx_clone.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
format!("[success] {}", res),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if let Ok(mut lock) = new_progress.write() {
|
||||||
|
lock.complete += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BusterTarget::Sub(_) => {
|
||||||
|
if let Some(res) = rustbuster(t.clone()) {
|
||||||
|
let mut url_path = out_path.clone();
|
||||||
|
url_path.push("domains.txt");
|
||||||
|
if let Ok(mut url_file) = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.append(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&url_path)
|
||||||
|
{
|
||||||
|
if let Err(e) =
|
||||||
|
writeln!(url_file, "{}", format!("{}", res))
|
||||||
|
{
|
||||||
|
let _ = tx_clone.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
format!(
|
||||||
|
"[error] saving domain to {}: {e}",
|
||||||
|
url_path.display()
|
||||||
|
),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = tx_clone.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
format!("[success] {}", res),
|
||||||
|
)));
|
||||||
|
if let Some((domain, ip)) = res.split_once(":") {
|
||||||
|
let mut new_host = Host::new();
|
||||||
|
new_host.ip = ip.to_string();
|
||||||
|
new_host.hostname = domain.to_string();
|
||||||
|
if let Ok(mut project_lock) = project_clone.lock() {
|
||||||
|
project_lock.add_host(new_host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(mut lock) = new_progress.write() {
|
||||||
|
lock.complete += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
drop(project_clone);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
"[error] no sub or dir wordlist provided!".to_string(),
|
||||||
|
)));
|
||||||
|
let _ = self
|
||||||
|
.main_tx
|
||||||
|
.send(ToolMessage::Output((rid, "USAGE:".to_string())));
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
"rustbuster target,target,target subs=/path/to/subwordlist dirs=/path/to/dirwordlist".to_string(),
|
||||||
|
)));
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
"be sure to include the subs= or dirs= part of the command above!"
|
||||||
|
.to_string(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
"[error] no arguments provided!".to_string(),
|
||||||
|
)));
|
||||||
|
let _ = self
|
||||||
|
.main_tx
|
||||||
|
.send(ToolMessage::Output((rid, "USAGE:".to_string())));
|
||||||
|
let _ = self.main_tx.send(ToolMessage::Output((
|
||||||
|
rid,
|
||||||
|
"rustbuster target,target,target subs=/path/to/subwordlist dirs=/path/to/dirwordlist".to_string(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"clear_operations" => {
|
||||||
|
self.progress_bars.clear();
|
||||||
|
}
|
||||||
"exit" => {
|
"exit" => {
|
||||||
self.save_all()?;
|
self.save_all()?;
|
||||||
let _ = self.main_tx.send(ToolMessage::AppStateExit);
|
let _ = self.main_tx.send(ToolMessage::AppStateExit);
|
||||||
@@ -1382,9 +1656,9 @@ impl AppState {
|
|||||||
let return_string = String::from("[success] Project promoted!");
|
let return_string = String::from("[success] Project promoted!");
|
||||||
let mut new_project = self.projects[self.selected_project].clone();
|
let mut new_project = self.projects[self.selected_project].clone();
|
||||||
let new_files_path = PathBuf::from(self.config.get("current_files").unwrap())
|
let new_files_path = PathBuf::from(self.config.get("current_files").unwrap())
|
||||||
.join(format!("{}/{}", new_project.org_name, new_project.name));
|
.join(format!("{}", new_project.org_name));
|
||||||
let new_notes_path = PathBuf::from(self.config.get("current_notes").unwrap())
|
let new_notes_path = PathBuf::from(self.config.get("current_notes").unwrap())
|
||||||
.join(format!("{}/{}", new_project.org_name, new_project.name));
|
.join(format!("{}", new_project.org_name));
|
||||||
let mut options = CopyOptions::new();
|
let mut options = CopyOptions::new();
|
||||||
create_dir_all(&new_files_path)?;
|
create_dir_all(&new_files_path)?;
|
||||||
create_dir_all(&new_notes_path)?;
|
create_dir_all(&new_notes_path)?;
|
||||||
@@ -2505,6 +2779,26 @@ id: {}",
|
|||||||
host_file.sync_all()?;
|
host_file.sync_all()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_port(&mut self, port: u16, tx: Sender<ToolMessage>) -> Result<(), Box<dyn Error>> {
|
||||||
|
if let Ok(ip) = self.ip.parse::<IpAddr>() {
|
||||||
|
let address = SocketAddr::new(ip, port);
|
||||||
|
if let Ok(_) = TcpStream::connect_timeout(&address, Duration::from_secs(3)) {
|
||||||
|
self.add_port(&format!("{}", port));
|
||||||
|
let _ = tx.send(ToolMessage::Output((
|
||||||
|
0,
|
||||||
|
format!("[success] {} is open on {}", port, self.ip),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let _ = tx.send(ToolMessage::Output((
|
||||||
|
0,
|
||||||
|
format!("[error] {} is not a valid ip address!", self.ip),
|
||||||
|
)));
|
||||||
|
return Err("not valid IP".into());
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -3013,3 +3307,9 @@ struct PromptQuery {
|
|||||||
id: usize,
|
id: usize,
|
||||||
query: String,
|
query: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ToolProgress {
|
||||||
|
name: String,
|
||||||
|
total: usize,
|
||||||
|
complete: usize,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use dns_lookup::lookup_host;
|
||||||
use headless_chrome::{Browser, LaunchOptions};
|
use headless_chrome::{Browser, LaunchOptions};
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use trust_dns_resolver::lookup::Ipv4Lookup;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum BusterTarget {
|
||||||
|
Sub(String),
|
||||||
|
Dir(String),
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rustwitness(
|
pub fn rustwitness(
|
||||||
input: String,
|
input: String,
|
||||||
@@ -61,3 +71,85 @@ pub fn rustwitness(
|
|||||||
capture_screenshot(&input, proxy.clone(), output.clone())?;
|
capture_screenshot(&input, proxy.clone(), output.clone())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rustbuster(target: BusterTarget) -> Option<String> {
|
||||||
|
match target {
|
||||||
|
BusterTarget::Dir(url) => {
|
||||||
|
if let Ok(resp) = reqwest::blocking::get(&url) {
|
||||||
|
let status = resp.status();
|
||||||
|
match status {
|
||||||
|
StatusCode::OK => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::ACCEPTED => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::CONTINUE => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::CREATED => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::FOUND => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::IM_A_TEAPOT => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::MOVED_PERMANENTLY => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::PERMANENT_REDIRECT => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
StatusCode::TEMPORARY_REDIRECT => {
|
||||||
|
return Some(format!("{}", url));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BusterTarget::Sub(name) => {
|
||||||
|
let mut base = String::new();
|
||||||
|
if let Some((given_name, tld)) = name.rsplit_once(".") {
|
||||||
|
if let Some((_, base_name)) = name.rsplit_once(".") {
|
||||||
|
base = format!("{}.{}", base_name, tld);
|
||||||
|
} else {
|
||||||
|
base = format!("{}.{}", given_name, tld);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut wildcards = Vec::new();
|
||||||
|
if base.len() > 0 {
|
||||||
|
if let Ok(ips) = lookup_host(&format!("burstpyrofoo.{}", base)) {
|
||||||
|
ips.into_iter().for_each(|ip| {
|
||||||
|
wildcards.push(ip);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(mut ips) = lookup_host(&name) {
|
||||||
|
let mut found_ips = Vec::new();
|
||||||
|
let not_wild = !ips.all(|ip| wildcards.contains(&ip));
|
||||||
|
if not_wild {
|
||||||
|
ips.for_each(|ip| {
|
||||||
|
found_ips.push(ip.to_string());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if found_ips.len() > 0 {
|
||||||
|
let mut out = String::new();
|
||||||
|
found_ips.iter().for_each(|ip| {
|
||||||
|
out.push_str(&format!("{}:{}\n", name, ip));
|
||||||
|
});
|
||||||
|
return Some(out);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user