Add test modules and helper functions

Modularize mode 5600, add random_ helper functions, update readme.
pull/1835/head
R. Yushaev 5 years ago
parent ae53dd3b85
commit 9f1749dce3

@ -8,7 +8,7 @@
use strict;
use warnings;
use Data::Types qw (is_count is_whole);
use Data::Types qw (is_count is_int is_whole);
use File::Basename;
use FindBin;
@ -25,8 +25,9 @@ is_in_array ($TYPE, $TYPES) or usage_exit ();
is_whole ($MODE) or die "Mode must be a number\n";
my $module = sprintf ("m%05d.pm", $MODE);
my $MODULE_FILE = sprintf ("m%05d.pm", $MODE);
eval { require $module; } or die "Could not load test module: $module\n$@";
eval { require $MODULE_FILE } or die "Could not load test module: $MODULE_FILE\n$@";
exists &{module_generate_hash} or die "Module function 'module_generate_hash' not found\n";
exists &{module_verify_hash} or die "Module function 'module_verify_hash' not found\n";
@ -54,18 +55,21 @@ sub single
{
my $len = shift;
# fallback to incrementing length
undef $len unless is_count ($len);
my $format = "echo -n %-32s | ./hashcat \${OPTS} -a 0 -m %d '%s'\n";
for (my $i = 1; $i <= 32; $i++)
{
# requested or incrementing length
my $cur_len = $len // $i;
my $word = random_numeric_string ($cur_len);
my $hash = module_generate_hash ($word);
# possible if the requested length is not supported by algorithm
next unless defined $hash;
print sprintf ($format, $word, $MODE, $hash);
@ -116,8 +120,10 @@ sub verify
my $hash = module_verify_hash ($line);
# possible if the hash:password pair does not match
next unless defined $hash;
# possible if the hash is in cracksfile, but not in hashfile
next unless is_in_array ($hash, $hashlist);
print OUT "$line\n";
@ -138,6 +144,7 @@ sub is_in_array
return grep { $_ eq $value } @{$array};
}
# detect hashcat $HEX[...] notation and pack as binary
sub pack_if_HEX_notation
{
my $string = shift;
@ -152,8 +159,33 @@ sub pack_if_HEX_notation
return $string;
}
# random_count (max)
# returns integer from 1 to max
sub random_count
{
my $max = shift;
return unless is_count $max;
return int ((rand ($max - 1)) + 1);
}
# random_number (min, max)
sub random_number
{
my $min = shift;
my $max = shift;
return unless is_int ($min);
return unless is_int ($max);
return unless $min lt $max;
return int ((rand ($max - $min)) + $min);
}
sub random_bytes
{
# length in bytes
my $count = shift;
return pack ("H*", random_hex_string (2 * $count));
@ -161,6 +193,7 @@ sub random_bytes
sub random_hex_string
{
# length in characters
my $count = shift;
return if ! is_count ($count);
@ -172,6 +205,51 @@ sub random_hex_string
return $string;
}
sub random_lowercase_string
{
my $count = shift;
return if ! is_count ($count);
my @chars = ('a'..'z');
my $string;
$string .= $chars[rand @chars] for (1 .. $count);
return $string;
}
sub random_uppercase_string
{
my $count = shift;
return if ! is_count ($count);
my @chars = ('A'..'Z');
my $string;
$string .= $chars[rand @chars] for (1 .. $count);
return $string;
}
sub random_mixedcase_string
{
my $count = shift;
return if ! is_count ($count);
my @chars = ('A'..'Z', 'a'..'z');
my $string;
$string .= $chars[rand @chars] for (1 .. $count);
return $string;
}
sub random_numeric_string
{
my $count = shift;
@ -185,6 +263,21 @@ sub random_numeric_string
return $string;
}
sub random_string
{
my $count = shift;
return if ! is_count ($count);
my @chars = ('A'..'Z', 'a'..'z', '0'..'9');
my $string;
$string .= $chars[rand @chars] for (1 .. $count);
return $string;
}
sub usage_exit
{
my $f = basename ($0);

@ -1,15 +1,16 @@
### Hashcat test modules ###
Each module provides the two functions `module_generate_hash` and `module_verify_hash`. The first parameter to `module_generate_hash` is the password, which can be either in ASCII or binary (packed) form. The `module_verify_hash` function accepts a line from the cracks file, without the newline characters.
Each module provides the functions `module_generate_hash` and `module_verify_hash`. The first parameter to `module_generate_hash` is the password, which can be either in ASCII or binary (packed) form. The `module_verify_hash` function accepts a line from the cracks file, without the newline characters.
During `single` and `passthrough` tests the `module_generate_hash` function must provide random values (e.g. salt) for hash generation if necessary. The test.pl script offers a few handy functions like `random_hex_string`, `random_numeric_string` and `random_bytes`. You can implement your own salt generation functions, if your mode has specific requirements.
During `verify` tests the `module_verify_hash` function must parse the hash:password line and to calculate a hash by passing all necessary data to `module_generate_hash`. How you pass it is up to you, as long as the first parameter is the password.
During `verify` tests the `module_verify_hash` function must parse the hash:password line and calculate a hash by passing all necessary data to `module_generate_hash`. How you pass it is up to you, as long as the first parameter is the password.
**Important**: You have to call `pack_if_HEX_notation` as soon as you have parsed the password, or your tests will fail on passwords in the `$HEX[...]` format.
#### Examples ####
* For the most basic test modules, see [m0.pm](m0.pm) and [m100.pm](m100.pm)
* For the basic salted hash tests, see [m110.pm](m110.pm) and [m120.pm](m120.pm)
* For the most basic test modules, see [m00000.pm](m00000.pm) and [m00100.pm](m00100.pm)
* For the basic salted hash tests, see [m00110.pm](m00110.pm) and [m00120.pm](m00120.pm)
* For some sligthly more complex modules with PBKDF2 and encryption, see [m18400.pm](m18400.pm) and [m18600.pm](m18600.pm)
* For a test module with a custom salt generation algorithm, see [m05600.pm](m05600.pm)

@ -23,7 +23,7 @@ sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split ":", $line;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;

@ -23,7 +23,7 @@ sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (":", $line);
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;

@ -13,7 +13,7 @@ use Digest::SHA qw (sha1_hex);
sub module_generate_hash
{
my $word = shift;
my $salt = shift // random_numeric_string (int (rand 16));
my $salt = shift // random_numeric_string (random_count (15));
my $hash = sha1_hex ($word . $salt) . ":$salt";
@ -24,7 +24,7 @@ sub module_verify_hash
{
my $line = shift;
my ($hash, $salt, $word) = split (":", $line);
my ($hash, $salt, $word) = split (':', $line);
return unless defined $hash;
return unless defined $salt;

@ -13,7 +13,7 @@ use Digest::SHA qw (sha1_hex);
sub module_generate_hash
{
my $word = shift;
my $salt = shift // random_numeric_string (int (rand 16));
my $salt = shift // random_numeric_string (random_count (15));
my $hash = sha1_hex ($salt . $word) . ":$salt";
@ -24,7 +24,7 @@ sub module_verify_hash
{
my $line = shift;
my ($hash, $salt, $word) = split (":", $line);
my ($hash, $salt, $word) = split (':', $line);
return unless defined $hash;
return unless defined $salt;

@ -0,0 +1,89 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use Authen::Passphrase::NTHash;
use Digest::HMAC qw (hmac hmac_hex);
use Digest::MD5 qw (md5);
use Encode qw (encode);
sub module_generate_hash
{
my $word = shift;
my $user_len = random_number (0, 27);
my $domain_len = 27 - $user_len;
my $user = shift // random_string ($user_len);
my $domain = shift // random_string ($domain_len);
my $srv_ch = shift // random_hex_string (2*8);
my $cli_ch = shift // random_client_challenge ();
my $b_srv_ch = pack ("H*", $srv_ch);
my $b_cli_ch = pack ("H*", $cli_ch);
my $nthash = Authen::Passphrase::NTHash->new (passphrase => $word)->hash;
my $identity = encode ("UTF-16LE", uc ($user) . $domain);
my $hash_buf = hmac_hex ($b_srv_ch . $b_cli_ch, hmac ($identity, $nthash, \&md5, 64), \&md5, 64);
my $hash = sprintf ("%s::%s:%s:%s:%s", $user, $domain, $srv_ch, $hash_buf, $cli_ch);
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my $user;
my $domain;
my $srv_ch;
my $cli_ch;
my $word;
my $hash;
my $index1 = index ($line, "::");
my $index2 = index ($line, ":", $index1 + 2);
my $index3 = index ($line, ":", $index2 + 3 + 16 + 32);
return if $index1 eq -1;
return if $index2 eq -1;
return if $index3 eq -1;
$hash = substr ($line, 0, $index3);
$user = substr ($line, 0, $index1);
$domain = substr ($line, $index1 + 2, $index2 - $index1 - 2);
$srv_ch = substr ($line, $index2 + 1, 16);
$cli_ch = substr ($line, $index2 + 3 + 16 + 32, $index3 - $index2 - 3 - 16 - 32);
$word = substr ($line, $index3 + 1);
$word = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word, $user, $domain, $srv_ch, $cli_ch);
return unless lc $new_hash eq lc $hash;
return $new_hash;
}
sub random_client_challenge
{
my $ch;
$ch .= '0101000000000000';
$ch .= random_hex_string (2*16);
$ch .= '00000000';
$ch .= random_hex_string(2 * random_count (20));
$ch .= '00';
return $ch;
}
1;

@ -47,7 +47,7 @@ sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (":", $line);
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;

@ -60,7 +60,7 @@ sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (":", $line);
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;

Loading…
Cancel
Save