Help to recover password from an unknown hash type
#1
Hi All,

Is this possible to crack if I only have the $encrypted_password / hash? How should I go about doing this. Any help or point me to the right direction is much appreciated.

Thanks.

Code:
// validate password function
function validatePassword(
    $username, /* username string */
    $cleartext_password, /* clear text password that's supplied by user */
    $encrypted_password /* encrypted password that's stored in the database */
    ) {

    // build a key based on supplied username and clear text password
    $key = trim($username).trim($cleartext_password);

    // decrypt the encrypted password based on the above key
    $decrypted_password = decrypt($encrypted_password, $key, 'twofish', 'cfb');

    // if both string matches, password is correct
    if ($decrypted_password == $cleartext_password) return true;

    // if both string does not match, password is wrong
    return false;
}

Best regards,
Azren[/php]
#2
hashcat doesn't support cracking encrypted passwords, only hashed passwords and non-hash formats which use a kdf.

since you have access to the source you should also have access to $key, so you don't actually need to crack anything here.
#3
epixoip,

Thank you. So I guess this is bad security as compromise of the source code and db could lead to recovery of the plain text password. Should use crypto instead of encryption.

Thanks again.

Best regards,
Azren
#4
Well no, "crypto" is short for "cryptography."

By the way, your title is wrong. The hash type is not unknown in your example, it's simply nonexistent. The application you have here is simply using Twofish to encrypt each password, there's no hashing at all.

But yes, this is an absolutely terrible way to store passwords. If an attacker has access to the database they almost always also have access to the encryption key. So instead of cracking each password individually, they can simply steal the key and decrypt the entire database. It's essentially no different than storing them in plaintext.

This is why we never encrypt passwords;, we always hash them, using a password-hashing function such as bcrypt.
#5
Azren, I think epixoip missed this line:
Code:
$key = trim($username).trim($cleartext_password);

This is a very weird construction it's encrypting the password with the password. You can think of this like the LM hash. It would be interesting to know what the decrypt function does to the key. I assume SHA256 (or some other hash) or padding. Also if you can find the "encrypt" function just to make sure the IV isn't stupid, but this might be apparent from the decrypt function (ie null or fixed IV). Nice thing is it leaks the password length. So cracking can go much faster by dropping incorrect length passwords.

Anyway this is probably not a common hash so it would be unlikely for Atom to add it.
#6
(01-06-2015, 09:49 AM)Sc00bz Wrote: Azren, I think epixoip missed this line:
Code:
$key = trim($username).trim($cleartext_password);

This is a very weird construction it's encrypting the password with the password. You can think of this like the LM hash. It would be interesting to know what the decrypt function does to the key. I assume SHA256 (or some other hash) or padding. Also if you can find the "encrypt" function just to make sure the IV isn't stupid, but this might be apparent from the decrypt function (ie null or fixed IV). Nice thing is it leaks the password length. So cracking can go much faster by dropping incorrect length passwords.

Anyway this is probably not a common hash so it would be unlikely for Atom to add it.

This is the whole functions if you're interested to know... Please fire at will

Code:
function setCipherModeKey($cipher, $mode, $encryptkey) {
    $key = '';
    if (!empty($encryptkey)) {
        // get the size of the encryption key
        $keysize = mcrypt_get_key_size ($cipher, $mode);

        // if the encryption key is less than 32 characters long and the expected keysize is at least 32 md5 the key
        if ((strlen($encryptkey) < 32) && ($keysize >= 32)) {
            $encryptkey = md5($encryptkey);
        // if encryption key is longer than $keysize and the keysize is 32 then md5 the encryption key
        } elseif ((strlen($encryptkey) > $keysize) && ($keysize == 32)) {
            $encryptkey = md5($encryptkey);
        } else {
            if ($keysize > strlen($encryptkey)) {
                // if encryption key is shorter than the keysize, strpad it with space
                $encryptkey = str_pad($encryptkey, $keysize);
            } else {
                // if encryption key is longer than the keysize substr it to the correct keysize length
                $encryptkey = substr($encryptkey, 0, $keysize);
            }
        }
        $key = $encryptkey;
    }
    return $key;
}

function decrypt($encrypted, $key, $cipher, $mode) {
    if (0 == strlen($encrypted)) return '';  //nothing to decrpyt or encrypt....

    $key = setCipherModeKey($cipher, $mode, $key);     // set key

    // extract encrypted value from base64 encoded value
    $data = base64_decode($encrypted);

    // open encryption module
    $td = @mcrypt_module_open($cipher, '', $mode, '');

    // get what size the IV should be
    $ivsize = mcrypt_enc_get_iv_size($td);

    // get the IV from the encrypted string
    $iv = substr($data, 0, $ivsize);

    // remove the IV from the data so we decrypt cleanly
    $data = substr($data, $ivsize);

    // initialize decryption
    @mcrypt_generic_init ($td, $key, $iv);

    // decrypt the data
    $decrypted = mdecrypt_generic ($td, $data);

    // cleanup
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    // get rid of original data
    unset($data);

    return $decrypted;
}

Best regards,
Azren
#7
Lame I was hoping this would be 16 but it's 32:
Code:
echo mcrypt_get_key_size('twofish', 'cfb');

So the key is the MD5 of the username and password (the key is in hex).

I'm just going to guess the IV is random since it is prepended to the encrypted password... but they are dumb enough to think that MD5, a 128 bit hash, is the proper size for a 32 byte (256 bits) key.

If you want to, the "change password/create password" function is where you would find the IV creation. Which let's hope is something like "$iv = substr(md5($password), 0, 16)" Smile
#8
(01-06-2015, 09:49 AM)Sc00bz Wrote: Azren, I think epixoip missed this line:
Code:
$key = trim($username).trim($cleartext_password);

This is a very weird construction it's encrypting the password with the password. You can think of this like the LM hash. It would be interesting to know what the decrypt function does to the key. I assume SHA256 (or some other hash) or padding. Also if you can find the "encrypt" function just to make sure the IV isn't stupid, but this might be apparent from the decrypt function (ie null or fixed IV). Nice thing is it leaks the password length. So cracking can go much faster by dropping incorrect length passwords.

Anyway this is probably not a common hash so it would be unlikely for Atom to add it.

Ha, yeah, you're right, I totally missed that line. So it is indeed encrypting the password with the password.

It does some weird shit to the key. If the keysize is >= 32 chars and the key is not exactly 32 chars, it hashes it with md5. Doesn't seem to do any padding at all if keysize is > 32 and it hashes it with md5. If keysize is < 32 then it either pads it with spaces or truncates the key. Which is sweet if you have a really long username Wink

Looks like the IV is stored in the database as part of the "hash" string.

This is some ridiculously goofy code. What is this from??
#9
(01-06-2015, 10:35 AM)Sc00bz Wrote: If you want to, the "change password/create password" function is where you would find the IV creation. Which let's hope is something like "$iv = substr(md5($password), 0, 16)" Smile

I'll check that out when i get back home.
#10
[/quote]
This is some ridiculously goofy code. What is this from??
[/quote]

This is from an in house web application where i'm reviewing.