How to Generate Cisco “enable secret” Password Hashes Programmatically


3 views

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:

  1. The "5" indicates the hash type (MD5-based)
  2. The "$1$" prefix identifies it as MD5 crypt
  3. The "salt" is an 8-character random string
  4. 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:

  1. Accept plaintext passwords from secure sources (vaults, environment variables)
  2. Generate the hashes at configuration time
  3. Never store the plaintext passwords
  4. 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:

  1. Type identifier (5 for MD5)
  2. Salt (8 characters)
  3. 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:

  1. Generate random salts for each device
  2. Support multiple hash types for compatibility
  3. 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}"