Skip to content

Commit

Permalink
Add support for encrypted zip files
Browse files Browse the repository at this point in the history
  • Loading branch information
danielpclark committed Oct 11, 2017
1 parent 2b82d08 commit 9d2c27c
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "abrute"
version = "0.1.0"
version = "0.1.1"
authors = ["Daniel P. Clark <[email protected]>"]
description = "AESCrypt Brute force attempter."
documentation = "http:https://danielpclark.github.io/abrute/index.html"
Expand Down
108 changes: 84 additions & 24 deletions src/core.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::sync::Mutex;
use digits::Digits;
use std::io::{self, Write};
use std::process::Command;
use std::process::{Command, Output};
use rayon::prelude::*;
use super::result::Error;
extern crate num_cpus;

fn chunk_sequence(d: &mut Digits, qty: usize, adj: Option<&str>) -> Vec<String> {
fn chunk_sequence(d: &mut Digits, adj: Option<&str>) -> Vec<String> {
let qty: usize = num_cpus::get() * 32;
let mut counter = 0;
let mut result = vec![];
loop {
Expand All @@ -26,27 +27,56 @@ fn chunk_sequence(d: &mut Digits, qty: usize, adj: Option<&str>) -> Vec<String>
result
}

pub fn core_loop<'a>(max: usize, mut sequencer: Digits<'a>, target: &str, adj: Option<&str>) -> Result<(), Error> {
loop {
if sequencer.length() > max {
return Err(Error::PasswordNotFound);
}
fn aes_command(value: &str, target: &str) -> Output {
Command::new("aescrypt").
arg("-d").
arg("-p").
arg(value).
arg(target).
output().
unwrap()
}

fn unzip_command(value: &str, target: &str) -> Output {
Command::new("unzip").
arg("-u").
arg("-P").
arg(value).
arg(target).
output().
unwrap()
}

fn progress_report<'a>(sequencer: &Digits<'a>) {
print!("{}..", sequencer.to_s()); // Verbose
io::stdout().flush().unwrap();
}

fn has_reached_end<'a>(sequencer: &Digits<'a>, max: usize) -> Result<(), Error> {
if sequencer.length() > max {
return Err(Error::PasswordNotFound);
}

print!("{}..", sequencer.to_s()); // Verbose
io::stdout().flush().unwrap();
Ok(())
}

pub fn aescrypt_core_loop<'a>(
max: usize,
mut sequencer: Digits<'a>,
target: &str,
adj: Option<&str>
) -> Result<(), Error> {

loop {
has_reached_end(&sequencer, max)?;
progress_report(&sequencer);

let chunk = chunk_sequence(&mut sequencer, num_cpus::get() * 32, adj);
let chunk = chunk_sequence(&mut sequencer, adj);
let code: Mutex<Vec<String>> = Mutex::new(vec![]);

chunk.par_iter().for_each(|ref value|
{
let output = Command::new("aescrypt").
arg("-d").
arg("-p").
arg(&value).
arg(&target).
output().
unwrap();
let output = aes_command(&value, &target);

if output.status.success() {
let mut code_mutex = code.lock().unwrap();
Expand All @@ -63,15 +93,45 @@ pub fn core_loop<'a>(max: usize, mut sequencer: Digits<'a>, target: &str, adj: O
// answer and decrypt the source one last time. Otherwise we'd need to isolate
// every attempt in a temp dir or mem dir and copying that much data that many
// times would be very slow and difficult to implement in a threaded way.
Command::new("aescrypt").
arg("-d").
arg("-p").
arg(code.first().unwrap()).
arg(&target).
output().
unwrap();

aes_command(code.first().unwrap(), target);

break;
}
}

Ok(())
}

pub fn unzip_core_loop<'a>(
max: usize,
mut sequencer: Digits<'a>,
target: &str,
adj: Option<&str>
) -> Result<(), Error> {

loop {
has_reached_end(&sequencer, max)?;
progress_report(&sequencer);

let chunk = chunk_sequence(&mut sequencer, adj);
let code: Mutex<Vec<Result<(), Error>>> = Mutex::new(vec![]);

chunk.par_iter().for_each(|ref value|
{
let output = unzip_command(&value, &target);

if output.status.success() {
let mut code_mutex = code.lock().unwrap();
code_mutex.push(Ok(()));
println!("Success!\nPassword is: {}", value);
}
}
);

let mut code = code.lock().unwrap();
if !code.is_empty() {
return code.pop().unwrap();
}
}
}
53 changes: 19 additions & 34 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
extern crate digits;
extern crate rayon;
use digits::Digits;
use std::path::Path;
use std::io::{self, Write};
use std::process::{Command,Stdio};
mod result;
use result::Error;
use std::error::Error as StdError;
Expand Down Expand Up @@ -48,6 +46,11 @@ fn run_app() -> Result<(), Error> {
long("start").
takes_value(true)
).
arg(Arg::with_name("zip").
short("z").
long("zip").
takes_value(false)
).
arg(Arg::with_name("TARGET").
required(true).
last(true)
Expand All @@ -69,6 +72,7 @@ fn run_app() -> Result<(), Error> {
not allow any characters of the same kind to neighbor
in the attempts.
-s, --start Starting character sequence to begin with.
-z, --zip Use `unzip` decryption instead of `aescrypt`.
<TARGET> Target file to decrypt. The target must be preceeded
by a double dash: -- target.aes
-h, --help Prints help information.
Expand All @@ -78,50 +82,31 @@ fn run_app() -> Result<(), Error> {
USE OF THIS BINARY FALLS UNDER THE MIT LICENSE (c) 2017").
get_matches();

if Command::new("aescrypt").
stdout(Stdio::null()).
stderr(Stdio::null()).
spawn().
is_err() {
return Err(Error::AescryptMissing);
if matches.is_present("zip") {
validate_unzip_executable()?;
} else {
validate_aescrpyt_executable()?;
}

let (min, max) = derive_min_max(matches.value_of("RANGE").unwrap())?;
let mapping = derive_character_base(matches.value_of("CHARACTERS").unwrap());

if let Some(s) = matches.value_of("start") {
let _ = validate_string_length(s, max)?;

let chrctrs: Vec<char> = matches.value_of("CHARACTERS").unwrap().chars().collect();
let mut itr = s.chars();
loop {
match itr.next() {
Some(ref c) => {
if !chrctrs.contains(c) { return Err(Error::InvalidCharacterSet); }
},
_ => break,
}
}
}
validate_start_string(&matches, max)?;

let mapping = derive_character_base(matches.value_of("CHARACTERS").unwrap());
let mut sequencer = Digits::new(&mapping, matches.value_of("start").unwrap_or("").to_string());
sequencer.zero_fill(min as usize);

let target = matches.value_of("TARGET").unwrap_or("");
let adjacent = matches.value_of("adjacent");

let seq_base = sequencer.base();
if let &Some(num) = &adjacent {
validate_adjacent_input(num.to_string())?;
if seq_base > 3 {
sequencer.prep_non_adjacent(num.parse::<usize>().unwrap());
}
}
validate_and_prep_sequencer_adjacent(&mut sequencer, adjacent)?;
validate_file_exists(&target)?;

if !Path::new(&target).exists() {
return Err(Error::FileMissing)
if matches.is_present("zip") {
unzip_core_loop(max, sequencer, target, adjacent)
} else {
aescrypt_core_loop(max, sequencer, target, adjacent)
}

core_loop(max, sequencer, target, adjacent)
}

fn main() {
Expand Down
8 changes: 8 additions & 0 deletions src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum Error {
InvalidRange,
InvalidStringLength,
PasswordNotFound,
UnzipMissing,
}

impl fmt::Display for Error {
Expand All @@ -22,6 +23,7 @@ impl fmt::Display for Error {
Error::InvalidRange => f.write_str("InvalidRange" ),
Error::InvalidStringLength => f.write_str("InvalidStringLength" ),
Error::PasswordNotFound => f.write_str("PasswordNotFound" ),
Error::UnzipMissing => f.write_str("UnzipMissing" ),
}
}
}
Expand Down Expand Up @@ -61,6 +63,11 @@ fn password_not_found() -> &'static str {
"Password not found for given length and character set."
}

#[inline]
fn unzip_missing() -> &'static str {
"unzip does not appear to be installed."
}

impl StdError for Error {
fn description(&self) -> &str {
match *self {
Expand All @@ -71,6 +78,7 @@ impl StdError for Error {
Error::InvalidRange => invalid_range(),
Error::InvalidStringLength => invalid_string_length(),
Error::PasswordNotFound => password_not_found(),
Error::UnzipMissing => unzip_missing(),
}
}
}
74 changes: 71 additions & 3 deletions src/validators.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,79 @@
use std::process::{Command,Stdio};
use super::result::Error;
use std::path::Path;
use digits::Digits;
use clap;

pub fn validate_adjacent_input(v: String) -> Result<(), Error> {
if v.parse::<u8>().is_ok() { return Ok(()); }
Err(Error::InvalidAdjacentNumber)
}

pub fn validate_string_length(v: &str, max: usize) -> Result<(), Error> {
if v.len() <= max { return Ok(()); }
Err(Error::InvalidStringLength)
pub fn validate_start_string(matches: &clap::ArgMatches, max: usize) -> Result<(), Error> {
if let Some(s) = matches.value_of("start") {
if s.len() > max { return Err(Error::InvalidStringLength); }

let chrctrs: Vec<char> = matches.value_of("CHARACTERS").unwrap().chars().collect();
let mut itr = s.chars();
loop {
match itr.next() {
Some(ref c) => {
if !chrctrs.contains(c) { return Err(Error::InvalidCharacterSet); }
},
_ => break,
}
}
}

Ok(())
}

pub fn validate_and_prep_sequencer_adjacent<'a>(
sequencer: &mut Digits<'a>,
adjacent: Option<&str>
) -> Result<(), Error> {

let seq_base = sequencer.base();

if let &Some(num) = &adjacent {
validate_adjacent_input(num.to_string())?;
if seq_base > 3 {
sequencer.prep_non_adjacent(num.parse::<usize>().unwrap());
}
}

Ok(())
}

pub fn validate_file_exists(target: &str) -> Result<(), Error> {
if !Path::new(target).exists() {
return Err(Error::FileMissing)
}

Ok(())

}

pub fn validate_aescrpyt_executable() -> Result<(), Error> {
if Command::new("aescrypt").
stdout(Stdio::null()).
stderr(Stdio::null()).
spawn().
is_err() {
return Err(Error::AescryptMissing);
}

Ok(())
}

pub fn validate_unzip_executable() -> Result<(), Error> {
if Command::new("unzip").
stdout(Stdio::null()).
stderr(Stdio::null()).
spawn().
is_err() {
return Err(Error::UnzipMissing);
}

Ok(())
}
Binary file added test/data/example.zip
Binary file not shown.
20 changes: 20 additions & 0 deletions test/decrypt_zip.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Note: This is not directly executable.
# This is for VIM syntax highlighting.

describe "* abrute decrypts zip files"
it "A) decrypts file"
folder="$(pwd)"
cd test/data/
../../target/debug/abrute 4 1234 -z -a 0 -- example.zip >/dev/null
assert equal $? 0
cd $folder
assert file_present test/data/zipped.file
end

it "B) clean up"
rm test/data/zipped.file
assert file_absent test/data/zipped.file
end

end

0 comments on commit 9d2c27c

Please sign in to comment.