Hide keyboard shortcuts

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 

15import datetime 

16import random 

17import socket 

18import struct 

19import os 

20 

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 

26 

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 

40 

41# Our random number generator 

42try: 

43 rand = random.SystemRandom() 

44except NotImplementedError: 

45 rand = random 

46 pass 

47 

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

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

341def 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################################################################################ 

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 

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 

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

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 

695 

696 def __str__ ( self): 

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

698 

699 

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

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