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.
hashcat/tools/test_modules/m33200.pm

225 lines
6.5 KiB

#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Digest::SHA qw (hmac_sha1);
use Crypt::Mode::CBC;
use Crypt::PBKDF2;
sub byte2hex
{
my $input = shift;
return unpack ("H*", $input);
}
sub hex2byte
{
my $input = shift;
return pack ("H*", $input);
}
sub pad
{
my $n = shift;
my $size = shift;
return (~$n + 1) & ($size - 1);
}
sub module_constraints { [[0, 256], [16, 16], [-1, -1], [-1, -1], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $salt = shift;
my $user = shift // "user";
my $realm = shift // "example.com";
my $checksum = shift;
my $edata2 = shift;
my $mysalt = uc $realm;
$mysalt = $mysalt . $user;
# first we generate the 'seed'
my $iter = 4096;
my $pbkdf2 = Crypt::PBKDF2->new
(
hash_class => 'HMACSHA1',
iterations => $iter,
output_len => 32
);
my $b_seed = $pbkdf2->PBKDF2 ($mysalt, $word);
# we can precompute this
my $b_kerberos_nfolded = hex2byte ('6b65726265726f737b9b5b2b93132b93');
my $b_iv = hex2byte ('0' x 32);
# 'key_bytes' will be the AES key used to generate 'ki' (for final hmac-sha1)
# and 'ke' (AES key to decrypt/encrypt the ticket)
my $cbc = Crypt::Mode::CBC->new ('AES', 0);
my $b_key_bytes = $cbc->encrypt ($b_kerberos_nfolded, $b_seed, $b_iv);
$b_key_bytes = $b_key_bytes . $cbc->encrypt ($b_key_bytes, $b_seed, $b_iv);
# precomputed stuff
my $b_nfolded1 = hex2byte ('6b60b0582a6ba80d5aad56ab55406ad5');
my $b_nfolded2 = hex2byte ('be349a4d24be500eaf57abd5ea80757a');
my $b_ki = $cbc->encrypt ($b_nfolded1, $b_key_bytes, $b_iv);
$b_ki = $b_ki . $cbc->encrypt ($b_ki, $b_key_bytes, $b_iv);
my $b_ke = $cbc->encrypt ($b_nfolded2, $b_key_bytes, $b_iv);
$b_ke = $b_ke . $cbc->encrypt ($b_ke, $b_key_bytes, $b_iv);
my $cleartext_ticket = '7981ef3081eca02b3029a003020112a12204200e97d1626616'.
'6e06252cbec52003e0f6b4f0280deec6dc58cdbf39845d6f0e77a11c301a3018a00302010'.
'0a111180f32303233303331363135353732315aa20602045b66ac3ea311180f3230333730'.
'3931343032343830355aa40703050050c10000a511180f323032333033313631353537323'.
'15aa611180f32303233303331363135353732315aa711180f323032333033313730313537'.
'32315aa811180f32303233303331373135353732315aa90d1b0b4558414d504c452e434f4'.
'daa20301ea003020101a11730151b066b72627467741b0b4558414d504c452e434f4d';
if (defined $edata2)
{
my $len_last_block = length ($edata2) % 32;
my $tmp = $len_last_block + 32;
my $b_truncated_enc_ticket = hex2byte (substr $edata2, 0, -$tmp);
my $b_last_block = hex2byte (substr $edata2, -$len_last_block);
my $b_n_1_block = hex2byte (substr (substr ($edata2, -$tmp), 0, 32));
my $b_truncated_ticket_decrypted = $cbc->decrypt ($b_truncated_enc_ticket, $b_ke, $b_iv);
my $truncated_ticket_decrypted = byte2hex ($b_truncated_ticket_decrypted);
my $check_correct = ((substr ($truncated_ticket_decrypted, 16, 4) eq "7981" || substr ($truncated_ticket_decrypted, 16, 4) eq "7a81") && (substr ($truncated_ticket_decrypted, 22, 2) eq "30")) ||
((substr ($truncated_ticket_decrypted, 16, 2) eq "79" || substr ($truncated_ticket_decrypted, 16, 2) eq "7a") && (substr ($truncated_ticket_decrypted, 20, 2) eq "30")) ||
((substr ($truncated_ticket_decrypted, 16, 4) eq "7982" || substr ($truncated_ticket_decrypted, 16, 4) eq "7a82") && (substr ($truncated_ticket_decrypted, 24, 2) eq "30"));
if ($check_correct == 1)
{
my $b_n_2 = substr $b_truncated_enc_ticket, -16;
my $b_n_1_decrypted = $cbc->decrypt ($b_n_1_block, $b_ke, $b_iv);
my $b_last_plain = substr $b_n_1_decrypted, 0, $len_last_block / 2;
$b_last_plain = $b_last_plain ^ $b_last_block;
my $omitted = substr $b_n_1_decrypted, -(16 - $len_last_block / 2);
my $b_n_1 = $b_last_block . $omitted;
$b_n_1 = $cbc->decrypt ($b_n_1, $b_ke, $b_iv);
$b_n_1 = $b_n_1 ^ $b_n_2;
my $b_cleartext_ticket = $b_truncated_ticket_decrypted . $b_n_1 . $b_last_plain;
$cleartext_ticket = byte2hex ($b_cleartext_ticket);
}
else # validation failed
{
# fake/wrong ticket (otherwise if we just decrypt/encrypt we end
#up with false positives all the time)
$cleartext_ticket = "0" x (length ($cleartext_ticket) + 32);
}
}
if (defined $checksum)
{
$checksum = pack ("H*", $checksum);
}
else
{
if (!defined $edata2)
{
my $nonce = unpack ("H*", random_bytes (16));
$cleartext_ticket = $nonce . $cleartext_ticket;
}
# we have what is required to compute checksum
$checksum = hmac_sha1 (hex2byte ($cleartext_ticket), $b_ki);
$checksum = substr $checksum, 0, 12;
}
my $len_cleartext_last_block = length ($cleartext_ticket) % 32;
my $cleartext_last_block = substr $cleartext_ticket, -$len_cleartext_last_block;
my $padding = pad (length ($cleartext_ticket), 32);
my $b_cleartext_last_block_padded = hex2byte ($cleartext_last_block . '0' x $padding);
# we will encrypt until n-1 block (included)
my $truncated_cleartext_ticket = substr $cleartext_ticket, 0, -$len_cleartext_last_block;
my $b_truncated_enc_ticket = $cbc->encrypt (hex2byte ($truncated_cleartext_ticket), $b_ke, $b_iv);
my $b_enc_ticket_n_1_block= substr $b_truncated_enc_ticket, -16;
my $b_enc_last_block = substr $b_enc_ticket_n_1_block, 0, $len_cleartext_last_block / 2;
# we now craft the new n-1 block
my $tmp = $b_enc_ticket_n_1_block ^ $b_cleartext_last_block_padded;
$b_enc_ticket_n_1_block = $cbc->encrypt ($tmp, $b_ke, $b_iv);
$tmp = substr $b_truncated_enc_ticket, 0, -16;
$edata2 = $tmp . $b_enc_ticket_n_1_block . $b_enc_last_block;
my $tmp_hash = sprintf ('$krb5asrep$18$%s$%s$%s$%s', $user, $realm, unpack ("H*", $checksum), unpack ("H*", $edata2));
return $tmp_hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my @data = split ('\$', $hash);
return unless scalar @data == 7;
shift @data;
my $signature = shift @data;
my $algorithm = shift @data;
my $user = shift @data;
my $realm = shift @data;
my $checksum = shift @data;
my $edata2 = shift @data;
return unless ($signature eq "krb5asrep");
return unless ($algorithm eq "18");
return unless (length ($checksum) == 24);
return unless (length ($edata2) >= 64);
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, undef, $user, $realm, $checksum, $edata2);
return ($new_hash, $word);
}
1;