From 06ccdb4a7e7b84c084d3abf77c8ebf233e7dfa80 Mon Sep 17 00:00:00 2001 From: David-Tillmann Schaefer Date: Fri, 14 Nov 2025 07:28:56 +0100 Subject: [PATCH 1/4] fix: apply cargo fmt --- crates/cli-interface/src/lib.rs | 5 ++++- crates/cracker/src/lib.rs | 17 ++++++----------- crates/engine/src/lib.rs | 2 +- crates/producer/src/default_query.rs | 10 +++++----- 4 files changed, 16 insertions(+), 18 deletions(-) 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..fb158b2 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 ! (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) .collect(); char_set.sort(); - + Self { max_length, min_length, From 069ed6c9bbb158628427a7768e5daa11d4474467 Mon Sep 17 00:00:00 2001 From: David-Tillmann Schaefer Date: Thu, 13 Nov 2025 17:34:50 +0100 Subject: [PATCH 2/4] add space into characters as passwords can contain spaces --- crates/producer/src/default_query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/producer/src/default_query.rs b/crates/producer/src/default_query.rs index fb158b2..f11fbfa 100644 --- a/crates/producer/src/default_query.rs +++ b/crates/producer/src/default_query.rs @@ -13,7 +13,7 @@ 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 ' ' (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) From 18d0453f794d49ad520cb0246516b1cc8b1061a2 Mon Sep 17 00:00:00 2001 From: David-Tillmann Schaefer Date: Thu, 13 Nov 2025 17:34:25 +0100 Subject: [PATCH 3/4] update Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 277cfc15ef2386c559e0f79e01ba404582057575 Mon Sep 17 00:00:00 2001 From: David-Tillmann Schaefer Date: Fri, 14 Nov 2025 07:35:51 +0100 Subject: [PATCH 4/4] add unit test checking for spaces --- examples/encrypted_00 0_bbbb.pdf | Bin 0 -> 1095 bytes tests/default-query.rs | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 examples/encrypted_00 0_bbbb.pdf diff --git a/examples/encrypted_00 0_bbbb.pdf b/examples/encrypted_00 0_bbbb.pdf new file mode 100644 index 0000000000000000000000000000000000000000..461cbb702de7178248abd7ff57833b74aea09667 GIT binary patch literal 1095 zcmZWoO-NKx6qdk6*G-FBB!RbNNTcz-AKxDgL!-`^r71J1gh||)cV~Py^PatTXV4Pb z*rF)N1Q$`zsx~2%7F4)NC|o7frVRQKYS$kvY}dVSobfSU9*_6V{m%K$Io~;&Bb6K!gHx(&GR`A^@G9njjE$Vv6%l4xj;?BOa6(A_<~VKy1@TazoYSq-A<= zMCO>$N&w#vpiZY~Qz!;q+k>tNhh6);V=m1I*&ndbY zmM|wt^6XuP-bx=O9f~P|0ziGlv~Y(r2}cA5R7DADhBO+?l4nw7O%>DBZwqgZSSgX` z4p0a7h+hRd&MF|(%3y%vcE&L+I|tFQWyfsKsu+S4yn-r4FR^oU3>qp&c+@4h0N~7- zoAAwE4N7N+{1_1YMD;zClAI&~ldwE0qbG3oIpa153{#n1o)htuNs<2ux zseahfpdH>@Dx^Z=&DYnvQ{4xC44%I)FTU-31S1ZRjR0Rk3NWE@zjvS%VrNsLKY}R3kk^HEDS&iD^H2bf3a<1 z*92$~w){Tpt~Iv*@|K2fFQ|74!)K!-_}WPt#6AXE=j6-8EH z7FKN{n+JZNy=_yOfHgKzW7Aq|lXm(_2HUwB9P8l@n<5+AHtJ$4Pu%jFvVn4^CMe{3 zu|el&@s_6I+nA#APK6jDp*Y`*d!7*RS8EUN;a)zO+!~jc9#}pQ&CmL6cs_z mv!*7?QMSKH$6*^=4x6$r64n`iB(Y2VR%yBjLZNtn0{jOI$u@rg literal 0 HcmV?d00001 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." + ) +}