openwall / john

John the Ripper jumbo - advanced offline password cracker, which supports hundreds of hash and cipher types, and runs on many operating systems, CPUs, GPUs, and even some FPGAs
https://www.openwall.com/john/
Other
10.32k stars 2.1k forks source link

Truecrypt formats accepts "boot" and twofish variants but can't crack them #1876

Open magnumripper opened 9 years ago

magnumripper commented 9 years ago

From http://hashcat.net/wiki/doku.php?id=example_hashes

These work: http://hashcat.net/misc/example_hashes/hashcat_ripemd160_aes.tc http://hashcat.net/misc/example_hashes/hashcat_sha512_aes.tc http://hashcat.net/misc/example_hashes/hashcat_whirlpool_aes.tc

This doesn't work, I have no idea why. http://hashcat.net/misc/example_hashes/hashcat_ripemd160_aes_boot.tc

These loads (we can't distinguish them from AES) but does not work: http://hashcat.net/misc/example_hashes/hashcat_ripemd160_twofish_boot.tc http://hashcat.net/misc/example_hashes/hashcat_ripemd160_twofish.tc http://hashcat.net/misc/example_hashes/hashcat_sha512_twofish.tc http://hashcat.net/misc/example_hashes/hashcat_whirlpool_twofish.tc

frank-dittrich commented 9 years ago

I think not even truecrypt can distinguish those. It just tries the passphrase with all supported algorithms, until it finds the one that works.

magnumripper commented 9 years ago

Yes but the "boot" case is really bad - we do support RIPEMD160+AES yet we get a false negative.

Also, we should add twofish support. Probably trivial.

magnumripper commented 9 years ago

@jfoug @kholia this is up for grabs - I'm concentrating on the OpenCL version.

kholia commented 7 years ago

I am able to support the boot case using the following hack. The boot mode uses a different iteration count.

diff --git a/run/truecrypt2john.py b/run/truecrypt2john.py
index 6816e91..06a7664 100755
--- a/run/truecrypt2john.py
+++ b/run/truecrypt2john.py
@@ -21,10 +21,10 @@
 import sys
 from os.path import basename
 import binascii
+import optparse

-def process_file(filename, keyfiles):
-
+def process_file(filename, keyfiles, options):
     try:
         f = open(filename, "rb")
     except Exception as e:
@@ -40,8 +40,10 @@ def process_file(filename, keyfiles):
     for tag in ["truecrypt_RIPEMD_160", "truecrypt_SHA_512", "truecrypt_WHIRLPOOL"]:
         sys.stdout.write("%s:%s$" % (basename(filename), tag))
         sys.stdout.write(binascii.hexlify(header))
-        if keyfiles:
+        if keyfiles or options.boot_mode:
             nkeyfiles = len(keyfiles)
+            if options.boot_mode:
+                nkeyfiles = -nkeyfiles
             sys.stdout.write("$%d" % (nkeyfiles))
             for keyfile in keyfiles:
                 sys.stdout.write("$%s" % keyfile)
@@ -71,13 +73,20 @@ def process_file(filename, keyfiles):

 if __name__ == "__main__":
     if len(sys.argv) < 2:
-        sys.stderr.write("Error: No truecrypt volume file specified.\n")
-        sys.stderr.write("\nUtility to import TrueCrypt volume to a format crackeable by John The Ripper\n")
-        sys.stderr.write("\nUsage: %s volume_filename [keyfiles(s)]> output_file\n" % sys.argv[0])
+        sys.stderr.write("Utility to import TrueCrypt volume to a format crackeable by John The Ripper\n")
+        sys.stderr.write("\nUsage: %s [-b] volume_filename [keyfiles(s)]> output_file\n" % sys.argv[0])
+        sys.stderr.write("\nEnable -b only when attacking TrueCrypt's boot mode.\n")
+        sys.stderr.write("\nError: No truecrypt volume file specified.\n")
         sys.exit(-1)

+    parser = optparse.OptionParser()
+    parser.add_option('-b', action="store_true", default=False, dest="boot_mode")
+    options, remainder = parser.parse_args()
+
     keyfiles = []
-    if len(sys.argv) > 2:
-        keyfiles = sys.argv[2:]
+    if len(remainder) > 2:
+        keyfiles = remainder[1:]

-    process_file(sys.argv[1], keyfiles)
+    process_file(remainder[0], keyfiles, options)
diff --git a/src/truecrypt_fmt_plug.c b/src/truecrypt_fmt_plug.c
index 2e6e71d..f70ed98 100644
--- a/src/truecrypt_fmt_plug.c
+++ b/src/truecrypt_fmt_plug.c
@@ -195,7 +195,7 @@ static int valid(char* ciphertext, int pos)
        /* check keyfile(s) */
        p = q + 1;
        nkeyfiles = atoi(p);
-       if (nkeyfiles > MAX_KEYFILES || nkeyfiles < 1)
+       if (nkeyfiles > MAX_KEYFILES) /* hack, zero or negative nkeyfiles values imply boot-mode */
            return 0;
    }

@@ -291,6 +291,11 @@ static void* get_salt(char *ciphertext)
    p = q + 1;
    s->nkeyfiles = atoi(p);

+   if (s->nkeyfiles == 0 || s->nkeyfiles < 0) { // hack to handle boot mode
+       s->nkeyfiles = -s->nkeyfiles;
+       s->num_iterations = 1000;
+   }
+
    for (idx = 0; idx < s->nkeyfiles; idx++) {
        p = strchr(p, '$') + 1; // at first filename
        q = strchr(p, '$');
kholia commented 7 years ago

I think we can have better support for TrueCrypt by combining our three different formats into a single format.

$ hashcat --help
...
   62XY | TrueCrypt                                        | Full-Disk Encryption (FDE)
     X  | 1 = PBKDF2-HMAC-RIPEMD160                        | Full-Disk Encryption (FDE)
     X  | 2 = PBKDF2-HMAC-SHA512                           | Full-Disk Encryption (FDE)
     X  | 3 = PBKDF2-HMAC-Whirlpool                        | Full-Disk Encryption (FDE)
     X  | 4 = PBKDF2-HMAC-RIPEMD160 + boot-mode            | Full-Disk Encryption (FDE)
      Y | 1 = XTS  512 bit pure AES                        | Full-Disk Encryption (FDE)
      Y | 1 = XTS  512 bit pure Serpent                    | Full-Disk Encryption (FDE)
      Y | 1 = XTS  512 bit pure Twofish                    | Full-Disk Encryption (FDE)
      Y | 2 = XTS 1024 bit pure AES                        | Full-Disk Encryption (FDE)
      Y | 2 = XTS 1024 bit pure Serpent                    | Full-Disk Encryption (FDE)
      Y | 2 = XTS 1024 bit pure Twofish                    | Full-Disk Encryption (FDE)
      Y | 2 = XTS 1024 bit cascaded AES-Twofish            | Full-Disk Encryption (FDE)
      Y | 2 = XTS 1024 bit cascaded Serpent-AES            | Full-Disk Encryption (FDE)
      Y | 2 = XTS 1024 bit cascaded Twofish-Serpent        | Full-Disk Encryption (FDE)
      Y | 3 = XTS 1536 bit all                             | Full-Disk Encryption (FDE)

We can do something similar perhaps? We can modify truecrypt2john.py to output hashes based on X and Y values.

frank-dittrich commented 7 years ago

Only slightly OT: Wouldn't it make sense to add a VeraCrypt format? https://www.veracrypt.fr/en/Home.html

Hashcat sems to support VeraCrypt.

kholia commented 7 years ago

I think we have https://github.com/magnumripper/JohnTheRipper/issues/2012 for adding VeraCrypt support.

VeraCrypt disk images are not compatible with TrueCrypt / CipherShed.

kholia commented 7 years ago

TrueCrypt cipher modes,

truecrypt-7-modes

kholia commented 7 years ago

Twofish support is also working now.

https://github.com/kholia/JohnTheRipper/tree/TC has the updated code. I will add support for Serpent XTS shortly.

I believe that supporting the cascaded ciphers in a flexible and user-controlled way will require changes to truecrypt2john.py and the hash format.

To understand this, imagine how the user can specify the cipher mode to attack (single ciphers, double-cascaded ciphers or triple-cascaded ciphers). We have to supply a switch for controlling this to the user.

frank-dittrich commented 7 years ago

One way would be to have different format names which all consider the same hash as valid. Then, picking an algorithm (even cascaded) would be done by the --format switch. Default should be AES. This should work without changing formats.h.

kholia commented 7 years ago

This can work but would involve creation of a large number of format names with a lot of boilerplate code!

A simple hash format change involving an addition of a field (with values 1/2/3) can solve this problem with the existing code we have. When this field has value 1, we only generate a 512-bit key and try pure AES + Twofish + Serpent ciphers. When this field is 2, we generate a 1024-bit key and can attack the double-cascaded ciphers and so on.

jfoug commented 7 years ago

Why would there need to be any core structure format change?? This should all be handled with 1 format, and data driven by the hash string. There would be a field listing how many chained crypts. If there is no chained crypts, then there would be a 1 in this field. Then the fields following the chained count would list what functions are used (with whatever data would be needed for the step) and then if chained, the next set of data, etc, etc.

Then the truecrypt2john would have to either obtain the needed data from the input file, OR provide some mechanism for the user to input this extra data. Then that tool should build the hash string that describes what operations are needed to work on this hash.

I would MUCH rather see something like this, than a set of enumerations like we have in gpg. There would be an enumeration for the encr type, but not a huge enumeration over all of the permutations of encryption types, and the chaining of multiples.

Sounds like a complex monster format, like gpg stuff was with multi algos, but even more complex due to the chaining

frank-dittrich commented 7 years ago

The hash string doesn't have information about the encryption algorithm. Truecrypt tries each implemented algorithm (and the cascades versions) in a sequence until it finds a match.

jfoug commented 7 years ago

So there is nothing that would tell us, what we have? hmm. thats pretty ugly for sure.

roycewilliams commented 7 years ago

Sometimes the user will know what format was used. In that case, the separate field could indicate that. That field could be 0 if the format is unknown, and non-zero if known. It could be a bit field for the other variables. This might mean that truecrypt2john would need to ask the user to supply the format if known.

FWIW, hashcat uses its mode number "namespace" to capture this as follows:

   62XY | TrueCrypt                                        
     X  | 1 = PBKDF2-HMAC-RIPEMD160                        
     X  | 2 = PBKDF2-HMAC-SHA512                           
     X  | 3 = PBKDF2-HMAC-Whirlpool                        
     X  | 4 = PBKDF2-HMAC-RIPEMD160 + boot-mode            
      Y | 1 = XTS  512 bit pure AES                        
      Y | 1 = XTS  512 bit pure Serpent                    
      Y | 1 = XTS  512 bit pure Twofish                    
      Y | 2 = XTS 1024 bit pure AES                        
      Y | 2 = XTS 1024 bit pure Serpent                    
      Y | 2 = XTS 1024 bit pure Twofish                    
      Y | 2 = XTS 1024 bit cascaded AES-Twofish            
      Y | 2 = XTS 1024 bit cascaded Serpent-AES            
      Y | 2 = XTS 1024 bit cascaded Twofish-Serpent        
      Y | 3 = XTS 1536 bit all                             

As you can see, not all combinations are represented - hashcat is trying some of them in sequence.

magnumripper commented 7 years ago

I think we can have better support for TrueCrypt by combining our three different formats into a single format.

Isn't our "tc_aes_xts" format exactly that? It includes the three separate ones: "tc_ripemd160", "tc_sha512" and "tc_whirlpool".

kholia commented 7 years ago

Isn't our "tc_aes_xts" format exactly that? It includes the three separate ones: "tc_ripemd160", "tc_sha512" and "tc_whirlpool".

I missed this completely. Thanks!

My currently plan is to change the hash format slightly. I would like to add a new field (with values 1/2/3). When this field has value 1, we only generate a 512-bit key and try all single ciphers (AES, Twofish, Serpent). When this field is 2, we generate a 1024-bit key and can attack the double-cascaded ciphers (only?) and so on. This needs to be done to provide some control over that attacks tried to the user.

magnumripper commented 7 years ago

When this field has value 1, we only generate a 512-bit key and try all single ciphers (AES, Twofish, Serpent). When this field is 2, we generate a 1024-bit key and can attack the double-cascaded ciphers

There's also (iirc) the possibility to generate 512 bits, try ciphers, then continue generating 512 more bits so we get the 1024 bits for the other ciphers. That's basically a speedup of 33% for trying all ciphers.

kholia commented 7 years ago

Yes, this is very useful when you don't know the cipher mode. When the user knows that a single cipher was used, there is no need to try other cipher modes (due to the heavy speed penalty involved). We have to allow user to specify this stuff in the hash format itself.

solardiz commented 7 years ago

On Sat, Sep 02, 2017 at 09:35:38AM +0000, magnum wrote:

There's also (iirc) the possibility to generate 512 bits, try ciphers, then continue generating bits so we get 1024 bits for the other ciphers.

Yes, but what's the point? It'd only save time for processing the one final correct password, right?

magnumripper commented 7 years ago

Yes, but what's the point? It'd only save time for processing the one final correct password, right?

Not sure what you mean but I edited that post after you quoted it. We have a blob of data and we don't know what algo was used. For each candidate we generate the first 512 bits of key and try the 512-bit ciphers. If none of them worked, we calculate another 512-bits and try the 1024-bit ciphers. So compared to running separate sessions or otherwise not re-using the work, we save a lot of time.

solardiz commented 7 years ago

I did miss the comment edit. The speedup you mention is compared to the case of first computing 512 bits and then computing 1024 bits from scratch, which is obviously not optimal. What I meant is that there's no speedup (except possibly for the very last password tested, in a sequential attack) compared to the much simpler approach of computing 1024 bits right away, before the use of 512 bits, and then using this same derived key (first 512 bits of it, then the full 1024-bit thing) for the two sets of ciphers.

magnumripper commented 7 years ago

Oh, right. Yes, of course we can do all 1024 bits right away, the point is using the one key generation for both sizes.

AlekseyCherepanov commented 2 years ago

BTW in case of changing format of ciphertext and truecrypt2john.py, consider that it is possible to provide precomputed kpool instead of keyfiles in ciphertext.

I cannot say that it would be better for usability: user can enroll any keyfiles to a volume. With explicit names of keyfiles, user can distinguish ciphertexts. But for this purpose, list of filenames could be inserted into GECOS.