AxCrypt v2
#1
Hi everyone, I was wondering if the version 2 of AxCrypt hash will ever be implemented in the future or if someone is actively working on it.

Cheers.
Reply
#2
Now you got me wondering which version of AxCrypt hashcat supports. I'm not that much familiar with the AxCrypt implementation and the version covered.

But this question is kind of contradicting: https://hashcat.net/forum/thread-9434.html


I think if axcrypt2john.py gives you "$axcrypt$*1*" hashes (the 1 is important for the version number), it will be the old AxCrypt 1.

We should probably update the name of the format in the --help etc to reflect this and...

we should probably contact FistOurs from https://github.com/Fist0urs/AxSuite that has helped us implement the already supported AxCrypt hash format.

Could you do these steps:
1. make a github issue to suggest changing "AxCrypt" to "AxCrypt 1" and "AxCrypt in-memory SHA1" to "AxCrypt 1 in-memory SHA1"
2. create a new github issue to ask for AxCrypt 2 support
3. try to contact FistOurs (the author of https://github.com/Fist0urs/AxSuite) and ask them if they can help us find out the differences and maybe implement this new format in hashcat

Thanks
Reply
#3
(08-11-2020, 09:43 AM)philsmd Wrote: Now you got me wondering which version of AxCrypt hashcat supports. I'm not that much familiar with the AxCrypt implementation and the version covered.

But this question is kind of contradicting: https://hashcat.net/forum/thread-9434.html


I think if axcrypt2john.py gives you "$axcrypt$*1*" hashes (the 1 is important for the version number), it will be the old AxCrypt 1.

We should probably update the name of the format in the --help etc to reflect this and...

we should probably contact FistOurs from https://github.com/Fist0urs/AxSuite that has helped us implement the already supported AxCrypt hash format.

Could you do these steps:
1. make a github issue to suggest changing "AxCrypt" to "AxCrypt 1" and "AxCrypt in-memory SHA1" to "AxCrypt 1 in-memory SHA1"
2. create a new github issue to ask for AxCrypt 2 support
3. try to contact FistOurs (the author of https://github.com/Fist0urs/AxSuite) and ask them if they can help us find out the differences and maybe implement this new format in hashcat

Thanks

Thanks for the reply. I will do it by the end of the day!

About axcrypt2john.py you are right. If the the .axx file has been encrypted with the old AxCrypt the hash is "$axcrypt$*1*". In my case I encrypted it with the new AxCrypt so it give me  "$axcrypt$*2*" which is not supported.

Edited: looks like there is no contact email on github for FistOurs.
Reply
#4
it's on the main page: https://github.com/Fist0urs/AxSuite#author
Reply
#5
(08-11-2020, 10:51 AM)philsmd Wrote: it's on the main page: https://github.com/Fist0urs/AxSuite#author

:facepalm:

sorry I'm blind.
Reply
#6
I have a nice update for you.

I just took some free time to have a quick glance on the JTR (John The Ripper) implementation of AxCrypt 2 and it seems the algorithm of AxCrypt 2 is pretty straight forward.

I've now coded a perl POC (proof of concept) axcrypt2.pl to demonstrate how the "cracking" would look like (please don't blame me for that bad code, it's just a quick test):
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");

#
# Example:
#

# 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);

so it's just PBKDF2-HMAC-SHA512 with a 64 byte output and a custom AES unwrapping code.


Some things are still unknown to me... axcrypt2john.py has a comment about AES-256 and that other ciphers could be used too (https://github.com/magnumripper/JohnTheR...ohn.py#L20).

We should really make sure that your target hash uses that AES-256 option and maybe also investigate which other ciphers would be allowed by AxCrypt (except from AES-256).

Could you also mention these new discoveries over https://github.com/hashcat/hashcat/issues/2510 (about the algorithm details ) ?

The github issue that was created by you is https://github.com/hashcat/hashcat/issues/2510 (just as a reference also here for the forum).
Reply
#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
#8
Oh wow. That's a lot of information, very nice research. Big Grin
First of all thanks for the time yous spent and the effort.

Unfortunately my knowledge on the matter are limited but I am more than happy to start researching.

As I mention on the JtR issue, all the tests that I done with different accounts and passwords never gave me a cost lower than 56500. Later today I can try to look into axcrypt2john and the premium version of AxCrypt; cannot promise I will find many answers but I will definitely try. Smile

Meanwhile I will throw the link of this discussion on both the issues!

About the last question, I am not sure about the size of the AxCrypt user-base nor of the people that would actually benefit of such an implementation; I guess whoever will need to code that Hashcat module need to decide if it is really worth it. If that implementation will happen, I will definitely give it a nice ride. Smile
Reply
#9
the cost doesn't matter at all. it will change depending on your hardware etc. That's a feature (the user shouldn't wait "too long" for the software to decrypt the files, but it should still be quite secure... therefore the system the user uses is "benchmarked" and depending on the performance, the cost factor will change probably).

Again, the cost factor is not the problem.

The problem is that JohnTheRipper as mentioned above already, only supports AES-256, while your file is using AES-128 (a different key size and therefore encryption/decryption algorithm !).

JTR doesn't crack it, because it's not yet supported to crack AES-128 files. The test / example from above was indeed meant to use the "Ciaociao123" example to proof this. so it should be quite obvious how to crack *exactly* your file.

Don't get confused by this cost factors, they are NOT the problem. The main decryption algorithm is (JTR only supports AES-256) and we need to find out if AES-128 can be detected from the .axx file metadata (I currently think it can't, the AxCrypt software probably just tries one after the other and as soon as one decrypted the file correctly it is done. If none could open the file, the password is wrong. This is just an assumption, of course).
Reply
#10
Ok, thanks for the clarification. that makes perfectly sense. I see clearly where the problem is now. Also it would be too time consuming to try both ciphers every time a hash need to be cracked.
Reply