Documentation Index
Fetch the complete documentation index at: https://mintlify.com/fortra/impacket/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Theimpacket.krb5.ccache module implements the Kerberos credential cache file format used to store Kerberos tickets (TGT and TGS) for single sign-on. The implementation supports:
- Reading/writing ccache files (versions 3 and 4)
- Extracting tickets from cache
- Converting between ccache and ticket formats
- Converting to/from Kirbi format (Mimikatz)
- Parsing credentials from environment variables
Module Location
from impacket.krb5.ccache import CCache
impacket/krb5/ccache.py
CCache Class
Main class for credential cache operations.class CCache:
def __init__(self, data=None)
@classmethod
def loadFile(cls, fileName)
@classmethod
def loadKirbiFile(cls, fileName)
@classmethod
def parseFile(cls, domain='', username='', target='')
def saveFile(self, fileName)
def saveKirbiFile(self, fileName)
def getCredential(self, server, anySPN=True)
def fromTGT(self, tgt, oldSessionKey, sessionKey)
def fromTGS(self, tgs, oldSessionKey, sessionKey)
def fromKRBCRED(self, encodedKrbCred)
def toKRBCRED(self)
File Format
CCache File Structure
[2 bytes] file_format_version (0x0504)
[2 bytes] headerlen
[headerlen bytes] headers (version 4 only)
[variable] principal
[variable] credentials...
Version Support
- Version 3: Basic format without headers
- Version 4: Includes header section (recommended)
ccache = CCache()
ccache.MiniHeader()['file_format_version'] = 0x0504 # Version 4
Loading Credentials
Load from File
Load ccache from file path:from impacket.krb5.ccache import CCache
# Load ccache file
ccache = CCache.loadFile('/tmp/krb5cc_1000')
if ccache:
print(f"Principal: {ccache.principal.prettyPrint()}")
print(f"Credentials: {len(ccache.credentials)}")
Load from Environment
Automatically use KRB5CCNAME environment variable:import os
# Set environment variable
os.environ['KRB5CCNAME'] = '/tmp/krb5cc_1000'
# Parse credentials
domain, username, TGT, TGS = CCache.parseFile(
domain='DOMAIN.LOCAL',
username='user',
target='cifs/server.domain.local'
)
if TGT:
print("TGT found in cache")
print(f"Session key: {TGT['sessionKey']}")
print(f"Cipher: {TGT['cipher']}")
if TGS:
print("TGS found for target")
Load from Binary Data
Parse ccache from bytes:with open('/tmp/krb5cc_1000', 'rb') as f:
data = f.read()
ccache = CCache(data)
Saving Credentials
Save to File
ccache = CCache()
# ... populate credentials ...
ccache.saveFile('/tmp/krb5cc_new')
Create from TGT
Convert AS-REP to ccache:from impacket.krb5.kerberosv5 import getKerberosTGT
from impacket.krb5.ccache import CCache
from impacket.krb5.types import Principal
from impacket.krb5 import constants
# Get TGT
clientName = Principal('user', type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
clientName,
'password',
'DOMAIN.LOCAL',
lmhash=b'',
nthash=b''
)
# Create ccache
ccache = CCache()
ccache.fromTGT(tgt, oldSessionKey, sessionKey)
ccache.saveFile('/tmp/krb5cc_new')
Create from TGS
Convert TGS-REP to ccache:from impacket.krb5.kerberosv5 import getKerberosTGS
# Get TGS
serverName = Principal('cifs/server.domain.local',
type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
serverName,
'DOMAIN.LOCAL',
'dc.domain.local',
tgt,
cipher,
tgtSessionKey
)
# Add to existing ccache
ccache.fromTGS(tgs, oldSessionKey, sessionKey)
ccache.saveFile('/tmp/krb5cc_updated')
Credential Structure
Credential Class
Represents a single cached ticket:class Credential:
# Access credential fields
credential['client'] # Client principal
credential['server'] # Service principal
credential['key'] # Session key
credential['time'] # Ticket times
credential['is_skey'] # Is session key?
credential['tktflags'] # Ticket flags
credential.ticket # Encoded ticket
Credential Fields
# Client and server principals
print(f"Client: {cred['client'].prettyPrint()}")
print(f"Server: {cred['server'].prettyPrint()}")
# Session key
keyblock = cred['key']
print(f"Key type: {keyblock['keytype']}")
print(f"Key value: {hexlify(keyblock['keyvalue'])}")
# Times
times = cred['time']
print(f"Auth time: {datetime.fromtimestamp(times['authtime'])}")
print(f"Start time: {datetime.fromtimestamp(times['starttime'])}")
print(f"End time: {datetime.fromtimestamp(times['endtime'])}")
print(f"Renew till: {datetime.fromtimestamp(times['renew_till'])}")
# Flags
print(f"Flags: 0x{cred['tktflags']:x}")
Retrieving Credentials
Get Credential by SPN
Find cached ticket for service:ccache = CCache.loadFile('/tmp/krb5cc_1000')
# Get TGT
tgt_cred = ccache.getCredential('krbtgt/DOMAIN.LOCAL@DOMAIN.LOCAL')
if tgt_cred:
print("Found TGT")
tgt = tgt_cred.toTGT()
# Get service ticket
tgs_cred = ccache.getCredential('cifs/server.domain.local@DOMAIN.LOCAL')
if tgs_cred:
print("Found service ticket")
tgs = tgs_cred.toTGS()
Match Any SPN
Find ticket with flexible matching:# Match without port
cred = ccache.getCredential('cifs/server.domain.local', anySPN=True)
# Will match:
# - cifs/server.domain.local@DOMAIN.LOCAL
# - cifs/server.domain.local:88@DOMAIN.LOCAL
# - cifs/server@DOMAIN.LOCAL
Iterate All Credentials
for i, cred in enumerate(ccache.credentials):
print(f"[{i}] {cred['server'].prettyPrint()}")
print(f" Expires: {datetime.fromtimestamp(cred['time']['endtime'])}")
Credential Conversion
Convert to TGT Format
Convert cached credential to TGT dict:tgt_cred = ccache.getCredential('krbtgt/DOMAIN.LOCAL@DOMAIN.LOCAL')
if tgt_cred:
tgt = tgt_cred.toTGT()
# TGT dictionary:
# {
# 'KDC_REP': bytes, # Encoded AS-REP
# 'cipher': cipher_class, # Cipher object
# 'sessionKey': Key_object # Session key
# }
# Use for TGS-REQ
from impacket.krb5.kerberosv5 import getKerberosTGS
tgs, cipher, oldKey, sessionKey = getKerberosTGS(
serverName,
domain,
kdcHost,
tgt['KDC_REP'],
tgt['cipher'],
tgt['sessionKey']
)
Convert to TGS Format
Convert cached credential to TGS dict:tgs_cred = ccache.getCredential('cifs/server.domain.local@DOMAIN.LOCAL')
if tgs_cred:
tgs = tgs_cred.toTGS()
# TGS dictionary format same as TGT
# Use for service authentication
Change SPN
Modify service principal in ticket:# Get credential
cred = ccache.getCredential('cifs/server.domain.local@DOMAIN.LOCAL')
# Convert with new SPN
tgs = cred.toTGS(newSPN='http/server.domain.local')
# Note: This modifies the ticket's sname field
# May not work if server validates ticket strictly
Kirbi Format
Load Kirbi File
Load Mimikatz .kirbi file:ccache = CCache.loadKirbiFile('ticket.kirbi')
# Now use as normal ccache
ccache.saveFile('/tmp/krb5cc_converted')
Save as Kirbi
Convert ccache to Mimikatz format:ccache = CCache.loadFile('/tmp/krb5cc_1000')
ccache.saveKirbiFile('ticket.kirbi')
Kirbi Format Details
Kirbi files use KRB-CRED ASN.1 structure:from impacket.krb5.asn1 import KRB_CRED, EncKrbCredPart
from pyasn1.codec.der import decoder, encoder
# Load kirbi
with open('ticket.kirbi', 'rb') as f:
data = f.read()
krbCred = decoder.decode(data, asn1Spec=KRB_CRED())[0]
encKrbCredPart = decoder.decode(
krbCred['enc-part']['cipher'],
asn1Spec=EncKrbCredPart()
)[0]
# Access ticket info
ticket = krbCred['tickets'][0]
krbCredInfo = encKrbCredPart['ticket-info'][0]
print(f"Key: {krbCredInfo['key']}")
print(f"Service: {krbCredInfo['sname']}")
Principal Structure
Principal Class
Represents Kerberos principal in ccache:class Principal:
header['name_type'] # Principal type
header['num_components'] # Component count
realm # Realm name
components[] # Name components
def prettyPrint(self) # Format as string
def fromPrincipal(principal) # From types.Principal
def toPrincipal(self) # To types.Principal
Create Principal
from impacket.krb5.ccache import Principal as CachePrincipal
from impacket.krb5.types import Principal
from impacket.krb5 import constants
# From types.Principal
principal = Principal('user@DOMAIN.LOCAL',
type=constants.PrincipalNameType.NT_PRINCIPAL.value)
cache_principal = CachePrincipal()
cache_principal.fromPrincipal(principal)
# Create manually
cache_principal = CachePrincipal()
cache_principal.header['name_type'] = 1
cache_principal.header['num_components'] = 1
from impacket.krb5.ccache import CountedOctetString
realm = CountedOctetString()
realm['length'] = len('DOMAIN.LOCAL')
realm['data'] = b'DOMAIN.LOCAL'
cache_principal.realm = realm
component = CountedOctetString()
component['length'] = len('user')
component['data'] = b'user'
cache_principal.components.append(component)
print(cache_principal.prettyPrint()) # b'user@DOMAIN.LOCAL'
Key Block Structure
KeyBlock Classes
Version 3 and 4 have different key formats:class KeyBlockV4(Structure):
structure = (
('keytype','!H=0'),
('etype','!H=0'),
('keylen','!H=0'),
('_keyvalue','_-keyvalue','self["keylen"]'),
('keyvalue',':'),
)
class KeyBlockV3(Structure):
structure = (
('keytype','!H=0'),
('etype','!H=0'),
('etype2', '!H=0'), # Duplicate etype
('keylen','!H=0'),
('_keyvalue','_-keyvalue','self["keylen"]'),
('keyvalue',':'),
)
Access Key Data
keyblock = credential['key']
print(f"Key type: {keyblock['keytype']}")
print(f"Encryption: {keyblock['etype']}")
print(f"Length: {keyblock['keylen']}")
print(f"Value: {hexlify(keyblock['keyvalue'])}")
# Create crypto.Key object
from impacket.krb5.crypto import Key
key = Key(keyblock['keytype'], keyblock['keyvalue'])
Time Structure
Times Class
Stores ticket validity times:class Times(Structure):
structure = (
('authtime','!L=0'),
('starttime','!L=0'),
('endtime','!L=0'),
('renew_till','!L=0'),
)
Access Times
from datetime import datetime
times = credential['time']
print(f"Auth: {datetime.fromtimestamp(times['authtime']).isoformat()}")
print(f"Start: {datetime.fromtimestamp(times['starttime']).isoformat()}")
print(f"End: {datetime.fromtimestamp(times['endtime']).isoformat()}")
print(f"Renew: {datetime.fromtimestamp(times['renew_till']).isoformat()}")
# Check if expired
now = datetime.now().timestamp()
if times['endtime'] < now:
print("Ticket expired")
Practical Examples
Dump Ccache Contents
from impacket.krb5.ccache import CCache
from datetime import datetime
from binascii import hexlify
def dump_ccache(filename):
ccache = CCache.loadFile(filename)
if not ccache:
print("Failed to load ccache")
return
print(f"Primary Principal: {ccache.principal.prettyPrint().decode()}")
print(f"\nCredentials ({len(ccache.credentials)}):")
print("=" * 80)
for i, cred in enumerate(ccache.credentials):
print(f"\n[{i}]")
print(f" Client: {cred['client'].prettyPrint().decode()}")
print(f" Server: {cred['server'].prettyPrint().decode()}")
keyblock = cred['key']
print(f" Key Type: {keyblock['keytype']}")
print(f" Key Value: {hexlify(keyblock['keyvalue']).decode()}")
times = cred['time']
print(f" Auth Time: {datetime.fromtimestamp(times['authtime'])}")
print(f" End Time: {datetime.fromtimestamp(times['endtime'])}")
print(f" Flags: 0x{cred['tktflags']:08x}")
print(f" Ticket Size: {cred.ticket['length']} bytes")
dump_ccache('/tmp/krb5cc_1000')
Extract and Use Ticket
import os
from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import getKerberosTGS
from impacket.krb5.types import Principal
from impacket.krb5 import constants
def use_cached_ticket(target_service):
# Load from environment
ccache_path = os.environ.get('KRB5CCNAME', '/tmp/krb5cc_1000')
ccache = CCache.loadFile(ccache_path)
if not ccache:
print("No ccache found")
return None
# Get TGT
tgt_cred = ccache.getCredential('krbtgt/', anySPN=True)
if not tgt_cred:
print("No TGT in cache")
return None
tgt = tgt_cred.toTGT()
# Get domain from TGT
domain = tgt_cred['server'].realm['data'].decode('utf-8')
# Request new service ticket
serverName = Principal(
target_service,
type=constants.PrincipalNameType.NT_SRV_INST.value
)
tgs, cipher, oldKey, sessionKey = getKerberosTGS(
serverName,
domain,
None, # Let it discover KDC
tgt['KDC_REP'],
tgt['cipher'],
tgt['sessionKey']
)
return tgs, sessionKey
# Use cached TGT to get new service ticket
tgs, sessionKey = use_cached_ticket('cifs/fileserver.domain.local')
Merge Ccache Files
def merge_ccaches(ccache1_path, ccache2_path, output_path):
# Load both caches
ccache1 = CCache.loadFile(ccache1_path)
ccache2 = CCache.loadFile(ccache2_path)
if not ccache1 or not ccache2:
print("Failed to load one or both ccaches")
return
# Use first principal
merged = CCache()
merged.principal = ccache1.principal
merged.headers = ccache1.headers
# Combine credentials
merged.credentials = ccache1.credentials[:]
# Add credentials from second cache if not duplicate
for cred2 in ccache2.credentials:
spn2 = cred2['server'].prettyPrint()
# Check if already exists
duplicate = False
for cred1 in ccache1.credentials:
if cred1['server'].prettyPrint() == spn2:
duplicate = True
break
if not duplicate:
merged.credentials.append(cred2)
# Save merged cache
merged.saveFile(output_path)
print(f"Merged {len(merged.credentials)} credentials")
merge_ccaches('/tmp/krb5cc_1000', '/tmp/krb5cc_backup', '/tmp/krb5cc_merged')
Convert Between Formats
def convert_ccache_kirbi(input_file, output_file, to_kirbi=True):
if to_kirbi:
# Ccache -> Kirbi
ccache = CCache.loadFile(input_file)
if ccache:
ccache.saveKirbiFile(output_file)
print(f"Converted to Kirbi: {output_file}")
else:
# Kirbi -> Ccache
ccache = CCache.loadKirbiFile(input_file)
if ccache:
ccache.saveFile(output_file)
print(f"Converted to ccache: {output_file}")
# Usage
convert_ccache_kirbi('/tmp/krb5cc_1000', 'ticket.kirbi', to_kirbi=True)
convert_ccache_kirbi('ticket.kirbi', '/tmp/krb5cc_new', to_kirbi=False)
Renew Expired Ticket
from impacket.krb5.kerberosv5 import getKerberosTGS
from datetime import datetime
def renew_ticket(ccache_path, spn):
ccache = CCache.loadFile(ccache_path)
# Find credential
cred = ccache.getCredential(spn)
if not cred:
print(f"Credential not found: {spn}")
return
# Check if renewable
times = cred['time']
now = datetime.now().timestamp()
if times['renew_till'] < now:
print("Ticket not renewable (past renew-till time)")
return
# Get TGT for renewal
tgt_cred = ccache.getCredential('krbtgt/', anySPN=True)
if not tgt_cred:
print("No TGT found")
return
tgt = tgt_cred.toTGT()
domain = tgt_cred['server'].realm['data'].decode('utf-8')
# Renew ticket
serverName = Principal(spn, type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, oldKey, sessionKey = getKerberosTGS(
serverName,
domain,
None,
tgt['KDC_REP'],
tgt['cipher'],
tgt['sessionKey'],
renew=True
)
# Update cache
ccache.fromTGS(tgs, oldKey, sessionKey)
ccache.saveFile(ccache_path)
print(f"Renewed ticket for {spn}")
Security Considerations
File Permissions
Protect ccache files:import os
ccache_path = '/tmp/krb5cc_1000'
# Set restrictive permissions (0600)
os.chmod(ccache_path, 0o600)
# Verify ownership
stat_info = os.stat(ccache_path)
if stat_info.st_uid != os.getuid():
print("Warning: Ccache owned by different user")
Credential Expiration
Check ticket validity:from datetime import datetime
def check_expiration(ccache_path):
ccache = CCache.loadFile(ccache_path)
now = datetime.now().timestamp()
for cred in ccache.credentials:
server = cred['server'].prettyPrint().decode()
endtime = cred['time']['endtime']
if endtime < now:
print(f"EXPIRED: {server}")
else:
remaining = datetime.fromtimestamp(endtime) - datetime.now()
print(f"Valid for {remaining}: {server}")