AxCrypt v2
#7
Hey lapolis,

while doing a little more research and searching for AxCrypt related topic on github and the JTR repo, I came across your new github issue on the JTR repo: https://github.com/magnumripper/JohnTheR...ssues/4292

I think I know the differences and problem of the 2 different "files".

They probably use 2 different algorithms : AES-128 vs AES-256 (the two different supported ciphers).

According to my little resarch the free version of AxCrypt is able to read AxCrypt V2 files with AES-256 encryption, but it can't create those files by default (only the premium/paid version can, see: https://www.axcrypt.net/pricing/).

I've also opened 2 of those files and it's quite obvious that they use different Algorithms (there is a separate column for that):
[Image: axcrypt-V2-AES128.jpg]
^ this is AES-128 (the default one for the free version, you can't simply upgrade the Algorithm without having the paid version, it seems. "AES-128" is in orange, maybe to highlight that you should upgrade to Premium and that it is not that "secure")

versus the AES-256 test files:
[Image: axcrypt-V2-AES256.jpg]
^ as said, you can open other files like AES-256 files, but can't really create new files with that algo, it seems (only paid version can, even if the source code is available here: https://bitbucket.org/axcryptab/axcrypt-net Wink ).


To highlight the difference again, I will post the 2 different perl scripts (one of course is basically the same as the
above with AES-256 encryption):

axcryptV2AES256.pl
Code:
#!/usr/bin/env perl

# Author: philsmd
# Date: August 2020
# License: public domain, credits go to philsmd and hashcat

# AxCrypt 2 with AES-256 encryption

use warnings;
use strict;

use Crypt::PBKDF2;
use Crypt::Mode::ECB;

#
# Constants:
#

my $AXCRYPT_MAGIC = pack ("H*", "a6a6a6a6a6a6a6a6");

#
# Examples:
#

# Example 1:

# $axcrypt$*2*28200*96fca6b5aff19cb540125e6452f1ab6e1cbf7097ad75326e7aa1726f687ed18e66a63295d735f458a138901be41d95d29ff760c7c4b4178320251f2ab0ecabd8*d1d90c425fd8e2d20b2aa7ef6f3ab9dcefad84917f9e07f65f8da61e7e84f490ba8007179718ce0033cbc887177c2b51ed00e88155741960ab667a5328f305969518bc436ee1ba28126a5be79b0b90f8a9c8a438cba9d0e59c0b6573cde124a8300a6fb01ff857485d302285f1eacd7f08d9cc70ec9fdf412b60bc5a728b5108ef8bda24caee5ab7cc4376951c080c6d*1000*fead8792ea352979ab19e7b287bf709f39df0706a7e460b5271d1ebc71014b3b:openwall

my $pass      = "openwall";
my $iter_wrap = 28200;
my $salt_wrap = pack ("H*", "96fca6b5aff19cb540125e6452f1ab6e1cbf7097ad75326e7aa1726f687ed18e66a63295d735f458a138901be41d95d29ff760c7c4b4178320251f2ab0ecabd8");
my $data      = pack ("H*", "d1d90c425fd8e2d20b2aa7ef6f3ab9dcefad84917f9e07f65f8da61e7e84f490ba8007179718ce0033cbc887177c2b51ed00e88155741960ab667a5328f305969518bc436ee1ba28126a5be79b0b90f8a9c8a438cba9d0e59c0b6573cde124a8300a6fb01ff857485d302285f1eacd7f08d9cc70ec9fdf412b60bc5a728b5108ef8bda24caee5ab7cc4376951c080c6d");
my $iter_kdf = 1000;
my $salt_kdf = pack ("H*", "fead8792ea352979ab19e7b287bf709f39df0706a7e460b5271d1ebc71014b3b");


# Example 2:

# $axcrypt$*2*23652*8817291e0611409390c50a8bdc99a98bf56d393174d7c177082391f7919ed392a701783790ad2eed49139ede2b1546dc4a9aaa142e2b2530b4c82cff03df18b1*2561a0c3f5e166c7273ea6fb59592a58b8d8235a379c82de39fc6d674291b6a25a268732c34eab1b092166ca4f9ef9e383325b90633896890f7dca4d29876ec5bb232ff89c8130aad917f93d9228a091a9996e1f14d4b130b5aad7516c11aa9143730d7b0ec8168b4d83bdd0ca1a9a36784572b9db992bd13289ecd577a421ded0576801e88c7275b2d5fa8eb7e540cb*1000*3dc38e68204a7060c3cd616ffcf6df17dbbb3c199e67ea85c7e29751b9ea2fc2:openwall123

# my $pass      = "openwall123";
# my $iter_wrap = 23652;
# my $salt_wrap = pack ("H*", "8817291e0611409390c50a8bdc99a98bf56d393174d7c177082391f7919ed392a701783790ad2eed49139ede2b1546dc4a9aaa142e2b2530b4c82cff03df18b1");
# my $data      = pack ("H*", "2561a0c3f5e166c7273ea6fb59592a58b8d8235a379c82de39fc6d674291b6a25a268732c34eab1b092166ca4f9ef9e383325b90633896890f7dca4d29876ec5bb232ff89c8130aad917f93d9228a091a9996e1f14d4b130b5aad7516c11aa9143730d7b0ec8168b4d83bdd0ca1a9a36784572b9db992bd13289ecd577a421ded0576801e88c7275b2d5fa8eb7e540cb");
# my $iter_kdf = 1000;
# my $salt_kdf = pack ("H*", "3dc38e68204a7060c3cd616ffcf6df17dbbb3c199e67ea85c7e29751b9ea2fc2");


#
# Start:
#

my $pbkdf2 = Crypt::PBKDF2->new
(
  hasher    => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 512),
  iterations => $iter_kdf,
  output_len => 64
);

# most heavy part (PBKDF2-HMAC-SHA512):

my $KEK = $pbkdf2->PBKDF2 ($salt_kdf, $pass);

# reduce 64 bytes of key to 32 bytes (why not just use 32 byte output length o.O ?)

$KEK = substr ($KEK,  0, 32) ^ substr ($KEK, 32, 32);

$KEK = $KEK ^ substr ($salt_wrap, 0, 32);

my $aes = Crypt::Mode::ECB->new ('AES', 0);


# unwrap:

for (my $j = $iter_wrap - 1; $j >= 0; $j--)
{
  for (my $k = 6; $k >= 1; $k--)
  {
    my $idx = 6 * $j + $k;

    my $block =  substr ($data,      0, 4) .
                (substr ($data,      4, 4) ^ pack ("L>", $idx)) .
                substr ($data, $k * 8, 8);

    $block = $aes->decrypt ($block, $KEK);

    substr ($data,      0, 8) = substr ($block, 0, 8);
    substr ($data, $k * 8, 8) = substr ($block, 8, 8);
  }
}

#
# Verify:
#

if (index ($data, $AXCRYPT_MAGIC) == 0)
{
  print "Password found: '$pass'\n";

  exit (0);
}

exit (1);


axcryptV2AES128.pl
Code:
#!/usr/bin/env perl

# Author: philsmd
# Date: August 2020
# License: public domain, credits go to philsmd and hashcat

# AxCrypt 2 with AES-128 encryption

use warnings;
use strict;

use Crypt::PBKDF2;
use Crypt::Mode::ECB;

#
# Constants:
#

my $AXCRYPT_MAGIC = pack ("H*", "a6a6a6a6a6a6a6a6");

#
# Examples:
#

# Example 1:

# $axcrypt$*2*56500*9b59fe4b3b77cecf72306b01dc946f100212510cd00ff7f73c67b78d82d4918728aaf9eb4ab004f6b66f3685e556ab1dc3dee1030fcb7f130f671a5410b5dc43*78336a566efa4258afb2800021b6f055ccc609a98d0f25e2c3ec35da7967da2e7d32ce1541bf03686f8993400216de4857599bc42a0bb5197a9391f45c496fdbc07a74761813ff6debbc9c66c3010bc88214cdad28f7cc339709fdc45d7b2cd50fb008dac65c7c21e02de0bfb7ffbb08895497c8398df25a388ad1a010a2e0a613af37209bea0ff9b34bd1c6cfb75104*1000*dd451ac8ab2cbf08305d28a8538311db54d546f88a810417005aac3fafc77a22:Ciaociao123

my $pass      = "Ciaociao123";
my $iter_wrap = 56500;
my $salt_wrap = pack ("H*", "9b59fe4b3b77cecf72306b01dc946f100212510cd00ff7f73c67b78d82d4918728aaf9eb4ab004f6b66f3685e556ab1dc3dee1030fcb7f130f671a5410b5dc43");
my $data      = pack ("H*", "78336a566efa4258afb2800021b6f055ccc609a98d0f25e2c3ec35da7967da2e7d32ce1541bf03686f8993400216de4857599bc42a0bb5197a9391f45c496fdbc07a74761813ff6debbc9c66c3010bc88214cdad28f7cc339709fdc45d7b2cd50fb008dac65c7c21e02de0bfb7ffbb08895497c8398df25a388ad1a010a2e0a613af37209bea0ff9b34bd1c6cfb75104");
my $iter_kdf = 1000;
my $salt_kdf = pack ("H*", "dd451ac8ab2cbf08305d28a8538311db54d546f88a810417005aac3fafc77a22");


#
# Start:
#

my $pbkdf2 = Crypt::PBKDF2->new
(
  hasher    => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 512),
  iterations => $iter_kdf,
  output_len => 64
);

# most heavy part (PBKDF2-HMAC-SHA512):

my $KEK = $pbkdf2->PBKDF2 ($salt_kdf, $pass);

# reduce 64 bytes of key to 16 bytes (why not just use 16 byte output length o.O ?)

$KEK = substr ($KEK,  0, 16) ^
      substr ($KEK, 16, 16) ^
      substr ($KEK, 32, 16) ^
      substr ($KEK, 48, 16);

$KEK = $KEK ^ substr ($salt_wrap, 0, 16);

my $aes = Crypt::Mode::ECB->new ('AES', 0);


# unwrap:

for (my $j = $iter_wrap - 1; $j >= 0; $j--)
{
  for (my $k = 4; $k >= 1; $k--)
  {
    my $idx = 4 * $j + $k;

    my $block =  substr ($data,      0, 4) .
                (substr ($data,      4, 4) ^ pack ("L>", $idx)) .
                substr ($data, $k * 8, 8);

    $block = $aes->decrypt ($block, $KEK);

    substr ($data,      0, 8) = substr ($block, 0, 8);
    substr ($data, $k * 8, 8) = substr ($block, 8, 8);
  }
}


#
# Verify:
#

if (index ($data, $AXCRYPT_MAGIC) == 0)
{
  print "Password found: '$pass'\n";

  exit (0);
}

exit (1);

so the difference is actually quite clear... the key length and the unwrapping "iterations" change (while the PBKDF2 with 64 byte output length remains the same).

I've also thought about the quite heavy unwrap code a little bit and I think we would need two major / heavy loops in hashcat with loop () and loop2 () kernel functions (both the PBKDF2 and the thousands of AES decrypts are quite heavy and slow, althrough AES decrypt is normally very fast, it's called very, very often here).

I also wanted to post on the JTR github issue, but I've no time for it now... maybe you can link to this forum post there or I will do during the next days or so...

One question remains... can axcrypt2john.py detect if AES-128 versus AES-256 is used just from the .axx metadata ? I didn't yet look into it (and this is also more or less a thing that axcrypt2john should do if possible, we can't do much here if we only have one JTR hash output from axcrypt2john).

Another important question now is, do we really need to implement all of these algos: 2 different algos for the *new* AxCrypt 2 format : AES-128 vs AES-256. So we would need to keep the old AxCrypt 1 hash types and add 2 further ones ?
Sounds like a lot of work, but might be doable if needed.
Reply


Messages In This Thread
AxCrypt v2 - by lapolis - 08-11-2020, 08:46 AM
RE: AxCrypt v2 - by philsmd - 08-11-2020, 09:43 AM
RE: AxCrypt v2 - by lapolis - 08-11-2020, 10:13 AM
RE: AxCrypt v2 - by philsmd - 08-11-2020, 10:51 AM
RE: AxCrypt v2 - by lapolis - 08-11-2020, 10:56 AM
RE: AxCrypt v2 - by philsmd - 08-11-2020, 02:45 PM
RE: AxCrypt v2 - by philsmd - 08-12-2020, 12:17 AM
RE: AxCrypt v2 - by lapolis - 08-12-2020, 08:32 AM
RE: AxCrypt v2 - by philsmd - 08-12-2020, 09:04 AM
RE: AxCrypt v2 - by lapolis - 08-12-2020, 09:37 AM
RE: AxCrypt v2 - by philsmd - 08-12-2020, 11:51 AM
RE: AxCrypt v2 - by lapolis - 08-12-2020, 12:56 PM
RE: AxCrypt v2 - by TomM - 08-12-2020, 03:57 PM
RE: AxCrypt v2 - by philsmd - 08-12-2020, 05:04 PM
RE: AxCrypt v2 - by lapolis - 08-12-2020, 06:32 PM
RE: AxCrypt v2 - by philsmd - 08-14-2020, 11:06 AM
RE: AxCrypt v2 - by lapolis - 08-15-2020, 05:39 AM
RE: AxCrypt v2 - by philsmd - 08-18-2020, 10:55 AM