diff --git a/README.md b/README.md index e56bd48..b45d29e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ - **Custom Query Builder:** You can write your own queries like `STRING{69-420}` which would generate and use a wordlist with the full number range. - **Date Bruteforce:** You can pass in a year which would bruteforce all 365 days of the year in `DDMMYYYY` format which is a pretty commonly used password format for PDFs. - **Number Bruteforce:** Just give a number range like `5000-100000` and it would bruteforce with the whole range. -- **Default Bruteforce:** Specify a maximum and optionally a minimum length for the password search and all passwords of length 4 up to the specified maximum consisting of letters and numbers (`a-zA-Z0-9`) will be tried +- **Default Bruteforce:** Specify a maximum and optionally a minimum length for the password search and all passwords of length 4 up to the specified maximum consisting of letters, numbers (`a-zA-Z0-9`) and other typical ASCII characters (`!#$%&()*+,-./:;<=>?@[\]^_{|}~`), space (` `) and quotes will be tried. ## Installation diff --git a/crates/cli-interface/src/lib.rs b/crates/cli-interface/src/lib.rs index ad679ed..ed975c0 100644 --- a/crates/cli-interface/src/lib.rs +++ b/crates/cli-interface/src/lib.rs @@ -93,7 +93,10 @@ pub fn entrypoint(args: Arguments) -> Result { match res { Some(password) => match std::str::from_utf8(&password) { Ok(password) => { - info!("Success! Found password, displaying as UTF-8: '{}'", password) + info!( + "Success! Found password, displaying as UTF-8: '{}'", + password + ) } Err(_) => { let hex_string: String = password diff --git a/crates/cracker/src/lib.rs b/crates/cracker/src/lib.rs index 9a79fb4..22893a9 100644 --- a/crates/cracker/src/lib.rs +++ b/crates/cracker/src/lib.rs @@ -1,11 +1,11 @@ -use std::{fs, io, cell::RefCell, sync::Arc}; use std::collections::hash_map::HashMap; +use std::{cell::RefCell, fs, io, sync::Arc}; use anyhow::anyhow; -use pdf::PdfError; use pdf::any::AnySync; use pdf::file::{Cache, NoLog, Storage}; use pdf::object::{ParseOptions, PlainRef}; +use pdf::PdfError; #[derive(Clone)] pub struct PDFCracker(Vec); @@ -20,9 +20,7 @@ impl PDFCracker { type ObjectCache = SimpleCache>>; type StreamCache = SimpleCache, Arc>>; -pub struct PDFCrackerState( - Storage, ObjectCache, StreamCache, NoLog> -); +pub struct PDFCrackerState(Storage, ObjectCache, StreamCache, NoLog>); impl PDFCrackerState { /// Init `pdf::file::Storage` so we can reuse it on each `attempt`. @@ -32,7 +30,7 @@ impl PDFCrackerState { ParseOptions::strict(), SimpleCache::new(), SimpleCache::new(), - NoLog + NoLog, ); match res { @@ -43,15 +41,12 @@ impl PDFCrackerState { /// Attempt to crack the cryptography using the password, return true on success. pub fn attempt(&mut self, password: &[u8]) -> bool { - self.0.load_storage_and_trailer_password(password) - .is_ok() + self.0.load_storage_and_trailer_password(password).is_ok() } } /// Single-threaded adapter to use `HashMap` as a `pdf::file::Cache`. -struct SimpleCache( - RefCell> -); +struct SimpleCache(RefCell>); impl SimpleCache { fn new() -> Self { diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 522bca3..11140d3 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -47,7 +47,7 @@ pub fn crack_file( let c2 = cracker_handle.clone(); let id: std::thread::JoinHandle<()> = std::thread::spawn(move || { let Ok(mut cracker) = PDFCrackerState::from_cracker(&c2) else { - return + return; }; while let Ok(passwd) = r2.recv() { diff --git a/crates/producer/src/default_query.rs b/crates/producer/src/default_query.rs index 3bd8b8c..f11fbfa 100644 --- a/crates/producer/src/default_query.rs +++ b/crates/producer/src/default_query.rs @@ -13,14 +13,14 @@ impl DefaultQuery { let mut char_set: Vec = (b'0'..=b'9') .chain(b'A'..=b'Z') .chain(b'a'..=b'z') - .chain(b'!'..=b'/') // Adding special characters from ASCII range ! (33) to / (47) - .chain(b':'..=b'@') // Adding special characters from ASCII range : (58) to @ (64) - .chain(b'['..=b'`') // Adding special characters from ASCII range [ (91) to ` (96) - .chain(b'{'..=b'~') // Adding special characters from ASCII range { (123) to ~ (126) + .chain(b' '..=b'/') // Adding special characters from ASCII range ' ' (32) to / (47) + .chain(b':'..=b'@') // Adding special characters from ASCII range : (58) to @ (64) + .chain(b'['..=b'`') // Adding special characters from ASCII range [ (91) to ` (96) + .chain(b'{'..=b'~') // Adding special characters from ASCII range { (123) to ~ (126) .collect(); char_set.sort(); - + Self { max_length, min_length, diff --git a/examples/encrypted_00 0_bbbb.pdf b/examples/encrypted_00 0_bbbb.pdf new file mode 100644 index 0000000..461cbb7 Binary files /dev/null and b/examples/encrypted_00 0_bbbb.pdf differ diff --git a/tests/default-query.rs b/tests/default-query.rs index 4782c00..378173e 100644 --- a/tests/default-query.rs +++ b/tests/default-query.rs @@ -33,3 +33,23 @@ fn success_pdf2() { assert!(matches!(res, Code::Success), "Failed cracking file.") } + +#[test] +#[ignore = "This is slow"] +fn success_pdf_space_support() { + let args = arguments::Arguments { + number_of_threads: 4, + filename: "examples/encrypted_00 0_bbbb.pdf".to_string(), + subcommand: arguments::Method::DefaultQuery(arguments::DefaultQueryArgs { + min_length: 4, + max_length: 4, + }), + }; + + let res = entrypoint(args).expect("An error occured when cracking file"); + + assert!( + matches!(res, Code::Success), + "Failed to crack PDF with space-containing password. Expected to find '00 0' but cracking returned failure." + ) +}