Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions examples/ticketer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1084,9 +1084,14 @@ def signEncryptTicket(self, kdcRep, encASorTGSRepPart, encTicketPart, pacInfos):
return encoder.encode(kdcRep), cipher, sessionKey

def saveTicket(self, ticket, sessionKey):
logging.info('Saving ticket in %s' % (self.__target.replace('/', '.') + '.ccache'))
logging.info('Saving/Updating ticket in %s' % (self.__target.replace('/', '.') + '.ccache'))
from impacket.krb5.ccache import CCache
ccache = CCache()
from os import getenv, path
krb5 = getenv('KRB5CCNAME')
if krb5 and path.isfile(krb5):
ccache = CCache.loadFile(krb5)
else:
ccache = CCache()

if self.__server == self.__domain:
ccache.fromTGT(ticket, sessionKey, sessionKey)
Expand Down
210 changes: 125 additions & 85 deletions impacket/krb5/ccache.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,13 +451,18 @@ def toTimeStamp(self, dt, epoch=datetime(1970,1,1)):
return int((td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) // 1e6)

def reverseFlags(self, flags):
result = 0
# If passed as a string representation (e.g., "'010100...'B"), clean it up
if isinstance(flags, str):
flags = flags[1:-2]
for i,j in enumerate(reversed(flags)):
if j != 0:
result += j << i
return result
bit_str = flags.replace("'", "").replace("B", "")
else:
# Standard pyasn1 BitString handling: join the bits into a string
bit_str = "".join(str(bit) for bit in flags)

# Ensure it's exactly 32 bits by padding with zeros on the right
bit_str = bit_str.ljust(32, '0')[:32]

# Convert the base-2 binary string back to an integer
return int(bit_str, 2)

def fromTGT(self, tgt, oldSessionKey, sessionKey):
self.headers = []
Expand Down Expand Up @@ -675,100 +680,135 @@ def saveKirbiFile(self, fileName):
f.close()

def fromKRBCRED(self, encodedKrbCred):

krbCred = decoder.decode(encodedKrbCred, asn1Spec=KRB_CRED())[0]
encKrbCredPart = decoder.decode(krbCred['enc-part']['cipher'], asn1Spec=EncKrbCredPart())[0]
krbCredInfo = encKrbCredPart['ticket-info'][0]
encKrbCredPart = decoder.decode(
krbCred["enc-part"]["cipher"], asn1Spec=EncKrbCredPart()
)[0]

self.setDefaultHeader()

tmpPrincipal = types.Principal()
tmpPrincipal.from_asn1(krbCredInfo, 'prealm', 'pname')
self.principal = Principal()
self.principal.fromPrincipal(tmpPrincipal)

credential = Credential()
server = types.Principal()
server.from_asn1(krbCredInfo, 'srealm', 'sname')
tmpServer = Principal()
tmpServer.fromPrincipal(server)

credential['client'] = self.principal
credential['server'] = tmpServer
credential['is_skey'] = 0

credential['key'] = KeyBlockV4()
credential['key']['keytype'] = int(krbCredInfo['key']['keytype'])
credential['key']['keyvalue'] = krbCredInfo['key']['keyvalue'].asOctets()
credential['key']['keylen'] = len(credential['key']['keyvalue'])

credential['time'] = Times()

credential['time']['authtime'] = self.toTimeStamp(types.KerberosTime.from_asn1(krbCredInfo['starttime']))
credential['time']['starttime'] = self.toTimeStamp(types.KerberosTime.from_asn1(krbCredInfo['starttime']))
credential['time']['endtime'] = self.toTimeStamp(types.KerberosTime.from_asn1(krbCredInfo['endtime']))
# After KB4586793 for CVE-2020-17049 this timestamp may be omitted
if krbCredInfo['renew-till'].hasValue():
credential['time']['renew_till'] = self.toTimeStamp(types.KerberosTime.from_asn1(krbCredInfo['renew-till']))

flags = self.reverseFlags(krbCredInfo['flags'])
credential['tktflags'] = flags

credential['num_address'] = 0
credential.ticket = CountedOctetString()
credential.ticket['data'] = encoder.encode(
krbCred['tickets'][0].clone(tagSet=Ticket.tagSet, cloneValueFlag=True)
)
credential.ticket['length'] = len(credential.ticket['data'])
credential.secondTicket = CountedOctetString()
credential.secondTicket['data'] = b''
credential.secondTicket['length'] = 0

self.credentials.append(credential)
# Loop through all ticket-info entries
for i, krbCredInfo in enumerate(encKrbCredPart["ticket-info"]):
# Set the ccache principal from the first entry
if i == 0:
tmpPrincipal = types.Principal()
tmpPrincipal.from_asn1(krbCredInfo, "prealm", "pname")
self.principal = Principal()
self.principal.fromPrincipal(tmpPrincipal)

credential = Credential()
server = types.Principal()
server.from_asn1(krbCredInfo, "srealm", "sname")
tmpServer = Principal()
tmpServer.fromPrincipal(server)

credential["client"] = self.principal
credential["server"] = tmpServer
credential["is_skey"] = 0

credential["key"] = KeyBlockV4()
credential["key"]["keytype"] = int(krbCredInfo["key"]["keytype"])
credential["key"]["keyvalue"] = krbCredInfo["key"]["keyvalue"].asOctets()
credential["key"]["keylen"] = len(credential["key"]["keyvalue"])

credential["time"] = Times()

credential["time"]["authtime"] = self.toTimeStamp(
types.KerberosTime.from_asn1(krbCredInfo["starttime"])
)
credential["time"]["starttime"] = self.toTimeStamp(
types.KerberosTime.from_asn1(krbCredInfo["starttime"])
)
credential["time"]["endtime"] = self.toTimeStamp(
types.KerberosTime.from_asn1(krbCredInfo["endtime"])
)
# After KB4586793 for CVE-2020-17049 this timestamp may be omitted
if krbCredInfo["renew-till"].hasValue():
credential["time"]["renew_till"] = self.toTimeStamp(
types.KerberosTime.from_asn1(krbCredInfo["renew-till"])
)

flags = self.reverseFlags(krbCredInfo["flags"])
credential["tktflags"] = flags

credential["num_address"] = 0
credential.ticket = CountedOctetString()
credential.ticket["data"] = encoder.encode(
krbCred["tickets"][i].clone(tagSet=Ticket.tagSet, cloneValueFlag=True)
)
credential.ticket["length"] = len(credential.ticket["data"])
credential.secondTicket = CountedOctetString()
credential.secondTicket["data"] = b""
credential.secondTicket["length"] = 0

self.credentials.append(credential)

def toKRBCRED(self):
principal = self.principal
credential = self.credentials[0]

krbCredInfo = KrbCredInfo()

krbCredInfo['key'] = noValue
krbCredInfo['key']['keytype'] = credential['key']['keytype']
krbCredInfo['key']['keyvalue'] = credential['key']['keyvalue']

krbCredInfo['prealm'] = principal.realm.fields['data']
krbCredInfos = []
tickets = []

krbCredInfo['pname'] = noValue
krbCredInfo['pname']['name-type'] = principal.header['name_type']
seq_set_iter(krbCredInfo['pname'], 'name-string', (principal.components[0].fields['data'],))

krbCredInfo['flags'] = credential['tktflags']

krbCredInfo['starttime'] = KerberosTime.to_asn1(datetime.fromtimestamp(credential['time']['starttime'], tz=timezone.utc))
krbCredInfo['endtime'] = KerberosTime.to_asn1(datetime.fromtimestamp(credential['time']['endtime'], tz=timezone.utc))
krbCredInfo['renew-till'] = KerberosTime.to_asn1(datetime.fromtimestamp(credential['time']['renew_till'], tz=timezone.utc))

krbCredInfo['srealm'] = credential['server'].realm.fields['data']

krbCredInfo['sname'] = noValue
krbCredInfo['sname']['name-type'] = credential['server'].header['name_type']
tmp_service_class = credential['server'].components[0].fields['data']
tmp_service_hostname = credential['server'].components[1].fields['data']
seq_set_iter(krbCredInfo['sname'], 'name-string', (tmp_service_class, tmp_service_hostname))
for credential in self.credentials:
krbCredInfo = KrbCredInfo()

krbCredInfo["key"] = noValue
krbCredInfo["key"]["keytype"] = credential["key"]["keytype"]
krbCredInfo["key"]["keyvalue"] = credential["key"]["keyvalue"]

krbCredInfo["prealm"] = principal.realm.fields["data"]

krbCredInfo["pname"] = noValue
krbCredInfo["pname"]["name-type"] = principal.header["name_type"]
seq_set_iter(
krbCredInfo["pname"],
"name-string",
tuple(c.fields["data"] for c in principal.components),
)

# Force the 32 bit representation to avoid any precision loss due to ASN1 conversion
krbCredInfo["flags"] = f"{int(credential['tktflags']):032b}"

krbCredInfo["starttime"] = KerberosTime.to_asn1(
datetime.fromtimestamp(credential["time"]["starttime"], tz=timezone.utc)
)
krbCredInfo["endtime"] = KerberosTime.to_asn1(
datetime.fromtimestamp(credential["time"]["endtime"], tz=timezone.utc)
)
if credential["time"]["renew_till"] != 0:
krbCredInfo["renew-till"] = KerberosTime.to_asn1(
datetime.fromtimestamp(
credential["time"]["renew_till"], tz=timezone.utc
)
)

krbCredInfo["srealm"] = credential["server"].realm.fields["data"]

krbCredInfo["sname"] = noValue
krbCredInfo["sname"]["name-type"] = credential["server"].header["name_type"]
seq_set_iter(
krbCredInfo["sname"],
"name-string",
tuple(c.fields["data"] for c in credential["server"].components),
)

krbCredInfos.append(krbCredInfo)

ticket = decoder.decode(credential.ticket["data"], asn1Spec=Ticket())[0]
tickets.append(ticket)

encKrbCredPart = EncKrbCredPart()
seq_set_iter(encKrbCredPart, 'ticket-info', (krbCredInfo,))
seq_set_iter(encKrbCredPart, "ticket-info", krbCredInfos)

krbCred = KRB_CRED()
krbCred['pvno'] = 5
krbCred['msg-type'] = 22
krbCred["pvno"] = 5
krbCred["msg-type"] = 22

krbCred['enc-part'] = noValue
krbCred['enc-part']['etype'] = 0
krbCred['enc-part']['cipher'] = encoder.encode(encKrbCredPart)
krbCred["enc-part"] = noValue
krbCred["enc-part"]["etype"] = 0
krbCred["enc-part"]["cipher"] = encoder.encode(encKrbCredPart)

ticket = decoder.decode(credential.ticket['data'], asn1Spec=Ticket())[0]
seq_set_iter(krbCred, 'tickets', (ticket,))
seq_set_iter(krbCred, "tickets", tickets)

encodedKrbCred = encoder.encode(krbCred)

Expand Down
Loading