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

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