encryptionsha256password-encryptioncisco

How to encode a Cisco Type 8 secret without a cisco device using python


I was wondering if it is possible to generate the hashed secret shown in the CISCO documentation using a python script, without a CISCO device.

https://learningnetwork.cisco.com/s/article/cisco-routers-password-types

Example :

R1(config)# username yasser algorithm-type sha256 secret cisco

R1# show running-config | inc username

username yasser secret 8 $8$dsYGNam3K1SIJO$7nv/35M/qr6t.dVc7UY9zrJDWRVqncHub1PE9UlMQFs

Solution

  • The hash values from the linked example can be reproduced if the following is considered in addition to the information given there:

    While the iteration count is described here, the others are more or less educated guesses (e.g. ./0-9A-Za-z is a common variant with the letter .), which are eventually confirmed by the successful tests.

    A possible Python implementation is:

    import hashlib
    import base64
    
    STD_B64_ALPHABET   = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    CISCO_B64_ALPHABET = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
    TRANS = str.maketrans(STD_B64_ALPHABET, CISCO_B64_ALPHABET)
    
    def b64CiscoEncode(data):
        return base64.b64encode(data).decode('ascii').translate(TRANS).rstrip('=')
    
    def type8Hash(salt, password):   
        return b64CiscoEncode(hashlib.pbkdf2_hmac('sha256', password, salt, 20000))
    
    # $8$mTj4RZG8N9ZDOk$elY/asfm8kD3iDmkBe3hD2r4xcA/0oWS5V3os.O91u.
    print(type8Hash(b'mTj4RZG8N9ZDOk', b'cisco')) # elY/asfm8kD3iDmkBe3hD2r4xcA/0oWS5V3os.O91u.
    
    # $8$dsYGNam3K1SIJO$7nv/35M/qr6t.dVc7UY9zrJDWRVqncHub1PE9UlMQFs
    print(type8Hash(b'dsYGNam3K1SIJO', b'cisco')) # 7nv/35M/qr6t.dVc7UY9zrJDWRVqncHub1PE9UlMQFs
    

    which successfully reproduces the hashes from the linked example.


    Edit:

    Concerning your comment: The salt is produced by randomly generating 10 bytes (= 80 bits) that are Base64 encoded (using the Cisco alphabet), resulting in 14 bytes. The type 8 hashed password is the concatenation of 8-prefix, salt and Base64 encoded PBKDF2 hash:

    import hashlib
    import base64
    import os
    
    salt = b64CiscoEncode(os.urandom(10)) # create random 10 bytes (=80 bits) salt and Base64 encode
    password = b'cisco'
    hash = type8Hash(salt.encode('ascii'), password) # calculate Base64 encoded PBKDF2 hash 
    
    print("$8$%s$%s" %(salt, hash)) # concatenate 8 prefix, salt and Base64 encoded PBKDF2 hash  
    

    If the hash is to be reconstructed to an existing type 8 hashed password, simply use the corresponding salt instead of the freshly generated salt (as in the first part of the answer).