Search code examples
pythonhashpbkdf2

Pbkdf2_sha256 encryption


Hello everyone so I'm figuring out how Pbkdf2_sha256 works.

Here are some of the cracked hashes I'm currently studying

PBKDF2 pbkdf2_sha256$10000$005OtPxTXhPq$K/2GplWPJsBVj+qbgdKW8YEteQyUkIiquT5MaOhPo4Y=:harry
PBKDF2 pbkdf2_sha256$10000$00Qhibr5Mbeg$l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY=:radio
PBKDF2 pbkdf2_sha256$10000$00h7h0g1ZKE1$YEobSm/y+cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack=:momo
PBKDF2 pbkdf2_sha256$10000$01JMkfGk1RXh$vD+GGZshw5kExtZOpl5+Lht3xECULdbNVOesoTicxto=:fred
PBKDF2 pbkdf2_sha256$10000$01vkw1viCg4J$2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0=:get
PBKDF2 pbkdf2_sha256$10000$01wayF5JLVSZ$2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs=:987456321

And here is my code and output for calculating the hash

>>> from passlib.hash import pbkdf2_sha256
>>> from passlib.utils.binary import ab64_decode
>>> print(pbkdf2_sha256.hash("harry", rounds=10000, salt=ab64_decode(b'005OtPxTXhPq')))
$pbkdf2-sha256$10000$005OtPxTXhPq$l9LhRMPBW.EEdlBE9b.P0Z70Kxidl9EJhfGK7FiLUHA

Comparing these two and you can see a difference.

$pbkdf2_sha256$10000$005OtPxTXhPq$K/2GplWPJsBVj+qbgdKW8YEteQyUkIiquT5MaOhPo4Y=
$pbkdf2-sha256$10000$005OtPxTXhPq$l9LhRMPBW.EEdlBE9b.P0Z70Kxidl9EJhfGK7FiLUHA

Can someone please explain what causes this and how can I calculate the correct hash?

Thanks in advance!


Solution

  • As already mentioned in the comment, the posted data has a format different from passlib: The passlib format is explained here. Salt and hash (checksum) are Base64 encoded. A special Base64 variant is used that is explained here: Padding (=) and whitespaces are omitted and . is applied instead +.

    The hash of the posted data on the other hand is standard Base64 encoded (i.e. with + instead of .) and with padding (=). Furthermore the salt is UTF8 decoded.

    If this is taken into account the salts and hashes are identical. The following code determines the passlib data from the posted data and compares salt and hash, where salt and hash of the posted data are displayed in passlib format (i.e. with the passlib Base64 variant and Base64 encoded salt):

    from passlib.hash import pbkdf2_sha256
    from base64 import b64decode
    from passlib.utils.binary import ab64_encode
    
    def hashAndCompare(crackedHash):
        
        crackedChain = crackedHash.split('$')   
        #crackedChainDigest = crackedChain[0]
        crackedChainRounds = crackedChain[1]
        crackedChainSalt = crackedChain[2]
        crackedChainSaltPasslibFormat = ab64_encode(crackedChainSalt.encode('utf8')).decode('utf8')
        crackedChainHashData = crackedChain[3].split(':')
        crackedChainHash = crackedChainHashData[0]
        crackedChainHashPasslibFormat = ab64_encode(b64decode(crackedChainHash)).decode('utf8')
        crackedChainData = crackedChainHashData[1]
        
        passlibHash = pbkdf2_sha256.hash(crackedChainData, rounds=crackedChainRounds, salt=crackedChainSalt.encode('utf8')) 
        passlibChain = passlibHash.split('$')
        passlibChainSalt = passlibChain[3]
        passlibChainHash = passlibChain[4]
        
        print('Passlib: Hash: {0} Salt: {1}\nCracked: Hash: {2} Salt: {3}\n'.format(passlibChainHash, passlibChainSalt, crackedChainHashPasslibFormat, crackedChainSaltPasslibFormat))
    
    hashAndCompare('pbkdf2_sha256$10000$005OtPxTXhPq$K/2GplWPJsBVj+qbgdKW8YEteQyUkIiquT5MaOhPo4Y=:harry')
    hashAndCompare('pbkdf2_sha256$10000$00Qhibr5Mbeg$l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY=:radio')
    hashAndCompare('pbkdf2_sha256$10000$00h7h0g1ZKE1$YEobSm/y+cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack=:momo')
    hashAndCompare('pbkdf2_sha256$10000$01JMkfGk1RXh$vD+GGZshw5kExtZOpl5+Lht3xECULdbNVOesoTicxto=:fred')
    hashAndCompare('pbkdf2_sha256$10000$01vkw1viCg4J$2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0=:get')
    hashAndCompare('pbkdf2_sha256$10000$01wayF5JLVSZ$2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs=:987456321')
    

    Salts and hashes are identical with consistent encoding:

    Passlib: Hash: K/2GplWPJsBVj.qbgdKW8YEteQyUkIiquT5MaOhPo4Y Salt: MDA1T3RQeFRYaFBx
    Cracked: Hash: K/2GplWPJsBVj.qbgdKW8YEteQyUkIiquT5MaOhPo4Y Salt: MDA1T3RQeFRYaFBx
    
    Passlib: Hash: l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY Salt: MDBRaGlicjVNYmVn
    Cracked: Hash: l9grYueDrl3qN3NA7e9j5PodgV1XkGTz0Z6ajhF99AY Salt: MDBRaGlicjVNYmVn
    
    Passlib: Hash: YEobSm/y.cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack Salt: MDBoN2gwZzFaS0Ux
    Cracked: Hash: YEobSm/y.cFg/VXhU4gGYJ6eOkZ68jhJ5axDu68Dack Salt: MDBoN2gwZzFaS0Ux
    
    Passlib: Hash: vD.GGZshw5kExtZOpl5.Lht3xECULdbNVOesoTicxto Salt: MDFKTWtmR2sxUlho
    Cracked: Hash: vD.GGZshw5kExtZOpl5.Lht3xECULdbNVOesoTicxto Salt: MDFKTWtmR2sxUlho
    
    Passlib: Hash: 2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0 Salt: MDF2a3cxdmlDZzRK
    Cracked: Hash: 2hjlbq10Jh/Su3yqjKfYCnCSt1WlKcKJtsqDET618M0 Salt: MDF2a3cxdmlDZzRK
    
    Passlib: Hash: 2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs Salt: MDF3YXlGNUpMVlNa
    Cracked: Hash: 2/9COWqb6SZG/raqabtU8fNBzkrt2puN7SaKw0U7jBs Salt: MDF3YXlGNUpMVlNa