3 Commits

3 changed files with 49 additions and 27 deletions
Generated
+1 -1
View File
@@ -875,7 +875,7 @@ checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7"
[[package]] [[package]]
name = "rustbuster" name = "rustbuster"
version = "0.1.0" version = "2.0.0"
dependencies = [ dependencies = [
"clap", "clap",
"dns-lookup", "dns-lookup",
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "rustbuster" name = "rustbuster"
version = "0.1.0" version = "2.0.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -9,7 +9,7 @@ edition = "2021"
clap = { version = "4.5.4", features = ["derive"] } clap = { version = "4.5.4", features = ["derive"] }
dns-lookup = "2.0.4" dns-lookup = "2.0.4"
futures = "0.3.32" futures = "0.3.32"
indicatif = "0.18.4"
reqwest = { version = "0.12.4", features = ["blocking"] } reqwest = { version = "0.12.4", features = ["blocking"] }
tokio = { version = "1.52.3", features = ["full"] } tokio = { version = "1.52.3", features = ["full"] }
trust-dns-resolver = "0.23.2" trust-dns-resolver = "0.23.2"
urlencoding = "2.1.3"
+45 -23
View File
@@ -1,5 +1,7 @@
use clap::Parser; use clap::Parser;
use futures::{stream, StreamExt}; use futures::{stream, StreamExt};
use indicatif::ProgressBar;
use indicatif::ProgressStyle;
use reqwest::{self, StatusCode}; use reqwest::{self, StatusCode};
use std::fs::File; use std::fs::File;
use std::io::BufWriter; use std::io::BufWriter;
@@ -10,7 +12,6 @@ use std::sync::Arc;
use std::{collections::HashMap, fs}; use std::{collections::HashMap, fs};
use tokio::sync::mpsc::{channel, Sender}; use tokio::sync::mpsc::{channel, Sender};
use trust_dns_resolver::{config::*, TokioAsyncResolver}; use trust_dns_resolver::{config::*, TokioAsyncResolver};
use urlencoding::encode;
enum OutputMessage { enum OutputMessage {
UrlResult(String), UrlResult(String),
@@ -44,7 +45,7 @@ struct Args {
///input file full of domains formatted to one domain per line. ///input file full of domains formatted to one domain per line.
#[arg(short, long)] #[arg(short, long)]
domainfile: Option<PathBuf>, indomainfile: Option<PathBuf>,
} }
async fn try_sub( async fn try_sub(
@@ -53,6 +54,7 @@ async fn try_sub(
tx: Sender<OutputMessage>, tx: Sender<OutputMessage>,
output: bool, output: bool,
resolver: Arc<TokioAsyncResolver>, resolver: Arc<TokioAsyncResolver>,
pb: Arc<ProgressBar>,
) { ) {
let mut ips = Vec::new(); let mut ips = Vec::new();
if let Ok(ipv4s) = resolver.ipv4_lookup(&domain).await { if let Ok(ipv4s) = resolver.ipv4_lookup(&domain).await {
@@ -93,25 +95,26 @@ async fn try_sub(
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", domain, ips.join(", ")); pb.println(&format!("{} {}", domain, ips.join(", ")));
} }
pb.inc(1);
} }
async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) { async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool, pb: Arc<ProgressBar>) {
let resp_stat = reqwest::get(&encode(&url).to_string()).await; let resp_stat = reqwest::get(&url).await;
if resp_stat.is_ok() { if resp_stat.is_ok() {
let resp = resp_stat.unwrap().status(); let resp = resp_stat.unwrap().status();
match resp { match resp {
StatusCode::OK => { StatusCode::OK => {
if output { if output {
if let Err(e) = tx if let Err(e) = tx
.send(OutputMessage::UrlResult(format!("{} {}", resp, url))) .send(OutputMessage::UrlResult(format!("{} {}", resp, &url)))
.await .await
{ {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, &url));
} }
StatusCode::ACCEPTED => { StatusCode::ACCEPTED => {
if output { if output {
@@ -122,7 +125,7 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, url));
} }
StatusCode::CONTINUE => { StatusCode::CONTINUE => {
if output { if output {
@@ -133,7 +136,7 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, url));
} }
StatusCode::CREATED => { StatusCode::CREATED => {
if output { if output {
@@ -144,7 +147,7 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, url));
} }
StatusCode::FOUND => { StatusCode::FOUND => {
if output { if output {
@@ -155,7 +158,7 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, url));
} }
StatusCode::IM_A_TEAPOT => { StatusCode::IM_A_TEAPOT => {
if output { if output {
@@ -166,7 +169,10 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("And what a beautiful teapot you are {}", url); pb.println(&format!(
"{} {} - and what a beautiful teapot you are!",
resp, url
));
} }
StatusCode::MOVED_PERMANENTLY => { StatusCode::MOVED_PERMANENTLY => {
if output { if output {
@@ -177,7 +183,7 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, url));
} }
StatusCode::PERMANENT_REDIRECT => { StatusCode::PERMANENT_REDIRECT => {
if output { if output {
@@ -188,7 +194,7 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, url));
} }
StatusCode::TEMPORARY_REDIRECT => { StatusCode::TEMPORARY_REDIRECT => {
if output { if output {
@@ -199,11 +205,12 @@ async fn try_dir(url: String, tx: Sender<OutputMessage>, output: bool) {
eprintln!("error sending output! {}", e); eprintln!("error sending output! {}", e);
} }
} }
println!("{} {}", resp, url); pb.println(&format!("{} {}", resp, url));
} }
_ => (), _ => (),
} }
} }
pb.inc(1);
} }
#[tokio::main] #[tokio::main]
@@ -298,7 +305,7 @@ async fn main() {
}); });
} }
} }
if let Some(domain_file) = args.domainfile { if let Some(domain_file) = args.indomainfile {
if let Ok(domain_string) = fs::read_to_string(domain_file) { if let Ok(domain_string) = fs::read_to_string(domain_file) {
domain_string.lines().into_iter().for_each(|domain| { domain_string.lines().into_iter().for_each(|domain| {
targets.push(domain.to_string()); targets.push(domain.to_string());
@@ -349,11 +356,15 @@ async fn main() {
for dir in dirwordlist.split("\n").collect::<Vec<&str>>() { for dir in dirwordlist.split("\n").collect::<Vec<&str>>() {
for target in &targets { for target in &targets {
if target.contains("http") { if target.contains("http") {
if dir.starts_with("/") || target.ends_with("/") {
dirs_to_try.push(format!("{}{}", target, dir.trim()));
} else {
dirs_to_try.push(format!("{}/{}", target, dir.trim())); dirs_to_try.push(format!("{}/{}", target, dir.trim()));
} }
} }
} }
} }
}
if args.subwordlist != String::from("none") { if args.subwordlist != String::from("none") {
let subwordlist = let subwordlist =
fs::read_to_string(args.subwordlist).expect("error reading subdomain word list"); fs::read_to_string(args.subwordlist).expect("error reading subdomain word list");
@@ -366,8 +377,8 @@ async fn main() {
} }
} }
println!("INFO:::loading targets, and wordlists...:::"); println!("INFO:::loading targets, and wordlists...:::");
let urls_len = dirs_to_try.len(); let urls_len = dirs_to_try.len() as u64;
let domains_len = subs_to_try.len(); let domains_len = subs_to_try.len() as u64;
println!( println!(
"INFO:::DONE!, {} URLS to try, and {} subdomains to try:::", "INFO:::DONE!, {} URLS to try, and {} subdomains to try:::",
&urls_len, &domains_len &urls_len, &domains_len
@@ -432,13 +443,22 @@ async fn main() {
println!("INFO:::results will be printed to console and not saved...:::"); println!("INFO:::results will be printed to console and not saved...:::");
} }
}); });
let total = urls_len + domains_len;
let pb = Arc::new(ProgressBar::new(total));
pb.set_style(
ProgressStyle::with_template("[{elapsed_precise}] {bar:40.orange/red} {pos}/{len} ({eta})")
.unwrap(),
);
let resolver = TokioAsyncResolver::tokio(ResolverConfig::default(), opts); let resolver = TokioAsyncResolver::tokio(ResolverConfig::default(), opts);
let resolver = Arc::new(resolver); let resolver = Arc::new(resolver);
pb.println("starting...");
pb.force_draw();
let dir_stream = stream::iter(dirs_to_try.into_iter().map(|url| { let dir_stream = stream::iter(dirs_to_try.into_iter().map(|url| {
let pb = pb.clone();
let tx = tx.clone(); let tx = tx.clone();
let output = output.clone(); let output = output.clone();
async move { async move {
try_dir(url, tx, output).await; try_dir(url, tx, output, pb).await;
} }
})); }));
let sub_stream = stream::iter(subs_to_try.into_iter().map(|sub| { let sub_stream = stream::iter(subs_to_try.into_iter().map(|sub| {
@@ -446,8 +466,9 @@ async fn main() {
let output = output.clone(); let output = output.clone();
let wc = wild_card_results.clone(); let wc = wild_card_results.clone();
let resolver = resolver.clone(); let resolver = resolver.clone();
let pb = pb.clone();
async move { async move {
try_sub(sub, wc, tx, output, resolver.clone()).await; try_sub(sub, wc, tx, output, resolver.clone(), pb).await;
} }
})); }));
@@ -461,11 +482,12 @@ async fn main() {
.buffer_unordered(concurrency) .buffer_unordered(concurrency)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.await; .await;
println!("INFO:::enumeration finished!:::"); pb.println("INFO:::enumeration finished!:::");
if output { if output {
println!("INFO:::waiting for output writer to finish...:::"); pb.println("INFO:::waiting for output writer to finish...:::");
tx.send(OutputMessage::Shutdown).await.unwrap(); tx.send(OutputMessage::Shutdown).await.unwrap();
} }
write_handler.await.unwrap(); write_handler.await.unwrap();
println!("INFO:::done bruteforcing, happy hunting!:::"); pb.println("INFO:::done bruteforcing, happy hunting!:::");
pb.finish_with_message("Done!");
} }