Skip to content

Instantly share code, notes, and snippets.

@rotarydrone
Created February 19, 2025 00:17
Show Gist options
  • Save rotarydrone/bbed1d69e0af76ae143a149deee062a0 to your computer and use it in GitHub Desktop.
Save rotarydrone/bbed1d69e0af76ae143a149deee062a0 to your computer and use it in GitHub Desktop.
Generate Kerberos Keys from Password
#!/usr/bin/env python3
"""
Original: https://snovvcrash.rocks/2021/05/21/calculating-kerberos-keys.html
Kerberos Key Calculator - Generates Kerberos encryption keys for machine and user accounts.
Uses multiple encryption types including RC4-HMAC-NT, AES128-HMAC, and AES256-HMAC.
"""
import argparse
from binascii import unhexlify, hexlify
from typing import Dict, Union
from impacket.krb5 import constants
from impacket.krb5.crypto import Key, string_to_key
from Cryptodome.Hash import MD4
CIPHER_TYPES: Dict[str, int] = {
'rc4_hmac_nt': int(constants.EncryptionTypes.rc4_hmac.value),
'aes128_hmac': int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value),
'aes256_hmac': int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value)
}
def generate_kerberos_keys(password: bytes, salt: bytes) -> Dict[str, str]:
"""
Generate Kerberos encryption keys for all supported cipher types.
Args:
password: Raw password bytes
salt: Salt value for key generation
Returns:
Dictionary mapping cipher names to their hexadecimal key values
"""
keys = {}
for name, cipher in CIPHER_TYPES.items():
if cipher == 23: # RC4-HMAC-NT
md4 = MD4.new()
md4.update(password)
key = Key(cipher, md4.digest())
else: # AES types
fixed_password = password.decode('utf-16-le', 'replace').encode('utf-8', 'replace')
key = string_to_key(cipher, fixed_password, salt)
keys[name] = hexlify(key.contents).decode('utf-8')
return keys
def process_machine_account(domain: str, hostname: str, hex_password: str) -> Dict[str, Union[str, Dict[str, str]]]:
"""
Generate Kerberos keys for a machine account.
Args:
domain: Domain name
hostname: Machine hostname
hex_password: Machine password in hexadecimal format
Returns:
Dictionary containing account info and generated keys
"""
salt = f'{domain.upper()}host{hostname.lower()}.{domain.lower()}'.encode('utf-8')
raw_password = unhexlify(hex_password)
return {
'account': f'{domain.upper()}\\{hostname.upper()}$',
'salt': salt.decode('utf-8'),
'keys': generate_kerberos_keys(raw_password, salt)
}
def process_user_account(domain: str, username: str, password: str) -> Dict[str, Union[str, Dict[str, str]]]:
"""
Generate Kerberos keys for a user account.
Args:
domain: Domain name
username: Username
password: Clear-text password
Returns:
Dictionary containing account info and generated keys
"""
salt = f'{domain.upper()}{username}'.encode('utf-8')
raw_password = password.encode('utf-16-le')
return {
'account': f'{domain.upper()}\\{username}',
'salt': salt.decode('utf-8'),
'keys': generate_kerberos_keys(raw_password, salt)
}
def print_results(results: Dict[str, Union[str, Dict[str, str]]]) -> None:
"""
Print the account information and generated keys in a formatted manner.
Args:
results: Dictionary containing account info and keys
"""
print(results['account'])
print(f" * Salt: {results['salt']}")
for cipher_name, key in results['keys'].items():
print(f" * {cipher_name}: {key}")
def main() -> None:
"""Main function to handle command line arguments and process accounts."""
parser = argparse.ArgumentParser(
description='Generate Kerberos encryption keys for machine or user accounts'
)
parser.add_argument('-d', '--domain', required=True,
help='Domain name')
subparsers = parser.add_subparsers(dest='account_type', required=True,
help='Account type')
# Machine account parser
machine_parser = subparsers.add_parser('machine',
help='Generate keys for machine account')
machine_parser.add_argument('-n', '--hostname', required=True,
help='Machine hostname')
machine_parser.add_argument('-p', '--hex-password', required=True,
help='Machine password in hexadecimal format')
# User account parser
user_parser = subparsers.add_parser('user',
help='Generate keys for user account')
user_parser.add_argument('-u', '--username', required=True,
help='Username')
user_parser.add_argument('-p', '--password', required=True,
help='User password')
args = parser.parse_args()
try:
if args.account_type == 'machine':
results = process_machine_account(
args.domain,
args.hostname,
args.hex_password
)
else: # user account
results = process_user_account(
args.domain,
args.username,
args.password
)
print_results(results)
except Exception as e:
parser.error(f"Error processing account: {str(e)}")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment