diff --git a/src/modules/module_27500.c b/src/modules/module_27500.c index a156fac92..e2e002844 100644 --- a/src/modules/module_27500.c +++ b/src/modules/module_27500.c @@ -10,6 +10,7 @@ #include "convert.h" #include "shared.h" #include "memory.h" +#include "emu_inc_hash_md5.h" static const u32 ATTACK_EXEC = ATTACK_EXEC_OUTSIDE_KERNEL; static const u32 DGST_POS0 = 0; @@ -196,6 +197,20 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE vbox->salt1_len = salt1_len / 2; + // handle unique salts detection + + md5_ctx_t md5_ctx; + + md5_init (&md5_ctx); + md5_update (&md5_ctx, vbox->salt1_buf, vbox->salt1_len); + md5_final (&md5_ctx); + + // store md5(vbox->salt1_buf) in salt_buf + + salt->salt_len = 16; + + memcpy (salt->salt_buf, md5_ctx.h, salt->salt_len); + // aes xts key len (128 or 256) const u8 *aes_key_len_pos = token.buf[3]; diff --git a/src/modules/module_27600.c b/src/modules/module_27600.c index 5e11a9be9..933236e9c 100644 --- a/src/modules/module_27600.c +++ b/src/modules/module_27600.c @@ -10,6 +10,7 @@ #include "convert.h" #include "shared.h" #include "memory.h" +#include "emu_inc_hash_md5.h" static const u32 ATTACK_EXEC = ATTACK_EXEC_OUTSIDE_KERNEL; static const u32 DGST_POS0 = 0; @@ -196,6 +197,20 @@ int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE vbox->salt1_len = salt1_len / 2; + // handle unique salts detection + + md5_ctx_t md5_ctx; + + md5_init (&md5_ctx); + md5_update (&md5_ctx, vbox->salt1_buf, vbox->salt1_len); + md5_final (&md5_ctx); + + // store md5(vbox->salt1_buf) in salt_buf + + salt->salt_len = 16; + + memcpy (salt->salt_buf, md5_ctx.h, salt->salt_len); + // aes xts key len (128 or 256) const u8 *aes_key_len_pos = token.buf[3]; diff --git a/tools/test_modules/m27500.pm b/tools/test_modules/m27500.pm new file mode 100644 index 000000000..7c1269e40 --- /dev/null +++ b/tools/test_modules/m27500.pm @@ -0,0 +1,134 @@ +#!/usr/bin/env perl + +## +## Author......: See docs/credits.txt +## License.....: MIT +## + +use strict; +use warnings; + +use Crypt::PBKDF2; +use MIME::Base64 qw (encode_base64 decode_base64); +use Encode; + +sub module_constraints { [[0, 256], [64, 64], [-1, -1], [-1, -1], [-1, -1]] } + +sub aes_decrypt +{ + my $key_main = shift; + my $key_tweak = shift; + my $data = shift; + + my $python_code = <<'END_CODE'; + +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.backends import default_backend +import base64 + +key = base64.b64decode (key_main) +tweak = base64.b64decode (key_tweak) + +cipher = Cipher(algorithms.AES(key), modes.XTS(tweak), backend=default_backend()) +decryptor = cipher.decryptor() + +decrypted = decryptor.update(base64.b64decode (data)[0:32]) +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"/; + + my $output_buf = `python3 -c '$python_code'`; + + $output_buf =~ s/[\r\n]//g; + + $output_buf = pack ("H*", $output_buf); + + return $output_buf; +} + +sub module_generate_hash +{ + my $word = shift; + my $salt1 = shift; + my $iter1 = shift // 260000; + my $enc_pass = shift // random_hex_string (64); + my $salt2 = shift // random_hex_string (64); + my $iter2 = shift // 20000; + + my $pbkdf2 = Crypt::PBKDF2->new + ( + hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256), + iterations => $iter1, + output_len => 32 + ); + + my $salt1_bin = pack ("H*", $salt1); + + my $key = $pbkdf2->PBKDF2 ($salt1_bin, $word); + + my $tweak = "\x00" x 16; + + my $key_b64 = encode_base64 ($key, ""); + my $tweak_b64 = encode_base64 ($tweak, ""); + my $enc_pass_b64 = encode_base64 (pack ("H*", $enc_pass), ""); + + my $dec_pass = aes_decrypt ($key_b64, $tweak_b64, $enc_pass_b64); + + $pbkdf2 = Crypt::PBKDF2->new + ( + hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256), + iterations => $iter2, + output_len => 32 + ); + + my $salt2_bin = pack ("H*", $salt2); + + my $hash_buf = $pbkdf2->PBKDF2 ($salt2_bin, $dec_pass); + + my $hash = sprintf ("\$vbox\$0\$%s\$%s\$8\$%s\$%s\$%s\$%s", $iter1, $salt1, $enc_pass, $iter2, $salt2, unpack ("H*", $hash_buf)); + + return $hash; +} + +sub module_verify_hash +{ + my $line = shift; + + my $idx = index ($line, ':'); + + return unless $idx >= 0; + + my $hash = substr ($line, 0, $idx); + my $word = substr ($line, $idx + 1); + + return unless substr ($hash, 0, 6) eq '$vbox$'; + + my (undef, $signature, $version, $iter1, $salt1, $klen, $enc_pass, $iter2, $salt2) = split '\$', $hash; + + return unless defined $signature; + return unless defined $version; + return unless defined $iter1; + return unless defined $salt1; + return unless defined $klen; + return unless defined $enc_pass; + return unless defined $iter2; + return unless defined $salt2; + + return unless ($version eq "0"); + return unless ($klen eq "8"); + + my $word_packed = pack_if_HEX_notation ($word); + + my $new_hash = module_generate_hash ($word, $salt1, $iter1, $enc_pass, $salt2, $iter2); + + return ($new_hash, $word); +} + +1; diff --git a/tools/test_modules/m27600.pm b/tools/test_modules/m27600.pm new file mode 100644 index 000000000..a74516aac --- /dev/null +++ b/tools/test_modules/m27600.pm @@ -0,0 +1,136 @@ +#!/usr/bin/env perl + +## +## Author......: See docs/credits.txt +## License.....: MIT +## + +use strict; +use warnings; + +use Crypt::PBKDF2; +use MIME::Base64 qw (encode_base64 decode_base64); +use Encode; + +sub module_constraints { [[0, 256], [64, 64], [-1, -1], [-1, -1], [-1, -1]] } + +sub aes_decrypt +{ + my $key_main = shift; + my $key_tweak = shift; + my $data = shift; + + my $python_code = <<'END_CODE'; + +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.backends import default_backend +import base64 + +key = base64.b64decode (key_main) +tweak = base64.b64decode (key_tweak) + +cipher = Cipher(algorithms.AES(key), modes.XTS(tweak), backend=default_backend()) +decryptor = cipher.decryptor() + +decrypted = decryptor.update(base64.b64decode (data)[0:64]) +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"/; + + my $output_buf = `python3 -c '$python_code'`; + + $output_buf =~ s/[\r\n]//g; + + $output_buf = pack ("H*", $output_buf); + + return $output_buf; +} + +sub module_generate_hash +{ + my $word = shift; + my $salt1 = shift; + my $iter1 = shift // 160000; + my $enc_pass = shift // random_hex_string (128); + my $salt2 = shift // random_hex_string (64); + my $iter2 = shift // 20000; + + my $pbkdf2 = Crypt::PBKDF2->new + ( + hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256), + iterations => $iter1, + output_len => 64 + ); + + my $salt1_bin = pack ("H*", $salt1); + + my $key = $pbkdf2->PBKDF2 ($salt1_bin, $word); + + my $tweak = "\x00" x 16; + + my $key_b64 = encode_base64 ($key, ""); + my $tweak_b64 = encode_base64 ($tweak, ""); + my $enc_pass_b64 = encode_base64 (pack ("H*", $enc_pass), ""); + + my $dec_pass = aes_decrypt ($key_b64, $tweak_b64, $enc_pass_b64); + + $pbkdf2 = Crypt::PBKDF2->new + ( + hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256), + iterations => $iter2, + output_len => 32 + ); + + my $salt2_bin = pack ("H*", $salt2); + + my $hash_buf = $pbkdf2->PBKDF2 ($salt2_bin, $dec_pass); + + my $hash = sprintf ("\$vbox\$0\$%s\$%s\$16\$%s\$%s\$%s\$%s", $iter1, $salt1, $enc_pass, $iter2, $salt2, unpack ("H*", $hash_buf)); + +# print "out: ", $hash, "\n"; + + return $hash; +} + +sub module_verify_hash +{ + my $line = shift; + + my $idx = index ($line, ':'); + + return unless $idx >= 0; + + my $hash = substr ($line, 0, $idx); + my $word = substr ($line, $idx + 1); + + return unless substr ($hash, 0, 6) eq '$vbox$'; + + my (undef, $signature, $version, $iter1, $salt1, $klen, $enc_pass, $iter2, $salt2) = split '\$', $hash; + + return unless defined $signature; + return unless defined $version; + return unless defined $iter1; + return unless defined $salt1; + return unless defined $klen; + return unless defined $enc_pass; + return unless defined $iter2; + return unless defined $salt2; + + return unless ($version eq "0"); + return unless ($klen eq "16"); + + my $word_packed = pack_if_HEX_notation ($word); + + my $new_hash = module_generate_hash ($word, $salt1, $iter1, $enc_pass, $salt2, $iter2); + + return ($new_hash, $word); +} + +1;