use std::str::from_utf8;
use async_trait::async_trait;
use colorgrad::Gradient;
use fake::{Fake, faker::name::raw::FirstName, locales::EN};
use rand::rngs::ThreadRng;
use rand::{Rng, rng};
use sha2::{Digest, Sha256};
use yansi::Paint;
use crate::args::AppConfig;
use crate::generators::gen_hex_string;
use crate::io::{csleep, cursor_up, newline, print};
use crate::modules::Module;
pub struct Bruteforce;
#[async_trait(?Send)]
impl Module for Bruteforce {
fn name(&self) -> &'static str {
"bruteforce"
}
fn signature(&self) -> String {
"./bruteforce.sh ./hashes.txt".to_string()
}
async fn run(&self, app_config: &AppConfig) {
let mut rng = rng();
let n_parallel = rng.random_range(2..10);
let pass_hash_pairs: Vec<_> = std::iter::repeat_with(|| gen_pass_and_hash(&mut rng))
.take(n_parallel)
.collect();
print("=> Hashes to decrypt").await;
newline().await;
csleep(500).await;
for (_, hash) in &pass_hash_pairs {
print(format!(" {hash}")).await;
newline().await;
}
csleep(500).await;
{
let message = "=> Extracting Rainbow Table";
let width = 30;
let millis_wait = 2500;
let fill_char = "=";
let mut spinner_statuses = ["|", "/", "-", "\\"].iter().cycle();
for i in 0..width {
let spinner = if i == (width - 1) {
fill_char
} else {
spinner_statuses.next().unwrap()
};
let progress = fill_char.repeat(i);
let spaces = " ".repeat(width - i - 1);
let progress_content = rainbow(&format!("{progress}{spinner}{spaces}"));
print(format!("\r{message} [{progress_content}]",)).await;
csleep((millis_wait / width) as u64).await;
if app_config.should_exit() {
return;
}
}
newline().await;
}
print("=> Begin matching").await;
newline().await;
csleep(500).await;
{
let mut guessers: Vec<_> = pass_hash_pairs
.iter()
.map(|(_, hash)| HashGuesser::new(hash))
.collect();
let mut first = true;
while !guessers.iter().all(|g| g.completed()) {
if first {
first = false;
} else {
cursor_up(n_parallel as u64).await;
}
for (i, a_guesser) in guessers.iter_mut().enumerate() {
a_guesser.tick_guess();
print(format!("\r :: {a_guesser} ::")).await;
if i != n_parallel {
newline().await;
}
}
csleep(10).await;
if app_config.should_exit() {
return;
}
}
}
print("=> Match found").await;
newline().await;
for (pass, hash) in pass_hash_pairs {
print(format!(" {hash}:{}", Paint::new(pass).bold())).await;
newline().await;
}
}
}
fn sha256(s: &str) -> String {
let mut hasher = Sha256::default();
hasher.update(s);
let result_bytes = hasher.finalize();
format!("{result_bytes:x}")
}
fn rainbow(s: &str) -> String {
use std::fmt::Write;
let len = s.len();
let colors = colorgrad::preset::sinebow().colors(len);
let mut ret = String::new();
for (color, ch) in colors.into_iter().zip(s.chars()) {
let approx = approx_color(color);
write!(ret, "{}", Paint::new(ch).fg(approx)).unwrap();
}
ret
}
fn approx_color(c: colorgrad::Color) -> yansi::Color {
let r = (c.r * 5.).round() as u8;
let g = (c.g * 5.).round() as u8;
let b = (c.b * 5.).round() as u8;
yansi::Color::Fixed(16 + 36 * r + 6 * g + b)
}
fn gen_pass_and_hash<T: Rng>(rng: &mut T) -> (String, String) {
let pass = format!(
"{}{:02}",
FirstName(EN).fake::<&str>().to_lowercase(),
rng.random_range(0..99)
);
let hash = sha256(&pass);
(pass, hash)
}
struct HashGuesser {
hash: Vec<u8>,
guesses: Vec<u8>,
progress: usize,
len: usize,
rng: ThreadRng,
}
impl HashGuesser {
fn new(hash: &str) -> Self {
Self {
hash: hash.bytes().collect(),
guesses: vec![], progress: 0,
len: hash.len(),
rng: rng(),
}
}
fn tick_guess(&mut self) {
if !self.completed() {
self.guesses = gen_hex_string(&mut self.rng, self.len as u64)
.bytes()
.collect();
while !self.completed() && self.guesses[self.progress] == self.hash[self.progress] {
self.progress += 1;
}
}
}
fn completed(&self) -> bool {
self.progress == self.len
}
}
impl std::fmt::Display for HashGuesser {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let progress = self.progress;
let (done, undone) = if progress < self.len {
let done = from_utf8(&self.hash[0..progress]).unwrap();
let undone = from_utf8(&self.guesses[progress..]).unwrap();
(done, undone)
} else {
(from_utf8(&self.hash).unwrap(), "")
};
let (done, undone) = (Paint::green(done), Paint::red(undone));
write!(f, "{done}{undone}")
}
}