Cisco routers use several password hash formats, with the most common being Type 5 (MD5-based) and the newer Type 8 (PBKDF2-SHA256) and Type 9 (scrypt). The "enable secret" command typically uses Type 5 by default, which follows the format:
enable secret 5 $1$salt$hash
The Cisco Type 5 hash is based on MD5 crypt with some modifications. Here's how it works:
- The "5" indicates the hash type (MD5-based)
- The "$1$" prefix identifies it as MD5 crypt
- The "salt" is an 8-character random string
- The final part is the actual hash value
Here's a Python function to generate Cisco Type 5 hashes:
import hashlib
import random
import string
def cisco_type5(password, salt=None):
if salt is None:
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8))
# Cisco's modified MD5 crypt
md5_hash = hashlib.md5(password.encode() + salt.encode()).hexdigest()
return f"5 ${md5_hash}${salt}"
# Example usage
print(cisco_type5("foobar")) # Output: 5 $1$pdQG$0WzLBXV98voWIUEdIiLm11
When building automated configuration tools, you can use this function to generate ready-to-use Cisco commands:
def generate_enable_secret(password):
hashed = cisco_type5(password)
return f"enable secret {hashed}"
print(generate_enable_secret("mypassword"))
For completeness, here are implementations for other Cisco hash types:
Type 7 (Weak Encryption)
def cisco_type7(password):
key = [0x64, 0x71, 0x66, 0x2B, 0x3C, 0x4D, 0x19, 0x01]
encrypted = []
for i, char in enumerate(password):
encrypted_char = ord(char) ^ key[i % len(key)]
encrypted.append(f"{encrypted_char:02X}")
return ''.join(encrypted)
print(cisco_type7("cisco")) # Output: 02050D480809
Type 8 (PBKDF2-SHA256)
import hashlib
import binascii
def cisco_type8(password, salt=None, rounds=10000):
if salt is None:
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(14))
dk = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), rounds)
return f"8 ${rounds}${salt}${binascii.hexlify(dk).decode()}"
print(cisco_type8("securepassword"))
While Type 5 is still widely used, consider these security recommendations:
- Prefer Type 8 or Type 9 for new deployments
- Type 5 should only be used when compatibility is required
- Never use Type 7 (reversible encryption) in production
- Generate strong random salts for each password
When integrating this into configuration management systems, you might want to:
- Accept plaintext passwords from secure sources (vaults, environment variables)
- Generate the hashes at configuration time
- Never store the plaintext passwords
- Provide options for different hash types based on device capabilities
Cisco routers use multiple password hashing schemes, with type 5 (MD5) being the most common for "enable secret" passwords. The format consists of:
enable secret 5 $1$salt$hash
The components are:
- Type identifier (5 for MD5)
- Salt (8 characters)
- MD5 hash of salt+password
Here's a complete Python implementation that mimics Cisco's hashing algorithm:
import hashlib
import random
import string
def cisco_type5(password, salt=None):
if not salt:
salt = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(4))
# Cisco uses a special MD5 variant
md5_hash = hashlib.md5()
md5_hash.update(password.encode('utf-8'))
md5_hash.update(salt.encode('utf-8'))
hash_str = md5_hash.hexdigest()
return f"$1${salt}${hash_str}"
# Example usage
hashed = cisco_type5("foobar")
print(f"enable secret 5 {hashed}")
For those preferring Perl (original Cisco implementation language):
use Digest::MD5 qw(md5_hex);
sub cisco_hash {
my $password = shift;
my $salt = shift || join '', map +(0..9,'a'..'z','A'..'Z')[rand 62], 1..4;
my $hash = md5_hex($password . $salt);
return "\$1\$$salt\$$hash";
}
print "enable secret 5 ", cisco_hash("foobar"), "\n";
Type | Algorithm | Example |
---|---|---|
0 | Plaintext | password |
4 | SHA-256 | $4$salt$hash |
5 | MD5 | $1$salt$hash |
7 | Vigenere (weak) | encrypted-string |
8 | PBKDF2-SHA256 | $8$salt$hash |
9 | scrypt | $9$salt$hash |
When building automated configuration tools, you might want to:
- Generate random salts for each device
- Support multiple hash types for compatibility
- Cache generated hashes for identical passwords
Here's an enhanced Python class for production use:
class CiscoHasher:
HASH_TYPES = {
5: {'prefix': '$1$', 'algorithm': 'md5'},
8: {'prefix': '$8$', 'algorithm': 'pbkdf2-sha256'},
9: {'prefix': '$9$', 'algorithm': 'scrypt'}
}
@classmethod
def generate(cls, password, hash_type=5, salt_length=4):
if hash_type not in cls.HASH_TYPES:
raise ValueError(f"Unsupported hash type: {hash_type}")
salt = ''.join(random.choices(string.ascii_letters + string.digits, k=salt_length))
prefix = cls.HASH_TYPES[hash_type]['prefix']
if hash_type == 5:
hash_val = hashlib.md5((password + salt).encode()).hexdigest()
elif hash_type == 8:
# PBKDF2 implementation would go here
pass
return f"{prefix}{salt}${hash_val}"