Coverage for /root/GitHubProjects/impacket/impacket/krb5/kerberosv5.py : 67%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Description:
10# Helper functions for kerberos
11# Just starting, TONS of things to do
12# In fact, make it easier
13#
14# Author:
15# Alberto Solino (@agsolino)
16#
17import datetime
18import random
19import socket
20import struct
21import os
23from pyasn1.codec.der import decoder, encoder
24from pyasn1.error import PyAsn1Error
25from pyasn1.type.univ import noValue, Sequence
26from pyasn1.type.useful import GeneralizedTime
27from six import b
28from binascii import unhexlify, hexlify
30from impacket.krb5.asn1 import AS_REQ, AP_REQ, TGS_REQ, KERB_PA_PAC_REQUEST, KRB_ERROR, PA_ENC_TS_ENC, AS_REP, TGS_REP, \
31 EncryptedData, Authenticator, EncASRepPart, EncTGSRepPart, seq_set, seq_set_iter, KERB_ERROR_DATA, METHOD_DATA, \
32 ETYPE_INFO2, ETYPE_INFO, AP_REP, EncAPRepPart
33from impacket.krb5.types import KerberosTime, Principal, Ticket
34from impacket.krb5.gssapi import CheckSumField, GSS_C_DCE_STYLE, GSS_C_MUTUAL_FLAG, GSS_C_REPLAY_FLAG, \
35 GSS_C_SEQUENCE_FLAG, GSS_C_CONF_FLAG, GSS_C_INTEG_FLAG
36from impacket.krb5 import constants
37from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum
38from impacket.smbconnection import SessionError
39from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp, ASN1_OID, asn1encode, ASN1_AID
40from impacket.krb5.gssapi import KRB5_AP_REQ
41from impacket import nt_errors, LOG
42from impacket.krb5.ccache import CCache
44# Our random number generator
45try:
46 rand = random.SystemRandom()
47except NotImplementedError:
48 rand = random
49 pass
51def sendReceive(data, host, kdcHost):
52 if kdcHost is None:
53 targetHost = host
54 else:
55 targetHost = kdcHost
57 messageLen = struct.pack('!i', len(data))
59 LOG.debug('Trying to connect to KDC at %s' % targetHost)
60 try:
61 af, socktype, proto, canonname, sa = socket.getaddrinfo(targetHost, 88, 0, socket.SOCK_STREAM)[0]
62 s = socket.socket(af, socktype, proto)
63 s.connect(sa)
64 except socket.error as e:
65 raise socket.error("Connection error (%s:%s)" % (targetHost, 88), e)
67 s.sendall(messageLen + data)
69 recvDataLen = struct.unpack('!i', s.recv(4))[0]
71 r = s.recv(recvDataLen)
72 while len(r) < recvDataLen: 72 ↛ 73line 72 didn't jump to line 73, because the condition on line 72 was never true
73 r += s.recv(recvDataLen-len(r))
75 try:
76 krbError = KerberosError(packet = decoder.decode(r, asn1Spec = KRB_ERROR())[0])
77 except:
78 return r
80 if krbError.getErrorCode() != constants.ErrorCodes.KDC_ERR_PREAUTH_REQUIRED.value: 80 ↛ 81line 80 didn't jump to line 81, because the condition on line 80 was never true
81 try:
82 for i in decoder.decode(r):
83 if type(i) == Sequence:
84 for k in vars(i)["_componentValues"]:
85 if type(k) == GeneralizedTime:
86 server_time = datetime.datetime.strptime(k.asOctets().decode("utf-8"), "%Y%m%d%H%M%SZ")
87 LOG.debug("Server time (UTC): %s" % server_time)
88 except:
89 # Couldn't get server time for some reason
90 pass
91 raise krbError
93 return r
95def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True):
97 # Convert to binary form, just in case we're receiving strings
98 if isinstance(lmhash, str):
99 try:
100 lmhash = unhexlify(lmhash)
101 except TypeError:
102 pass
103 if isinstance(nthash, str):
104 try:
105 nthash = unhexlify(nthash)
106 except TypeError:
107 pass
108 if isinstance(aesKey, str):
109 try:
110 aesKey = unhexlify(aesKey)
111 except TypeError:
112 pass
114 asReq = AS_REQ()
116 domain = domain.upper()
117 serverName = Principal('krbtgt/%s'%domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
119 pacRequest = KERB_PA_PAC_REQUEST()
120 pacRequest['include-pac'] = requestPAC
121 encodedPacRequest = encoder.encode(pacRequest)
123 asReq['pvno'] = 5
124 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)
126 asReq['padata'] = noValue
127 asReq['padata'][0] = noValue
128 asReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value)
129 asReq['padata'][0]['padata-value'] = encodedPacRequest
131 reqBody = seq_set(asReq, 'req-body')
133 opts = list()
134 opts.append( constants.KDCOptions.forwardable.value )
135 opts.append( constants.KDCOptions.renewable.value )
136 opts.append( constants.KDCOptions.proxiable.value )
137 reqBody['kdc-options'] = constants.encodeFlags(opts)
139 seq_set(reqBody, 'sname', serverName.components_to_asn1)
140 seq_set(reqBody, 'cname', clientName.components_to_asn1)
142 if domain == '': 142 ↛ 143line 142 didn't jump to line 143, because the condition on line 142 was never true
143 raise Exception('Empty Domain not allowed in Kerberos')
145 reqBody['realm'] = domain
147 now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
148 reqBody['till'] = KerberosTime.to_asn1(now)
149 reqBody['rtime'] = KerberosTime.to_asn1(now)
150 reqBody['nonce'] = rand.getrandbits(31)
152 # Yes.. this shouldn't happen but it's inherited from the past
153 if aesKey is None: 153 ↛ 154line 153 didn't jump to line 154, because the condition on line 153 was never true
154 aesKey = b''
156 if nthash == b'':
157 # This is still confusing. I thought KDC_ERR_ETYPE_NOSUPP was enough,
158 # but I found some systems that accepts all ciphers, and trigger an error
159 # when requesting subsequent TGS :(. More research needed.
160 # So, in order to support more than one cypher, I'm setting aes first
161 # since most of the systems would accept it. If we're lucky and
162 # KDC_ERR_ETYPE_NOSUPP is returned, we will later try rc4.
163 if aesKey != b'':
164 if len(aesKey) == 32:
165 supportedCiphers = (int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value),)
166 else:
167 supportedCiphers = (int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value),)
168 else:
169 supportedCiphers = (int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value),)
170 else:
171 # We have hashes to try, only way is to request RC4 only
172 supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value),)
174 seq_set_iter(reqBody, 'etype', supportedCiphers)
176 message = encoder.encode(asReq)
178 try:
179 r = sendReceive(message, domain, kdcHost)
180 except KerberosError as e:
181 if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
182 if supportedCiphers[0] in (constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value, constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value) and aesKey == b'':
183 supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value),)
184 seq_set_iter(reqBody, 'etype', supportedCiphers)
185 message = encoder.encode(asReq)
186 r = sendReceive(message, domain, kdcHost)
187 else:
188 raise
189 else:
190 raise
192 # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the
193 # 'Do not require Kerberos preauthentication' set
194 preAuth = True
195 try:
196 asRep = decoder.decode(r, asn1Spec = KRB_ERROR())[0]
197 except:
198 # Most of the times we shouldn't be here, is this a TGT?
199 asRep = decoder.decode(r, asn1Spec=AS_REP())[0]
200 # Yes
201 preAuth = False
203 encryptionTypesData = dict()
204 salt = ''
205 if preAuth is False: 205 ↛ 207line 205 didn't jump to line 207, because the condition on line 205 was never true
206 # In theory, we should have the right credentials for the etype specified before.
207 methods = asRep['padata']
208 encryptionTypesData[supportedCiphers[0]] = salt # handle RC4 fallback, we don't need any salt
209 tgt = r
210 else:
211 methods = decoder.decode(asRep['e-data'], asn1Spec=METHOD_DATA())[0]
213 for method in methods:
214 if method['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
215 etypes2 = decoder.decode(method['padata-value'], asn1Spec = ETYPE_INFO2())[0]
216 for etype2 in etypes2:
217 try:
218 if etype2['salt'] is None or etype2['salt'].hasValue() is False:
219 salt = ''
220 else:
221 salt = etype2['salt'].prettyPrint()
222 except PyAsn1Error:
223 salt = ''
225 encryptionTypesData[etype2['etype']] = b(salt)
226 elif method['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value:
227 etypes = decoder.decode(method['padata-value'], asn1Spec = ETYPE_INFO())[0]
228 for etype in etypes:
229 try:
230 if etype['salt'] is None or etype['salt'].hasValue() is False: 230 ↛ 231line 230 didn't jump to line 231, because the condition on line 230 was never true
231 salt = ''
232 else:
233 salt = etype['salt'].prettyPrint()
234 except PyAsn1Error:
235 salt = ''
237 encryptionTypesData[etype['etype']] = b(salt)
239 enctype = supportedCiphers[0]
241 cipher = _enctype_table[enctype]
243 # Pass the hash/aes key :P
244 if isinstance(nthash, bytes) and nthash != b'':
245 key = Key(cipher.enctype, nthash)
246 elif aesKey != b'':
247 key = Key(cipher.enctype, aesKey)
248 else:
249 key = cipher.string_to_key(password, encryptionTypesData[enctype], None)
251 if preAuth is True: 251 ↛ 326line 251 didn't jump to line 326, because the condition on line 251 was never false
252 if enctype in encryptionTypesData is False: 252 ↛ 253line 252 didn't jump to line 253, because the condition on line 252 was never true
253 raise Exception('No Encryption Data Available!')
255 # Let's build the timestamp
256 timeStamp = PA_ENC_TS_ENC()
258 now = datetime.datetime.utcnow()
259 timeStamp['patimestamp'] = KerberosTime.to_asn1(now)
260 timeStamp['pausec'] = now.microsecond
262 # Encrypt the shyte
263 encodedTimeStamp = encoder.encode(timeStamp)
265 # Key Usage 1
266 # AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the
267 # client key (Section 5.2.7.2)
268 encriptedTimeStamp = cipher.encrypt(key, 1, encodedTimeStamp, None)
270 encryptedData = EncryptedData()
271 encryptedData['etype'] = cipher.enctype
272 encryptedData['cipher'] = encriptedTimeStamp
273 encodedEncryptedData = encoder.encode(encryptedData)
275 # Now prepare the new AS_REQ again with the PADATA
276 # ToDo: cannot we reuse the previous one?
277 asReq = AS_REQ()
279 asReq['pvno'] = 5
280 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)
282 asReq['padata'] = noValue
283 asReq['padata'][0] = noValue
284 asReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_ENC_TIMESTAMP.value)
285 asReq['padata'][0]['padata-value'] = encodedEncryptedData
287 asReq['padata'][1] = noValue
288 asReq['padata'][1]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value)
289 asReq['padata'][1]['padata-value'] = encodedPacRequest
291 reqBody = seq_set(asReq, 'req-body')
293 opts = list()
294 opts.append( constants.KDCOptions.forwardable.value )
295 opts.append( constants.KDCOptions.renewable.value )
296 opts.append( constants.KDCOptions.proxiable.value )
297 reqBody['kdc-options'] = constants.encodeFlags(opts)
299 seq_set(reqBody, 'sname', serverName.components_to_asn1)
300 seq_set(reqBody, 'cname', clientName.components_to_asn1)
302 reqBody['realm'] = domain
304 now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
305 reqBody['till'] = KerberosTime.to_asn1(now)
306 reqBody['rtime'] = KerberosTime.to_asn1(now)
307 reqBody['nonce'] = rand.getrandbits(31)
309 seq_set_iter(reqBody, 'etype', ( (int(cipher.enctype),)))
311 try:
312 tgt = sendReceive(encoder.encode(asReq), domain, kdcHost)
313 except Exception as e:
314 if str(e).find('KDC_ERR_ETYPE_NOSUPP') >= 0:
315 if lmhash == b'' and nthash == b'' and (aesKey == b'' or aesKey is None):
316 from impacket.ntlm import compute_lmhash, compute_nthash
317 lmhash = compute_lmhash(password)
318 nthash = compute_nthash(password)
319 return getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey, kdcHost, requestPAC)
320 raise
323 asRep = decoder.decode(tgt, asn1Spec = AS_REP())[0]
325 # So, we have the TGT, now extract the new session key and finish
326 cipherText = asRep['enc-part']['cipher']
328 if preAuth is False: 328 ↛ 330line 328 didn't jump to line 330, because the condition on line 328 was never true
329 # Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it.
330 LOG.debug('$krb5asrep$%d$%s@%s:%s$%s' % (asRep['enc-part']['etype'],clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]),
331 hexlify(asRep['enc-part']['cipher'].asOctets()[16:])) )
332 # Key Usage 3
333 # AS-REP encrypted part (includes TGS session key or
334 # application session key), encrypted with the client key
335 # (Section 5.4.2)
336 try:
337 plainText = cipher.decrypt(key, 3, cipherText)
338 except InvalidChecksum as e:
339 # probably bad password if preauth is disabled
340 if preAuth is False:
341 error_msg = "failed to decrypt session key: %s" % str(e)
342 raise SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText)
343 raise
344 encASRepPart = decoder.decode(plainText, asn1Spec = EncASRepPart())[0]
346 # Get the session key and the ticket
347 cipher = _enctype_table[encASRepPart['key']['keytype']]
348 sessionKey = Key(cipher.enctype,encASRepPart['key']['keyvalue'].asOctets())
350 # ToDo: Check Nonces!
352 return tgt, cipher, key, sessionKey
354def getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey):
356 # Decode the TGT
357 try:
358 decodedTGT = decoder.decode(tgt, asn1Spec = AS_REP())[0]
359 except:
360 decodedTGT = decoder.decode(tgt, asn1Spec = TGS_REP())[0]
362 domain = domain.upper()
363 # Extract the ticket from the TGT
364 ticket = Ticket()
365 ticket.from_asn1(decodedTGT['ticket'])
367 apReq = AP_REQ()
368 apReq['pvno'] = 5
369 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
371 opts = list()
372 apReq['ap-options'] = constants.encodeFlags(opts)
373 seq_set(apReq,'ticket', ticket.to_asn1)
375 authenticator = Authenticator()
376 authenticator['authenticator-vno'] = 5
377 authenticator['crealm'] = decodedTGT['crealm'].asOctets()
379 clientName = Principal()
380 clientName.from_asn1( decodedTGT, 'crealm', 'cname')
382 seq_set(authenticator, 'cname', clientName.components_to_asn1)
384 now = datetime.datetime.utcnow()
385 authenticator['cusec'] = now.microsecond
386 authenticator['ctime'] = KerberosTime.to_asn1(now)
388 encodedAuthenticator = encoder.encode(authenticator)
390 # Key Usage 7
391 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
392 # TGS authenticator subkey), encrypted with the TGS session
393 # key (Section 5.5.1)
394 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)
396 apReq['authenticator'] = noValue
397 apReq['authenticator']['etype'] = cipher.enctype
398 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
400 encodedApReq = encoder.encode(apReq)
402 tgsReq = TGS_REQ()
404 tgsReq['pvno'] = 5
405 tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
406 tgsReq['padata'] = noValue
407 tgsReq['padata'][0] = noValue
408 tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
409 tgsReq['padata'][0]['padata-value'] = encodedApReq
411 reqBody = seq_set(tgsReq, 'req-body')
413 opts = list()
414 opts.append( constants.KDCOptions.forwardable.value )
415 opts.append( constants.KDCOptions.renewable.value )
416 opts.append( constants.KDCOptions.renewable_ok.value )
417 opts.append( constants.KDCOptions.canonicalize.value )
419 reqBody['kdc-options'] = constants.encodeFlags(opts)
420 seq_set(reqBody, 'sname', serverName.components_to_asn1)
421 reqBody['realm'] = domain
423 now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
425 reqBody['till'] = KerberosTime.to_asn1(now)
426 reqBody['nonce'] = rand.getrandbits(31)
427 seq_set_iter(reqBody, 'etype',
428 (
429 int(constants.EncryptionTypes.rc4_hmac.value),
430 int(constants.EncryptionTypes.des3_cbc_sha1_kd.value),
431 int(constants.EncryptionTypes.des_cbc_md5.value),
432 int(cipher.enctype)
433 )
434 )
436 message = encoder.encode(tgsReq)
438 r = sendReceive(message, domain, kdcHost)
440 # Get the session key
442 tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]
444 cipherText = tgs['enc-part']['cipher']
446 # Key Usage 8
447 # TGS-REP encrypted part (includes application session
448 # key), encrypted with the TGS session key (Section 5.4.2)
449 plainText = cipher.decrypt(sessionKey, 8, cipherText)
451 encTGSRepPart = decoder.decode(plainText, asn1Spec = EncTGSRepPart())[0]
453 newSessionKey = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue'].asOctets())
454 # Creating new cipher based on received keytype
455 cipher = _enctype_table[encTGSRepPart['key']['keytype']]
457 # Check we've got what we asked for
458 res = decoder.decode(r, asn1Spec = TGS_REP())[0]
459 spn = Principal()
460 spn.from_asn1(res['ticket'], 'realm', 'sname')
462 if spn.components[0] == serverName.components[0]: 462 ↛ 467line 462 didn't jump to line 467, because the condition on line 462 was never false
463 # Yes.. bye bye
464 return r, cipher, sessionKey, newSessionKey
465 else:
466 # Let's extract the Ticket, change the domain and keep asking
467 domain = spn.components[1]
468 return getKerberosTGS(serverName, domain, kdcHost, r, cipher, newSessionKey)
470################################################################################
471# DCE RPC Helpers
472################################################################################
473def getKerberosType3(cipher, sessionKey, auth_data):
474 negTokenResp = SPNEGO_NegTokenResp(auth_data)
475 # If DCE_STYLE = FALSE
476 #ap_rep = decoder.decode(negTokenResp['ResponseToken'][16:], asn1Spec=AP_REP())[0]
477 try:
478 krbError = KerberosError(packet = decoder.decode(negTokenResp['ResponseToken'][15:], asn1Spec = KRB_ERROR())[0])
479 except Exception:
480 pass
481 else:
482 raise krbError
484 ap_rep = decoder.decode(negTokenResp['ResponseToken'], asn1Spec=AP_REP())[0]
486 cipherText = ap_rep['enc-part']['cipher']
488 # Key Usage 12
489 # AP-REP encrypted part (includes application session
490 # subkey), encrypted with the application session key
491 # (Section 5.5.2)
492 plainText = cipher.decrypt(sessionKey, 12, cipherText)
494 encAPRepPart = decoder.decode(plainText, asn1Spec = EncAPRepPart())[0]
496 cipher = _enctype_table[int(encAPRepPart['subkey']['keytype'])]()
497 sessionKey2 = Key(cipher.enctype, encAPRepPart['subkey']['keyvalue'].asOctets())
499 sequenceNumber = int(encAPRepPart['seq-number'])
501 encAPRepPart['subkey'].clear()
502 encAPRepPart = encAPRepPart.clone()
504 now = datetime.datetime.utcnow()
505 encAPRepPart['cusec'] = now.microsecond
506 encAPRepPart['ctime'] = KerberosTime.to_asn1(now)
507 encAPRepPart['seq-number'] = sequenceNumber
508 encodedAuthenticator = encoder.encode(encAPRepPart)
510 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 12, encodedAuthenticator, None)
512 ap_rep['enc-part'].clear()
513 ap_rep['enc-part']['etype'] = cipher.enctype
514 ap_rep['enc-part']['cipher'] = encryptedEncodedAuthenticator
516 resp = SPNEGO_NegTokenResp()
517 resp['ResponseToken'] = encoder.encode(ap_rep)
519 return cipher, sessionKey2, resp.getData()
521def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT = None, TGS = None, targetName='',
522 kdcHost = None, useCache = True):
524 # Convert to binary form, just in case we're receiving strings
525 if isinstance(lmhash, str):
526 try:
527 lmhash = unhexlify(lmhash)
528 except TypeError:
529 pass
530 if isinstance(nthash, str):
531 try:
532 nthash = unhexlify(nthash)
533 except TypeError:
534 pass
535 if isinstance(aesKey, str): 535 ↛ 541line 535 didn't jump to line 541, because the condition on line 535 was never false
536 try:
537 aesKey = unhexlify(aesKey)
538 except TypeError:
539 pass
541 if TGT is None and TGS is None: 541 ↛ 578line 541 didn't jump to line 578, because the condition on line 541 was never false
542 if useCache is True: 542 ↛ 578line 542 didn't jump to line 578, because the condition on line 542 was never false
543 try:
544 ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
545 except Exception:
546 # No cache present
547 pass
548 else:
549 # retrieve domain information from CCache file if needed
550 if domain == '':
551 domain = ccache.principal.realm['data'].decode('utf-8')
552 LOG.debug('Domain retrieved from CCache: %s' % domain)
554 LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
555 principal = 'host/%s@%s' % (targetName.upper(), domain.upper())
556 creds = ccache.getCredential(principal)
557 if creds is None:
558 # Let's try for the TGT and go from there
559 principal = 'krbtgt/%s@%s' % (domain.upper(),domain.upper())
560 creds = ccache.getCredential(principal)
561 if creds is not None:
562 TGT = creds.toTGT()
563 LOG.debug('Using TGT from cache')
564 else:
565 LOG.debug("No valid credentials found in cache. ")
566 else:
567 TGS = creds.toTGS(principal)
569 # retrieve user information from CCache file if needed
570 if username == '' and creds is not None:
571 username = creds['client'].prettyPrint().split(b'@')[0].decode('utf-8')
572 LOG.debug('Username retrieved from CCache: %s' % username)
573 elif username == '' and len(ccache.principal.components) > 0:
574 username = ccache.principal.components[0]['data'].decode('utf-8')
575 LOG.debug('Username retrieved from CCache: %s' % username)
577 # First of all, we need to get a TGT for the user
578 userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
579 while True:
580 if TGT is None: 580 ↛ 602line 580 didn't jump to line 602, because the condition on line 580 was never false
581 if TGS is None: 581 ↛ 608line 581 didn't jump to line 608, because the condition on line 581 was never false
582 try:
583 tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
584 except KerberosError as e:
585 if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
586 # We might face this if the target does not support AES
587 # So, if that's the case we'll force using RC4 by converting
588 # the password to lm/nt hashes and hope for the best. If that's already
589 # done, byebye.
590 if lmhash == b'' and nthash == b'' and (aesKey == b'' or aesKey is None) and TGT is None and TGS is None:
591 from impacket.ntlm import compute_lmhash, compute_nthash
592 LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4')
593 lmhash = compute_lmhash(password)
594 nthash = compute_nthash(password)
595 continue
596 else:
597 raise
598 else:
599 raise
601 else:
602 tgt = TGT['KDC_REP']
603 cipher = TGT['cipher']
604 sessionKey = TGT['sessionKey']
606 # Now that we have the TGT, we should ask for a TGS for cifs
608 if TGS is None: 608 ↛ 630line 608 didn't jump to line 630, because the condition on line 608 was never false
609 serverName = Principal('host/%s' % targetName, type=constants.PrincipalNameType.NT_SRV_INST.value)
610 try:
611 tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)
612 except KerberosError as e:
613 if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
614 # We might face this if the target does not support AES
615 # So, if that's the case we'll force using RC4 by converting
616 # the password to lm/nt hashes and hope for the best. If that's already
617 # done, byebye.
618 if lmhash == b'' and nthash == b'' and (aesKey == b'' or aesKey is None) and TGT is None and TGS is None:
619 from impacket.ntlm import compute_lmhash, compute_nthash
620 LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4')
621 lmhash = compute_lmhash(password)
622 nthash = compute_nthash(password)
623 else:
624 raise
625 else:
626 raise
627 else:
628 break
629 else:
630 tgs = TGS['KDC_REP']
631 cipher = TGS['cipher']
632 sessionKey = TGS['sessionKey']
633 break
635 # Let's build a NegTokenInit with a Kerberos REQ_AP
637 blob = SPNEGO_NegTokenInit()
639 # Kerberos
640 blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]
642 # Let's extract the ticket from the TGS
643 tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
644 ticket = Ticket()
645 ticket.from_asn1(tgs['ticket'])
647 # Now let's build the AP_REQ
648 apReq = AP_REQ()
649 apReq['pvno'] = 5
650 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
652 opts = list()
653 opts.append(constants.APOptions.mutual_required.value)
654 apReq['ap-options'] = constants.encodeFlags(opts)
655 seq_set(apReq,'ticket', ticket.to_asn1)
657 authenticator = Authenticator()
658 authenticator['authenticator-vno'] = 5
659 authenticator['crealm'] = domain
660 seq_set(authenticator, 'cname', userName.components_to_asn1)
661 now = datetime.datetime.utcnow()
663 authenticator['cusec'] = now.microsecond
664 authenticator['ctime'] = KerberosTime.to_asn1(now)
667 authenticator['cksum'] = noValue
668 authenticator['cksum']['cksumtype'] = 0x8003
670 chkField = CheckSumField()
671 chkField['Lgth'] = 16
673 chkField['Flags'] = GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DCE_STYLE
674 #chkField['Flags'] = GSS_C_INTEG_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DCE_STYLE
675 authenticator['cksum']['checksum'] = chkField.getData()
676 authenticator['seq-number'] = 0
677 encodedAuthenticator = encoder.encode(authenticator)
679 # Key Usage 11
680 # AP-REQ Authenticator (includes application authenticator
681 # subkey), encrypted with the application session key
682 # (Section 5.5.1)
683 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)
685 apReq['authenticator'] = noValue
686 apReq['authenticator']['etype'] = cipher.enctype
687 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
689 blob['MechToken'] = struct.pack('B', ASN1_AID) + asn1encode( struct.pack('B', ASN1_OID) + asn1encode(
690 TypesMech['KRB5 - Kerberos 5'] ) + KRB5_AP_REQ + encoder.encode(apReq))
692 return cipher, sessionKey, blob.getData()
695class SessionKeyDecryptionError(Exception):
696 """
697 Exception risen when we fail to decrypt a session key within an AS-REP
698 message.
699 It provides context information such as full AS-REP message but also the
700 cipher, key and cipherText used when the error occurred.
701 """
702 def __init__( self, message, asRep, cipher, key, cipherText):
703 self.message = message
704 self.asRep = asRep
705 self.cipher = cipher
706 self.key = key
707 self.cipherText = cipherText
709 def __str__ ( self):
710 return "SessionKeyDecryptionError: %s" % self.message
713class KerberosError(SessionError):
714 """
715 This is the exception every client should catch regardless of the underlying
716 SMB version used. We'll take care of that. NETBIOS exceptions are NOT included,
717 since all SMB versions share the same NETBIOS instances.
718 """
719 def __init__( self, error = 0, packet=0):
720 SessionError.__init__(self)
721 self.error = error
722 self.packet = packet
723 if packet != 0: 723 ↛ exitline 723 didn't return from function '__init__', because the condition on line 723 was never false
724 self.error = self.packet['error-code']
726 def getErrorCode( self ):
727 return self.error
729 def getErrorPacket( self ):
730 return self.packet
732 def getErrorString( self ):
733 return constants.ERROR_MESSAGES[self.error]
735 def __str__( self ):
736 retString = 'Kerberos SessionError: %s(%s)' % (constants.ERROR_MESSAGES[self.error])
737 try:
738 # Let's try to get the NT ERROR, if not, we quit and give the general one
739 if self.error == constants.ErrorCodes.KRB_ERR_GENERIC.value:
740 eData = decoder.decode(self.packet['e-data'], asn1Spec = KERB_ERROR_DATA())[0]
741 nt_error = struct.unpack('<L', eData['data-value'].asOctets()[:4])[0]
742 retString += '\nNT ERROR: %s(%s)' % (nt_errors.ERROR_MESSAGES[nt_error])
743 except:
744 pass
746 return retString