mirror of https://github.com/hashcat/hashcat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
9.3 KiB
413 lines
9.3 KiB
![]()
2 years ago
|
#!/usr/bin/env perl
|
||
|
|
||
|
##
|
||
|
## Author......: See docs/credits.txt
|
||
|
## License.....: MIT
|
||
|
##
|
||
|
|
||
|
use strict;
|
||
|
use warnings;
|
||
|
|
||
|
use Crypt::CBC;
|
||
|
use Crypt::Mode::ECB;
|
||
|
use Digest::SHA qw (sha256);
|
||
|
|
||
|
sub module_constraints { [[64, 64], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] }
|
||
|
|
||
|
sub get_random_keepass_salt
|
||
|
{
|
||
|
my $version = random_number (1, 2);
|
||
|
|
||
|
my $algorithm;
|
||
|
|
||
|
my $iteration;
|
||
|
|
||
|
my $final_random_seed;
|
||
|
|
||
|
if ($version == 1)
|
||
|
{
|
||
|
$algorithm = random_number (0, 1);
|
||
|
|
||
|
$iteration = random_number (50000, 99999);
|
||
|
|
||
|
$final_random_seed = random_bytes (16);
|
||
|
$final_random_seed = unpack ("H*", $final_random_seed);
|
||
|
}
|
||
|
elsif ($version == 2)
|
||
|
{
|
||
|
$algorithm = 0;
|
||
|
|
||
|
$iteration = random_number (6000, 99999);
|
||
|
|
||
|
$final_random_seed = random_bytes (32);
|
||
|
$final_random_seed = unpack ("H*", $final_random_seed);
|
||
|
}
|
||
|
|
||
|
my $transf_random_seed = random_bytes (32);
|
||
|
$transf_random_seed = unpack ("H*", $transf_random_seed);
|
||
|
|
||
|
my $enc_iv = random_bytes (16);
|
||
|
$enc_iv = unpack ("H*", $enc_iv);
|
||
|
|
||
|
my $contents_hash = random_bytes (32);
|
||
|
$contents_hash = unpack ("H*", $contents_hash);
|
||
|
|
||
|
my $inline_flag = 1;
|
||
|
|
||
|
my $contents_len = random_number (128, 499);
|
||
|
|
||
|
my $contents = random_bytes ($contents_len);
|
||
|
|
||
|
$contents_len += 16 - $contents_len % 16;
|
||
|
|
||
|
$contents = unpack ("H*", $contents);
|
||
|
|
||
|
my $salt_buf;
|
||
|
|
||
|
#my $is_keyfile = random_number (0, 1);
|
||
|
my $is_keyfile = 0;
|
||
|
|
||
|
my $keyfile_attributes = "";
|
||
|
|
||
|
if ($is_keyfile == 1)
|
||
|
{
|
||
|
$keyfile_attributes = $keyfile_attributes
|
||
|
. "1*64*"
|
||
|
. unpack ("H*", random_bytes (32));
|
||
|
}
|
||
|
|
||
|
if ($version == 1)
|
||
|
{
|
||
|
$salt_buf = $version . '*' .
|
||
|
$iteration . '*' .
|
||
|
$algorithm . '*' .
|
||
|
$final_random_seed . '*' .
|
||
|
$transf_random_seed . '*' .
|
||
|
$enc_iv . '*' .
|
||
|
$contents_hash . '*' .
|
||
|
$inline_flag . '*' .
|
||
|
$contents_len . '*' .
|
||
|
$contents . '*' .
|
||
|
$keyfile_attributes;
|
||
|
}
|
||
|
elsif ($version == 2)
|
||
|
{
|
||
|
$contents = random_bytes (32);
|
||
|
$contents = unpack ("H*", $contents);
|
||
|
|
||
|
$salt_buf = $version . '*' .
|
||
|
$iteration . '*' .
|
||
|
$algorithm . '*' .
|
||
|
$final_random_seed . '*' .
|
||
|
$transf_random_seed . '*' .
|
||
|
$enc_iv . '*' .
|
||
|
$contents_hash . '*' .
|
||
|
$contents . '*' .
|
||
|
$keyfile_attributes;
|
||
|
}
|
||
|
|
||
|
return $salt_buf;
|
||
|
}
|
||
|
|
||
|
sub module_generate_hash
|
||
|
{
|
||
|
my $word = shift;
|
||
|
my $salt = shift;
|
||
|
my $param = shift;
|
||
|
|
||
|
if (length $salt == 0)
|
||
|
{
|
||
|
$salt = get_random_keepass_salt ();
|
||
|
}
|
||
|
|
||
|
my @salt_arr = split ('\*', $salt);
|
||
|
|
||
|
my $version = $salt_arr[0];
|
||
|
|
||
|
my $iteration = $salt_arr[1];
|
||
|
|
||
|
my $algorithm = $salt_arr[2];
|
||
|
|
||
|
my $final_random_seed = $salt_arr[3];
|
||
|
|
||
|
my $transf_random_seed = $salt_arr[4];
|
||
|
|
||
|
my $enc_iv = $salt_arr[5];
|
||
|
|
||
|
my $contents_hash;
|
||
|
|
||
|
# specific to version 1
|
||
|
my $inline_flag;
|
||
|
my $contents_len;
|
||
|
my $contents;
|
||
|
|
||
|
# specific to version 2
|
||
|
my $expected_bytes;
|
||
|
|
||
|
# specific to keyfile handling
|
||
|
my $inline_keyfile_flag;
|
||
|
my $keyfile_len;
|
||
|
my $keyfile_content;
|
||
|
my $keyfile_attributes = "";
|
||
|
|
||
|
$final_random_seed = pack ("H*", $final_random_seed);
|
||
|
|
||
|
$transf_random_seed = pack ("H*", $transf_random_seed);
|
||
|
|
||
|
$enc_iv = pack ("H*", $enc_iv);
|
||
|
|
||
|
my $word_bin = pack ("H*", $word);
|
||
|
|
||
|
#my $intermediate_hash = sha256 ($word);
|
||
|
my $intermediate_hash = sha256 ($word_bin);
|
||
|
|
||
|
if ($version == 1)
|
||
|
{
|
||
|
$contents_hash = $salt_arr[6];
|
||
|
|
||
|
$contents_hash = pack ("H*", $contents_hash);
|
||
|
|
||
|
$inline_flag = $salt_arr[7];
|
||
|
|
||
|
|
||
|
$contents_len = $salt_arr[8];
|
||
|
|
||
|
|
||
|
$contents = $salt_arr[9];
|
||
|
|
||
|
$contents = pack ("H*", $contents);
|
||
|
|
||
|
# keyfile handling
|
||
|
if (scalar @salt_arr == 13)
|
||
|
{
|
||
|
$inline_keyfile_flag = $salt_arr[10];
|
||
|
|
||
|
$keyfile_len = $salt_arr[11];
|
||
|
|
||
|
$keyfile_content = $salt_arr[12];
|
||
|
|
||
|
$keyfile_attributes = $keyfile_attributes
|
||
|
. "*" . $inline_keyfile_flag
|
||
|
. "*" . $keyfile_len
|
||
|
. "*" . $keyfile_content;
|
||
|
|
||
|
$intermediate_hash = $intermediate_hash . pack ("H*", $keyfile_content);
|
||
|
|
||
|
$intermediate_hash = sha256 ($intermediate_hash);
|
||
|
}
|
||
|
}
|
||
|
elsif ($version == 2)
|
||
|
{
|
||
|
# keyfile handling
|
||
|
if (scalar @salt_arr == 11)
|
||
|
{
|
||
|
$inline_keyfile_flag = $salt_arr[8];
|
||
|
|
||
|
$keyfile_len = $salt_arr[9];
|
||
|
|
||
|
$keyfile_content = $salt_arr[10];
|
||
|
|
||
|
$intermediate_hash = $intermediate_hash . pack ("H*", $keyfile_content);
|
||
|
|
||
|
$keyfile_attributes = $keyfile_attributes
|
||
|
. "*" . $inline_keyfile_flag
|
||
|
. "*" . $keyfile_len
|
||
|
. "*" . $keyfile_content;
|
||
|
|
||
|
}
|
||
|
|
||
|
#$intermediate_hash = sha256 ($intermediate_hash);
|
||
|
}
|
||
|
|
||
|
my $aes = Crypt::Mode::ECB->new ('AES', 1);
|
||
|
|
||
|
for (my $j = 0; $j < $iteration; $j++)
|
||
|
{
|
||
|
$intermediate_hash = $aes->encrypt ($intermediate_hash, $transf_random_seed);
|
||
|
|
||
|
$intermediate_hash = substr ($intermediate_hash, 0, 32);
|
||
|
}
|
||
|
|
||
|
$intermediate_hash = sha256 ($intermediate_hash);
|
||
|
|
||
|
my $final_key = sha256 ($final_random_seed . $intermediate_hash);
|
||
|
|
||
|
my $final_algorithm;
|
||
|
|
||
|
if ($version == 1 && $algorithm == 1)
|
||
|
{
|
||
|
$final_algorithm = "Crypt::Twofish";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$final_algorithm = "Crypt::Rijndael";
|
||
|
}
|
||
|
|
||
|
my $cipher = Crypt::CBC->new ({
|
||
|
key => $final_key,
|
||
|
cipher => $final_algorithm,
|
||
|
iv => $enc_iv,
|
||
|
literal_key => 1,
|
||
|
header => "none",
|
||
|
keysize => 32
|
||
|
});
|
||
|
|
||
|
my $hash;
|
||
|
|
||
|
if ($version == 1)
|
||
|
{
|
||
|
if (defined $param)
|
||
|
{
|
||
|
# if we try to verify the crack, we need to decrypt the contents instead of only encrypting it:
|
||
|
|
||
|
$contents = $cipher->decrypt ($contents);
|
||
|
|
||
|
# and check the output
|
||
|
|
||
|
my $contents_hash_old = $contents_hash;
|
||
|
|
||
|
$contents_hash = sha256 ($contents);
|
||
|
|
||
|
if ($contents_hash_old ne $contents_hash)
|
||
|
{
|
||
|
# fake content
|
||
|
$contents = "\x00" x length ($contents);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$contents_hash = sha256 ($contents);
|
||
|
}
|
||
|
|
||
|
$contents = $cipher->encrypt ($contents);
|
||
|
|
||
|
$hash = sprintf ('$keepass$*%d*%d*%d*%s*%s*%s*%s*%d*%d*%s%s',
|
||
|
$version,
|
||
|
$iteration,
|
||
|
$algorithm,
|
||
|
unpack ("H*", $final_random_seed),
|
||
|
unpack ("H*", $transf_random_seed),
|
||
|
unpack ("H*", $enc_iv),
|
||
|
unpack ("H*", $contents_hash),
|
||
|
$inline_flag,
|
||
|
$contents_len,
|
||
|
unpack ("H*", $contents),
|
||
|
$keyfile_attributes);
|
||
|
}
|
||
|
if ($version == 2)
|
||
|
{
|
||
|
$expected_bytes = $salt_arr[6];
|
||
|
|
||
|
$contents_hash = $salt_arr[7];
|
||
|
$contents_hash = pack ("H*", $contents_hash);
|
||
|
|
||
|
$expected_bytes = $cipher->decrypt ($contents_hash);
|
||
|
|
||
|
$expected_bytes = substr ($expected_bytes . "\x00" x 32, 0, 32); # padding
|
||
|
|
||
|
$hash = sprintf ('$keepass$*%d*%d*%d*%s*%s*%s*%s*%s%s',
|
||
|
$version,
|
||
|
$iteration,
|
||
|
$algorithm,
|
||
|
unpack ("H*", $final_random_seed),
|
||
|
unpack ("H*", $transf_random_seed),
|
||
|
unpack ("H*", $enc_iv),
|
||
|
unpack ("H*", $expected_bytes),
|
||
|
unpack ("H*", $contents_hash),
|
||
|
$keyfile_attributes);
|
||
|
}
|
||
|
|
||
|
return $hash;
|
||
|
}
|
||
|
|
||
|
sub module_verify_hash
|
||
|
{
|
||
|
my $line = shift;
|
||
|
|
||
|
my ($hash_in, $word) = split ":", $line;
|
||
|
|
||
|
return unless defined $hash_in;
|
||
|
return unless defined $word;
|
||
|
|
||
|
my @data = split ('\*', $hash_in);
|
||
|
|
||
|
return unless (scalar @data == 9
|
||
|
|| scalar @data == 11
|
||
|
|| scalar @data == 12
|
||
|
|| scalar @data == 14);
|
||
|
|
||
|
my $signature = shift @data;
|
||
|
return unless ($signature eq '$keepass$');
|
||
|
|
||
|
my $version = shift @data;
|
||
|
return unless ($version == 1 || $version == 2);
|
||
|
|
||
|
my $iteration = shift @data;
|
||
|
|
||
|
my $algorithm = shift @data;
|
||
|
|
||
|
my $final_random_seed = shift @data;
|
||
|
|
||
|
if ($version == 1)
|
||
|
{
|
||
|
return unless (length ($final_random_seed) == 32);
|
||
|
}
|
||
|
elsif ($version == 2)
|
||
|
{
|
||
|
return unless (length ($final_random_seed) == 64);
|
||
|
}
|
||
|
|
||
|
my $transf_random_seed = shift @data;
|
||
|
return unless (length ($transf_random_seed) == 64);
|
||
|
|
||
|
my $enc_iv = shift @data;
|
||
|
return unless (length ($enc_iv) == 32);
|
||
|
|
||
|
if ($version == 1)
|
||
|
{
|
||
|
my $contents_hash = shift @data;
|
||
|
return unless (length ($contents_hash) == 64);
|
||
|
|
||
|
my $inline_flags = shift @data;
|
||
|
return unless ($inline_flags == 1);
|
||
|
|
||
|
my $contents_len = shift @data;
|
||
|
|
||
|
my $contents = shift @data;
|
||
|
return unless (length ($contents) == $contents_len * 2);
|
||
|
}
|
||
|
elsif ($version == 2)
|
||
|
{
|
||
|
my $expected_bytes = shift @data;
|
||
|
return unless (length ($expected_bytes) == 64);
|
||
|
|
||
|
my $contents_hash = shift @data;
|
||
|
return unless (length ($contents_hash) == 64);
|
||
|
}
|
||
|
|
||
|
if (scalar @data == 12 || scalar @data == 14)
|
||
|
{
|
||
|
my $inline_flags = shift @data;
|
||
|
return unless ($inline_flags == 1);
|
||
|
|
||
|
my $keyfile_len = shift @data;
|
||
|
return unless ($keyfile_len == 64);
|
||
|
|
||
|
my $keyfile = shift @data;
|
||
|
return unless (length ($keyfile) == $keyfile_len);
|
||
|
}
|
||
|
|
||
|
my $salt = substr ($hash_in, length ("*keepass*") + 1);
|
||
|
my $param = 1; # distinguish between encrypting vs decrypting
|
||
|
|
||
|
return unless defined $salt;
|
||
|
|
||
|
$word = pack_if_HEX_notation ($word);
|
||
|
|
||
|
my $new_hash = module_generate_hash ($word, $salt, $param);
|
||
|
|
||
|
return ($new_hash, $word);
|
||
|
}
|
||
|
|
||
|
1;
|