1#!/usr/bin/env perl
2
3##
4## Author......: See docs/credits.txt
5## License.....: MIT
6##
7
8use strict;
9use warnings;
10
11use Encode;
12use Crypt::RC4;
13use Digest::HMAC_MD5 qw (hmac_md5);
14use Digest::MD4      qw (md4);
15use Digest::MD5      qw (md5_hex);
16use POSIX            qw (strftime);
17
18sub module_constraints { [[0, 256], [16, 16], [0, 27], [16, 16], [-1, -1]] }
19
20sub module_generate_hash
21{
22  my $word                = shift;
23  my $salt                = shift;
24  my $user_principal_name = shift // "user\@domain.com";
25  my $checksum            = shift;
26  my $edata2              = shift;
27
28  my $k = md4 (encode ("UTF-16LE", $word));
29
30  my $k1 = hmac_md5 ("\x08\x00\x00\x00", $k);
31
32  my $cleartext_ticket = '7981df3081dca01b3019a003020117a112041071e026814da2' .
33  '3f129f0e67a01b73f79aa11c301a3018a003020100a111180f32303138313033303039353' .
34  '831365aa206020460fdc6caa311180f32303337303931343032343830355aa40703050050' .
35  'c10000a511180f32303138313033303039353831365aa611180f323031383130333030393' .
36  '53831365aa711180f32303138313033303139353831365aa811180f323031383130333131' .
37  '30303433385aa90d1b0b545952454c4c2e434f5250aa20301ea003020101a11730151b066' .
38  'b72627467741b0b545952454c4c2e434f5250';
39
40  if (defined $checksum)
41  {
42    $checksum = pack ("H*", $checksum);
43  }
44  else
45  {
46    my $nonce = unpack ("H*", random_bytes (8));
47
48    $cleartext_ticket = $nonce . $cleartext_ticket;
49
50    $checksum = hmac_md5 (pack ("H*", $cleartext_ticket), $k1);
51  }
52
53  my $k3 = hmac_md5 ($checksum, $k1);
54
55  if (defined $edata2)
56  {
57    my $cipher_decrypt = Crypt::RC4->new ($k3);
58
59    my $ticket_decrypt = unpack ("H*", $cipher_decrypt->RC4 (pack ("H*", $edata2)));
60
61    my $check_correct  = ((substr ($ticket_decrypt, 16, 4) eq "7981" && substr ($ticket_decrypt, 22, 2) eq "30")) ||
62                         ((substr ($ticket_decrypt, 16, 2) eq "79") && (substr ($ticket_decrypt, 20, 2) eq "30")) ||
63                         ((substr ($ticket_decrypt, 16, 4) eq "7982")  && (substr ($ticket_decrypt, 24, 2) eq "30"));
64
65    if ($check_correct == 1)
66    {
67      $cleartext_ticket = $ticket_decrypt;
68    }
69    else # validation failed
70    {
71      # fake/wrong ticket (otherwise if we just decrypt/encrypt we end up with false positives all the time)
72      $cleartext_ticket = "0" x (length ($cleartext_ticket) + 16);
73    }
74  }
75
76  my $cipher = Crypt::RC4->new ($k3);
77
78  $edata2 = $cipher->RC4 (pack ("H*", $cleartext_ticket));
79
80  my $tmp_hash = sprintf ('$krb5asrep$23$%s:%s$%s', $user_principal_name, unpack ("H*", $checksum), unpack ("H*", $edata2));
81
82  return $tmp_hash;
83}
84
85sub module_verify_hash
86{
87  my $line = shift;
88
89  my ($hash, $hash2, $word) = split (':', $line);
90
91  return unless defined $hash;
92  return unless defined $hash2;
93  return unless defined $word;
94
95  my @data = split ('\$', $hash);
96
97  return unless scalar @data == 4;
98
99  shift @data;
100
101  my $signature            = shift @data;
102  my $algorithm            = shift @data;
103  my $user_principal_name  = shift @data;
104
105  return unless ($signature eq "krb5asrep");
106
107  my @data2 = split ('\$', $hash2);
108
109  my $checksum             = shift @data2;
110  my $edata2               = shift @data2;
111
112  return unless (length ($checksum) == 32);
113  return unless (length ($edata2) >= 64);
114
115  my $word_packed = pack_if_HEX_notation ($word);
116
117  my $new_hash = module_generate_hash ($word_packed, undef, $user_principal_name, $checksum, $edata2);
118
119  return ($new_hash, $word);
120}
121
1221;
123