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

22 

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 

29 

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 

43 

44# Our random number generator 

45try: 

46 rand = random.SystemRandom() 

47except NotImplementedError: 

48 rand = random 

49 pass 

50 

51def sendReceive(data, host, kdcHost): 

52 if kdcHost is None: 

53 targetHost = host 

54 else: 

55 targetHost = kdcHost 

56 

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

58 

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) 

66 

67 s.sendall(messageLen + data) 

68 

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

70 

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

74 

75 try: 

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

77 except: 

78 return r 

79 

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 

92 

93 return r 

94 

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

96 

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 

113 

114 asReq = AS_REQ() 

115 

116 domain = domain.upper() 

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

118 

119 pacRequest = KERB_PA_PAC_REQUEST() 

120 pacRequest['include-pac'] = requestPAC 

121 encodedPacRequest = encoder.encode(pacRequest) 

122 

123 asReq['pvno'] = 5 

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

125 

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 

130 

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

132 

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) 

138 

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

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

141 

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

144 

145 reqBody['realm'] = domain 

146 

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) 

151 

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

155 

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

173 

174 seq_set_iter(reqBody, 'etype', supportedCiphers) 

175 

176 message = encoder.encode(asReq) 

177 

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 

191 

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 

202 

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] 

212 

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

224 

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

236 

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

238 

239 enctype = supportedCiphers[0] 

240 

241 cipher = _enctype_table[enctype] 

242 

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) 

250 

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

254 

255 # Let's build the timestamp 

256 timeStamp = PA_ENC_TS_ENC() 

257 

258 now = datetime.datetime.utcnow() 

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

260 timeStamp['pausec'] = now.microsecond 

261 

262 # Encrypt the shyte 

263 encodedTimeStamp = encoder.encode(timeStamp) 

264 

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) 

269 

270 encryptedData = EncryptedData() 

271 encryptedData['etype'] = cipher.enctype 

272 encryptedData['cipher'] = encriptedTimeStamp 

273 encodedEncryptedData = encoder.encode(encryptedData) 

274 

275 # Now prepare the new AS_REQ again with the PADATA 

276 # ToDo: cannot we reuse the previous one? 

277 asReq = AS_REQ() 

278 

279 asReq['pvno'] = 5 

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

281 

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 

286 

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 

290 

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

292 

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) 

298 

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

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

301 

302 reqBody['realm'] = domain 

303 

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) 

308 

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

310 

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 

321 

322 

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

324 

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

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

327 

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] 

345 

346 # Get the session key and the ticket 

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

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

349 

350 # ToDo: Check Nonces! 

351 

352 return tgt, cipher, key, sessionKey 

353 

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

355 

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] 

361 

362 domain = domain.upper() 

363 # Extract the ticket from the TGT 

364 ticket = Ticket() 

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

366 

367 apReq = AP_REQ() 

368 apReq['pvno'] = 5 

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

370 

371 opts = list() 

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

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

374 

375 authenticator = Authenticator() 

376 authenticator['authenticator-vno'] = 5 

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

378 

379 clientName = Principal() 

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

381 

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

383 

384 now = datetime.datetime.utcnow() 

385 authenticator['cusec'] = now.microsecond 

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

387 

388 encodedAuthenticator = encoder.encode(authenticator) 

389 

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) 

395 

396 apReq['authenticator'] = noValue 

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

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

399 

400 encodedApReq = encoder.encode(apReq) 

401 

402 tgsReq = TGS_REQ() 

403 

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 

410 

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

412 

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 ) 

418 

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

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

421 reqBody['realm'] = domain 

422 

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

424 

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 ) 

435 

436 message = encoder.encode(tgsReq) 

437 

438 r = sendReceive(message, domain, kdcHost) 

439 

440 # Get the session key 

441 

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

443 

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

445 

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) 

450 

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

452 

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

456 

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

461 

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) 

469 

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 

483 

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

485 

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

487 

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) 

493 

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

495 

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

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

498 

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

500 

501 encAPRepPart['subkey'].clear() 

502 encAPRepPart = encAPRepPart.clone() 

503 

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) 

509 

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

511 

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

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

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

515 

516 resp = SPNEGO_NegTokenResp() 

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

518 

519 return cipher, sessionKey2, resp.getData() 

520 

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

522 kdcHost = None, useCache = True): 

523 

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 

540 

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) 

553 

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) 

568 

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) 

576 

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 

600 

601 else: 

602 tgt = TGT['KDC_REP'] 

603 cipher = TGT['cipher'] 

604 sessionKey = TGT['sessionKey'] 

605 

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

607 

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 

634 

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

636 

637 blob = SPNEGO_NegTokenInit() 

638 

639 # Kerberos 

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

641 

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

646 

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) 

651 

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) 

656 

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

662 

663 authenticator['cusec'] = now.microsecond 

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

665 

666 

667 authenticator['cksum'] = noValue 

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

669 

670 chkField = CheckSumField() 

671 chkField['Lgth'] = 16 

672 

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) 

678 

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) 

684 

685 apReq['authenticator'] = noValue 

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

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

688 

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

691 

692 return cipher, sessionKey, blob.getData() 

693 

694 

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 

708 

709 def __str__ ( self): 

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

711 

712 

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

725 

726 def getErrorCode( self ): 

727 return self.error 

728 

729 def getErrorPacket( self ): 

730 return self.packet 

731 

732 def getErrorString( self ): 

733 return constants.ERROR_MESSAGES[self.error] 

734 

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 

745 

746 return retString