mirror of
https://github.com/hashcat/hashcat.git
synced 2025-07-19 21:18:21 +00:00

In the automatic downtune routine, hashcat prepares a fixed 512MiB host buffer that is known to be allocated by the compute runtimes (CUDA, HIP, OpenCL, Metal), and over which hashcat has no control. However, hashcat still divides the maximum available host memory by the active device count to automatically as a preparation to later downtune the -n and -T parameters when memory is limited. Hashcat reserves 512MiB per active device. With bridges, the active devices become bridge units, which for modes 70000, 70100, and 70200 equals the CPU core count. On a 32-core CPU, this multiplies to 16GiB, even though the memory is actually shared because of threading. This leads to an overestimation of memory usage. A simple fix is to divide the 512MiB buffer by the active device count. This keeps the full 512MiB for a single GPU but avoids overestimating memory usage with many virtual devices. Fix code handling kernel-accel value in argon2_common.c for CPU, which was accidentally removed during previous refactoring. Set thread count to 1 for hash-mode 70000. Oversubscribing the CPU isn't useful here. This allows to keep the wordlist count low, which is very welcome for slow hashes like Argon2id. Fix unit test for 20011/20012/20013 (DiskCryptor) by adding setuptools to install_modules.sh and replacing AES.MODE_XTS with python_AES.MODE_XTS. Fix false negative for kernel 32800, which only occurred if all conditions were true: multihash, -a 3, optimized mode, and password length between 16 and 31. Fix Python package name in BUILD_WSL.md command line example.
506 lines
11 KiB
Perl
506 lines
11 KiB
Perl
#!/usr/bin/env perl
|
|
|
|
##
|
|
## Author......: See docs/credits.txt
|
|
## License.....: MIT
|
|
##
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Crypt::PBKDF2;
|
|
use MIME::Base64 qw (encode_base64);
|
|
use Encode;
|
|
|
|
sub module_constraints { [[0, 256], [128, 128], [-1, -1], [-1, -1], [-1, -1]] }
|
|
|
|
sub aes_encrypt
|
|
{
|
|
my $key_main = shift;
|
|
my $key_tweak = shift;
|
|
my $data = shift;
|
|
|
|
my $data_base64 = encode_base64 ($data, "");
|
|
|
|
my $python_code = <<'END_CODE';
|
|
|
|
from CryptoPlus.Cipher import python_AES
|
|
import base64
|
|
|
|
key1 = base64.b64decode (key_main)
|
|
key2 = base64.b64decode (key_tweak)
|
|
|
|
key = (key1, key2)
|
|
|
|
cipher = python_AES.new (key, python_AES.MODE_XTS)
|
|
|
|
sequence = b"\x01"
|
|
|
|
encrypted = cipher.encrypt (base64.b64decode (data), sequence)
|
|
|
|
print (encrypted.hex ())
|
|
|
|
END_CODE
|
|
|
|
# replace code with these values
|
|
|
|
$python_code =~ s/key_main/"$key_main"/;
|
|
$python_code =~ s/key_tweak/"$key_tweak"/;
|
|
$python_code =~ s/data/"$data_base64"/;
|
|
|
|
my $output_buf = `python3 -c '$python_code'`;
|
|
|
|
$output_buf =~ s/[\r\n]//g;
|
|
|
|
$output_buf = substr ($output_buf, 128); # remove the "garbage" from the start (encrypted salt)
|
|
|
|
return $output_buf;
|
|
}
|
|
|
|
sub aes_decrypt
|
|
{
|
|
my $key_main = shift;
|
|
my $key_tweak = shift;
|
|
my $data = shift;
|
|
|
|
my $data_base64 = encode_base64 ($data, "");
|
|
|
|
my $python_code = <<'END_CODE';
|
|
|
|
from CryptoPlus.Cipher import python_AES
|
|
import base64
|
|
|
|
key1 = base64.b64decode (key_main)
|
|
key2 = base64.b64decode (key_tweak)
|
|
|
|
key = (key1, key2)
|
|
|
|
cipher = python_AES.new (key, python_AES.MODE_XTS)
|
|
|
|
sequence = b"\x01"
|
|
|
|
decrypted = cipher.decrypt (base64.b64decode (data), sequence)
|
|
|
|
print (decrypted.hex ())
|
|
|
|
END_CODE
|
|
|
|
# replace code with these values
|
|
|
|
$python_code =~ s/key_main/"$key_main"/;
|
|
$python_code =~ s/key_tweak/"$key_tweak"/;
|
|
$python_code =~ s/data/"$data_base64"/;
|
|
|
|
my $output_buf = `python3 -c '$python_code'`;
|
|
|
|
$output_buf =~ s/[\r\n]//g;
|
|
|
|
$output_buf = substr ($output_buf, 128); # remove the "garbage" from the start (encrypted salt)
|
|
|
|
$output_buf = pack ("H*", $output_buf);
|
|
|
|
return $output_buf;
|
|
}
|
|
|
|
sub twofish_encrypt
|
|
{
|
|
my $key_main = shift;
|
|
my $key_tweak = shift;
|
|
my $data = shift;
|
|
|
|
my $data_base64 = encode_base64 ($data, "");
|
|
|
|
my $python_code = <<'END_CODE';
|
|
|
|
from CryptoPlus.Cipher import python_Twofish
|
|
import base64
|
|
|
|
key1 = base64.b64decode (key_main)
|
|
key2 = base64.b64decode (key_tweak)
|
|
|
|
key = (key1, key2)
|
|
|
|
cipher = python_Twofish.new (key, python_Twofish.MODE_XTS)
|
|
|
|
sequence = b"\x01"
|
|
|
|
encrypted = cipher.encrypt (base64.b64decode (data), sequence)
|
|
|
|
print (encrypted.hex ())
|
|
|
|
END_CODE
|
|
|
|
# replace code with these values
|
|
|
|
$python_code =~ s/key_main/"$key_main"/;
|
|
$python_code =~ s/key_tweak/"$key_tweak"/;
|
|
$python_code =~ s/data/"$data_base64"/;
|
|
|
|
my $output_buf = `python3 -c '$python_code'`;
|
|
|
|
$output_buf =~ s/[\r\n]//g;
|
|
|
|
$output_buf = substr ($output_buf, 128); # remove the "garbage" from the start (encrypted salt)
|
|
|
|
return $output_buf;
|
|
}
|
|
|
|
sub twofish_decrypt
|
|
{
|
|
my $key_main = shift;
|
|
my $key_tweak = shift;
|
|
my $data = shift;
|
|
|
|
my $data_base64 = encode_base64 ($data, "");
|
|
|
|
my $python_code = <<'END_CODE';
|
|
|
|
from CryptoPlus.Cipher import python_Twofish
|
|
import base64
|
|
|
|
key1 = base64.b64decode (key_main)
|
|
key2 = base64.b64decode (key_tweak)
|
|
|
|
key = (key1, key2)
|
|
|
|
cipher = python_Twofish.new (key, python_Twofish.MODE_XTS)
|
|
|
|
sequence = b"\x01"
|
|
|
|
decrypted = cipher.decrypt (base64.b64decode (data), sequence)
|
|
|
|
print (decrypted.hex ())
|
|
|
|
END_CODE
|
|
|
|
# replace code with these values
|
|
|
|
$python_code =~ s/key_main/"$key_main"/;
|
|
$python_code =~ s/key_tweak/"$key_tweak"/;
|
|
$python_code =~ s/data/"$data_base64"/;
|
|
|
|
my $output_buf = `python3 -c '$python_code'`;
|
|
|
|
$output_buf =~ s/[\r\n]//g;
|
|
|
|
$output_buf = substr ($output_buf, 128); # remove the "garbage" from the start (encrypted salt)
|
|
|
|
$output_buf = pack ("H*", $output_buf);
|
|
|
|
return $output_buf;
|
|
}
|
|
|
|
sub serpent_encrypt
|
|
{
|
|
my $key_main = shift;
|
|
my $key_tweak = shift;
|
|
my $data = shift;
|
|
|
|
my $data_base64 = encode_base64 ($data, "");
|
|
|
|
my $python_code = <<'END_CODE';
|
|
|
|
from CryptoPlus.Cipher import python_Serpent
|
|
import base64
|
|
|
|
key1 = base64.b64decode (key_main)
|
|
key2 = base64.b64decode (key_tweak)
|
|
|
|
key = (key1, key2)
|
|
|
|
cipher = python_Serpent.new (key, python_Serpent.MODE_XTS)
|
|
|
|
sequence = b"\x01"
|
|
|
|
encrypted = cipher.encrypt (base64.b64decode (data), sequence)
|
|
|
|
print (encrypted.hex ())
|
|
|
|
END_CODE
|
|
|
|
# replace code with these values
|
|
|
|
$python_code =~ s/key_main/"$key_main"/;
|
|
$python_code =~ s/key_tweak/"$key_tweak"/;
|
|
$python_code =~ s/data/"$data_base64"/;
|
|
|
|
my $output_buf = `python3 -c '$python_code'`;
|
|
|
|
$output_buf =~ s/[\r\n]//g;
|
|
|
|
$output_buf = substr ($output_buf, 128); # remove the "garbage" from the start (encrypted salt)
|
|
|
|
return $output_buf;
|
|
}
|
|
|
|
sub serpent_decrypt
|
|
{
|
|
my $key_main = shift;
|
|
my $key_tweak = shift;
|
|
my $data = shift;
|
|
|
|
my $data_base64 = encode_base64 ($data, "");
|
|
|
|
my $python_code = <<'END_CODE';
|
|
|
|
from CryptoPlus.Cipher import python_Serpent
|
|
import base64
|
|
|
|
key1 = base64.b64decode (key_main)
|
|
key2 = base64.b64decode (key_tweak)
|
|
|
|
key = (key1, key2)
|
|
|
|
cipher = python_Serpent.new (key, python_Serpent.MODE_XTS)
|
|
|
|
sequence = b"\x01"
|
|
|
|
decrypted = cipher.decrypt (base64.b64decode (data), sequence)
|
|
|
|
print (decrypted.hex ())
|
|
|
|
END_CODE
|
|
|
|
# replace code with these values
|
|
|
|
$python_code =~ s/key_main/"$key_main"/;
|
|
$python_code =~ s/key_tweak/"$key_tweak"/;
|
|
$python_code =~ s/data/"$data_base64"/;
|
|
|
|
my $output_buf = `python3 -c '$python_code'`;
|
|
|
|
$output_buf =~ s/[\r\n]//g;
|
|
|
|
$output_buf = substr ($output_buf, 128); # remove the "garbage" from the start (encrypted salt)
|
|
|
|
$output_buf = pack ("H*", $output_buf);
|
|
|
|
return $output_buf;
|
|
}
|
|
|
|
sub verify_data
|
|
{
|
|
my $data = shift;
|
|
|
|
if (length ($data) < 16)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (substr ($data, 0, 4) ne "DCRP")
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
my $flags = unpack ("H*", substr ($data, 8, 6));
|
|
|
|
if (($flags ne "020004000000") && ($flags ne "020005000000") && ($flags ne "020008000000"))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub module_generate_hash
|
|
{
|
|
my $word = shift;
|
|
my $salt = shift;
|
|
my $data = shift // "";
|
|
|
|
my $iter = 1000;
|
|
|
|
my $pbkdf2 = Crypt::PBKDF2->new
|
|
(
|
|
hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 512),
|
|
iterations => $iter,
|
|
output_len => 192
|
|
);
|
|
|
|
my $word_utf16le = encode ("UTF-16LE", $word);
|
|
|
|
my $salt_bin = pack ("H*", $salt);
|
|
|
|
my $key = $pbkdf2->PBKDF2 ($salt_bin, $word_utf16le);
|
|
|
|
my $key1 = encode_base64 (substr ($key, 0, 32), "");
|
|
my $key2 = encode_base64 (substr ($key, 32, 32), "");
|
|
my $key3 = encode_base64 (substr ($key, 64, 32), "");
|
|
my $key4 = encode_base64 (substr ($key, 96, 32), "");
|
|
my $key5 = encode_base64 (substr ($key, 128, 32), "");
|
|
my $key6 = encode_base64 (substr ($key, 160, 32), "");
|
|
|
|
my $algo = random_number (1, 7);
|
|
|
|
my $diskcryptor_data = "";
|
|
|
|
$diskcryptor_data .= $salt_bin;
|
|
|
|
if (length ($data) == 0) # normal case
|
|
{
|
|
$diskcryptor_data .= "DCRP";
|
|
|
|
$diskcryptor_data .= random_bytes (4);
|
|
|
|
my $flags = random_number (1, 3);
|
|
|
|
if ($flags == 1)
|
|
{
|
|
$diskcryptor_data .= pack ("H*", "02000400");
|
|
}
|
|
elsif ($flags == 2)
|
|
{
|
|
$diskcryptor_data .= pack ("H*", "02000500");
|
|
}
|
|
else
|
|
{
|
|
$diskcryptor_data .= pack ("H*", "02000800");
|
|
}
|
|
|
|
$diskcryptor_data .= "\x00" x (2048 - length ($diskcryptor_data)); # fill it up to 2048 bytes
|
|
}
|
|
else # verify
|
|
{
|
|
# decrypt the data and verify:
|
|
|
|
$algo = 1;
|
|
|
|
my $output_buf = aes_decrypt ($key1, $key2, $data);
|
|
|
|
if (verify_data ($output_buf) == 0)
|
|
{
|
|
$algo = 2;
|
|
|
|
$output_buf = twofish_decrypt ($key1, $key2, $data);
|
|
|
|
if (verify_data ($output_buf) == 0)
|
|
{
|
|
$algo = 3;
|
|
|
|
$output_buf = serpent_decrypt ($key1, $key2, $data);
|
|
|
|
if (verify_data ($output_buf) == 0)
|
|
{
|
|
$algo = 4;
|
|
|
|
$output_buf = aes_decrypt ($key2, $key4, $data);
|
|
$output_buf = twofish_decrypt ($key1, $key3, $salt_bin . $output_buf);
|
|
|
|
if (verify_data ($output_buf) == 0)
|
|
{
|
|
$algo = 5;
|
|
|
|
$output_buf = twofish_decrypt ($key2, $key4, $data);
|
|
$output_buf = serpent_decrypt ($key1, $key3, $salt_bin . $output_buf);
|
|
|
|
if (verify_data ($output_buf) == 0)
|
|
{
|
|
$algo = 6;
|
|
|
|
$output_buf = serpent_decrypt ($key2, $key4, $data);
|
|
$output_buf = aes_decrypt ($key1, $key3, $salt_bin . $output_buf);
|
|
|
|
if (verify_data ($output_buf) == 0)
|
|
{
|
|
$algo = 7;
|
|
|
|
$output_buf = aes_decrypt ($key3, $key6, $data);
|
|
$output_buf = twofish_decrypt ($key2, $key5, $salt_bin . $output_buf);
|
|
$output_buf = serpent_decrypt ($key1, $key4, $salt_bin . $output_buf);
|
|
|
|
if (verify_data ($output_buf) == 0)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$diskcryptor_data .= $output_buf;
|
|
}
|
|
|
|
my $hash_buf = "";
|
|
|
|
if ($algo == 1)
|
|
{
|
|
$hash_buf = aes_encrypt ($key1, $key2, $diskcryptor_data);
|
|
}
|
|
elsif ($algo == 2)
|
|
{
|
|
$hash_buf = twofish_encrypt ($key1, $key2, $diskcryptor_data);
|
|
}
|
|
elsif ($algo == 3)
|
|
{
|
|
$hash_buf = serpent_encrypt ($key1, $key2, $diskcryptor_data);
|
|
}
|
|
elsif ($algo == 4)
|
|
{
|
|
$hash_buf = twofish_encrypt ($key1, $key3, $diskcryptor_data);
|
|
$hash_buf = aes_encrypt ($key2, $key4, $salt_bin . pack ("H*", $hash_buf));
|
|
}
|
|
elsif ($algo == 5)
|
|
{
|
|
$hash_buf = serpent_encrypt ($key1, $key3, $diskcryptor_data);
|
|
$hash_buf = twofish_encrypt ($key2, $key4, $salt_bin . pack ("H*", $hash_buf));
|
|
}
|
|
elsif ($algo == 6)
|
|
{
|
|
$hash_buf = aes_encrypt ($key1, $key3, $diskcryptor_data);
|
|
$hash_buf = serpent_encrypt ($key2, $key4, $salt_bin . pack ("H*", $hash_buf));
|
|
}
|
|
else
|
|
{
|
|
$hash_buf = serpent_encrypt ($key1, $key4, $diskcryptor_data);
|
|
$hash_buf = twofish_encrypt ($key2, $key5, $salt_bin . pack ("H*", $hash_buf));
|
|
$hash_buf = aes_encrypt ($key3, $key6, $salt_bin . pack ("H*", $hash_buf));
|
|
}
|
|
|
|
my $hash = sprintf ("\$diskcryptor\$0*%s%s", $salt, $hash_buf);
|
|
|
|
return $hash;
|
|
}
|
|
|
|
sub module_verify_hash
|
|
{
|
|
my $line = shift;
|
|
|
|
my $index1 = index ($line, ":");
|
|
|
|
return if $index1 < 1;
|
|
|
|
my $hash_in = substr ($line, 0, $index1);
|
|
|
|
return unless (substr ($hash_in, 0, 13) eq "\$diskcryptor\$");
|
|
|
|
my $word = substr ($line, $index1 + 1);
|
|
|
|
my $index2 = index ($hash_in, "\*", 13);
|
|
|
|
return if $index2 < 1;
|
|
|
|
my $version = substr ($hash_in, 13, $index2 - 13);
|
|
|
|
return unless ($version eq "0");
|
|
|
|
my $data = substr ($hash_in, $index2 + 1);
|
|
|
|
return unless (length ($data) == 4096);
|
|
|
|
my $salt = substr ($data, 0, 128);
|
|
|
|
return unless defined $word;
|
|
|
|
$word = pack_if_HEX_notation ($word);
|
|
|
|
$data = pack ("H*", $data);
|
|
|
|
my $new_hash = module_generate_hash ($word, $salt, $data);
|
|
|
|
return ($new_hash, $word);
|
|
}
|
|
|
|
1;
|