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

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

## 452 statements

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#

14

15 import datetime

16 import random

17 import socket

18 import struct

19 import os

20

21 from pyasn1.codec.der import decoder, encoder

22 from pyasn1.error import PyAsn1Error

23 from pyasn1.type.univ import noValue

24 from six import b

25 from binascii import unhexlify, hexlify

26

27 from 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

30 from impacket.krb5.types import KerberosTime, Principal, Ticket

31 from 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

33 from impacket.krb5 import constants

34 from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum

35 from impacket.smbconnection import SessionError

36 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp, ASN1_OID, asn1encode, ASN1_AID

37 from impacket.krb5.gssapi import KRB5_AP_REQ

38 from impacket import nt_errors, LOG

39 from impacket.krb5.ccache import CCache

40

41# Our random number generator

42 try:

43 rand = random.SystemRandom()

44 except NotImplementedError:

45 rand = random

46 pass

47

48 def sendReceive(data, host, kdcHost):

49 if kdcHost is None:

50 targetHost = host

51 else:

52 targetHost = kdcHost

53

54 messageLen = struct.pack('!i', len(data))

55

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)

63

64 s.sendall(messageLen + data)

65

66 recvDataLen = struct.unpack('!i', s.recv(4))[0]

67

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))

71

72 try:

73 krbError = KerberosError(packet = decoder.decode(r, asn1Spec = KRB_ERROR())[0])

74 except:

75 return r

76

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

79

80 return r

81

82 def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True):

83

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

100

101 asReq = AS_REQ()

102

103 domain = domain.upper()

104 serverName = Principal('krbtgt/%s'%domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)

105

106 pacRequest = KERB_PA_PAC_REQUEST()

107 pacRequest['include-pac'] = requestPAC

108 encodedPacRequest = encoder.encode(pacRequest)

109

110 asReq['pvno'] = 5

111 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)

112

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

117

118 reqBody = seq_set(asReq, 'req-body')

119

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)

125

126 seq_set(reqBody, 'sname', serverName.components_to_asn1)

127 seq_set(reqBody, 'cname', clientName.components_to_asn1)

128

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')

131

132 reqBody['realm'] = domain

133

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)

138

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''

142

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),)

160

161 seq_set_iter(reqBody, 'etype', supportedCiphers)

162

163 message = encoder.encode(asReq)

164

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

178

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

189

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]

199

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 = ''

211

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 = ''

223

224 encryptionTypesData[etype['etype']] = b(salt)

225

226 enctype = supportedCiphers[0]

227

228 cipher = _enctype_table[enctype]

229

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)

237

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!')

241

242 # Let's build the timestamp

243 timeStamp = PA_ENC_TS_ENC()

244

245 now = datetime.datetime.utcnow()

246 timeStamp['patimestamp'] = KerberosTime.to_asn1(now)

247 timeStamp['pausec'] = now.microsecond

248

249 # Encrypt the shyte

250 encodedTimeStamp = encoder.encode(timeStamp)

251

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)

256

257 encryptedData = EncryptedData()

258 encryptedData['etype'] = cipher.enctype

259 encryptedData['cipher'] = encriptedTimeStamp

260 encodedEncryptedData = encoder.encode(encryptedData)

261

262 # Now prepare the new AS_REQ again with the PADATA

263 # ToDo: cannot we reuse the previous one?

264 asReq = AS_REQ()

265

266 asReq['pvno'] = 5

267 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)

268

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

273

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

277

278 reqBody = seq_set(asReq, 'req-body')

279

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)

285

286 seq_set(reqBody, 'sname', serverName.components_to_asn1)

287 seq_set(reqBody, 'cname', clientName.components_to_asn1)

288

289 reqBody['realm'] = domain

290

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)

295

296 seq_set_iter(reqBody, 'etype', ( (int(cipher.enctype),)))

297

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

308

309

310 asRep = decoder.decode(tgt, asn1Spec = AS_REP())[0]

311

312 # So, we have the TGT, now extract the new session key and finish

313 cipherText = asRep['enc-part']['cipher']

314

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]

332

333 # Get the session key and the ticket

334 cipher = _enctype_table[encASRepPart['key']['keytype']]

335 sessionKey = Key(cipher.enctype,encASRepPart['key']['keyvalue'].asOctets())

336

337 # ToDo: Check Nonces!

338

339 return tgt, cipher, key, sessionKey

340

341 def getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey):

342

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]

348

349 domain = domain.upper()

350 # Extract the ticket from the TGT

351 ticket = Ticket()

352 ticket.from_asn1(decodedTGT['ticket'])

353

354 apReq = AP_REQ()

355 apReq['pvno'] = 5

356 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)

357

358 opts = list()

359 apReq['ap-options'] = constants.encodeFlags(opts)

360 seq_set(apReq,'ticket', ticket.to_asn1)

361

362 authenticator = Authenticator()

363 authenticator['authenticator-vno'] = 5

364 authenticator['crealm'] = decodedTGT['crealm'].asOctets()

365

366 clientName = Principal()

367 clientName.from_asn1( decodedTGT, 'crealm', 'cname')

368

369 seq_set(authenticator, 'cname', clientName.components_to_asn1)

370

371 now = datetime.datetime.utcnow()

372 authenticator['cusec'] = now.microsecond

373 authenticator['ctime'] = KerberosTime.to_asn1(now)

374

375 encodedAuthenticator = encoder.encode(authenticator)

376

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)

382

383 apReq['authenticator'] = noValue

384 apReq['authenticator']['etype'] = cipher.enctype

385 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

386

387 encodedApReq = encoder.encode(apReq)

388

389 tgsReq = TGS_REQ()

390

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

397

398 reqBody = seq_set(tgsReq, 'req-body')

399

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 )

405

406 reqBody['kdc-options'] = constants.encodeFlags(opts)

407 seq_set(reqBody, 'sname', serverName.components_to_asn1)

408 reqBody['realm'] = domain

409

410 now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

411

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 )

422

423 message = encoder.encode(tgsReq)

424

425 r = sendReceive(message, domain, kdcHost)

426

427 # Get the session key

428

429 tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]

430

431 cipherText = tgs['enc-part']['cipher']

432

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)

437

438 encTGSRepPart = decoder.decode(plainText, asn1Spec = EncTGSRepPart())[0]

439

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']]

443

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')

448

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)

456

457################################################################################

458# DCE RPC Helpers

459################################################################################

460 def 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

470

471 ap_rep = decoder.decode(negTokenResp['ResponseToken'], asn1Spec=AP_REP())[0]

472

473 cipherText = ap_rep['enc-part']['cipher']

474

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)

480

481 encAPRepPart = decoder.decode(plainText, asn1Spec = EncAPRepPart())[0]

482

483 cipher = _enctype_table[int(encAPRepPart['subkey']['keytype'])]()

484 sessionKey2 = Key(cipher.enctype, encAPRepPart['subkey']['keyvalue'].asOctets())

485

486 sequenceNumber = int(encAPRepPart['seq-number'])

487

488 encAPRepPart['subkey'].clear()

489 encAPRepPart = encAPRepPart.clone()

490

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)

496

497 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 12, encodedAuthenticator, None)

498

499 ap_rep['enc-part'].clear()

500 ap_rep['enc-part']['etype'] = cipher.enctype

501 ap_rep['enc-part']['cipher'] = encryptedEncodedAuthenticator

502

503 resp = SPNEGO_NegTokenResp()

504 resp['ResponseToken'] = encoder.encode(ap_rep)

505

506 return cipher, sessionKey2, resp.getData()

507

508 def getKerberosType1(username, password, domain, lmhash, nthash, aesKey='', TGT = None, TGS = None, targetName='',

509 kdcHost = None, useCache = True):

510

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

527

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)

540

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)

555

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)

563

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

587

588 else:

589 tgt = TGT['KDC_REP']

590 cipher = TGT['cipher']

591 sessionKey = TGT['sessionKey']

592

593 # Now that we have the TGT, we should ask for a TGS for cifs

594

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

621

622 # Let's build a NegTokenInit with a Kerberos REQ_AP

623

624 blob = SPNEGO_NegTokenInit()

625

626 # Kerberos

627 blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]

628

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'])

633

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)

638

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)

643

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()

649

650 authenticator['cusec'] = now.microsecond

651 authenticator['ctime'] = KerberosTime.to_asn1(now)

652

653

654 authenticator['cksum'] = noValue

655 authenticator['cksum']['cksumtype'] = 0x8003

656

657 chkField = CheckSumField()

658 chkField['Lgth'] = 16

659

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)

665

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)

671

672 apReq['authenticator'] = noValue

673 apReq['authenticator']['etype'] = cipher.enctype

674 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

675

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))

678

679 return cipher, sessionKey, blob.getData()

680

681

682 class 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

695

696 def __str__ ( self):

697 return "SessionKeyDecryptionError: %s" % self.message

698

699

700 class 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']

712

713 def getErrorCode( self ):

714 return self.error

715

716 def getErrorPacket( self ):

717 return self.packet

718

719 def getErrorString( self ):

720 return constants.ERROR_MESSAGES[self.error]

721

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

732

733 return retString