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
The hash values from the linked example can be reproduced if the following is considered in addition to the information given there:
$8$<random 14 bytes salt>$<Base64 encoded PBKDF2 hash>
./0-9A-Za-z
as Base64 alphabetWhile 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).