1# SPDX-License-Identifier: GPL-2.0-or-later OR MPL-2.0 2# This file is part of Scapy 3# See https://scapy.net/ for more information 4# Copyright (C) Gabriel Potter 5 6# flake8: noqa 7 8""" 9Create/Edit Kerberos ticket using Scapy 10 11See https://scapy.readthedocs.io/en/latest/layers/kerberos.html 12""" 13 14from datetime import datetime, timedelta, timezone 15 16import collections 17import platform 18import struct 19import random 20import re 21 22from scapy.asn1.asn1 import ( 23 ASN1_BIT_STRING, 24 ASN1_GENERAL_STRING, 25 ASN1_GENERALIZED_TIME, 26 ASN1_INTEGER, 27 ASN1_STRING, 28) 29from scapy.compat import bytes_hex, hex_bytes 30from scapy.config import conf 31from scapy.error import log_interactive 32from scapy.fields import ( 33 ByteField, 34 FieldLenField, 35 FlagsField, 36 IntEnumField, 37 IntField, 38 PacketField, 39 PacketListField, 40 ShortEnumField, 41 ShortField, 42 StrLenField, 43 UTCTimeField, 44) 45from scapy.packet import Packet 46from scapy.utils import pretty_list 47 48from scapy.layers.dcerpc import NDRUnion 49from scapy.layers.kerberos import ( 50 AuthorizationData, 51 AuthorizationDataItem, 52 EncTicketPart, 53 EncryptedData, 54 EncryptionKey, 55 KRB_Ticket, 56 KerberosClient, 57 KerberosSSP, 58 PrincipalName, 59 TransitedEncoding, 60 kpasswd, 61 krb_as_req, 62 krb_tgs_req, 63 _AD_TYPES, 64 _ADDR_TYPES, 65 _KRB_E_TYPES, 66 _KRB_S_TYPES, 67 _PRINCIPAL_NAME_TYPES, 68 _TICKET_FLAGS, 69) 70from scapy.layers.msrpce.mspac import ( 71 CLAIM_ENTRY, 72 CLAIMS_ARRAY, 73 CLAIMS_SET, 74 CLAIMS_SET_METADATA, 75 CYPHER_BLOCK, 76 FILETIME, 77 GROUP_MEMBERSHIP, 78 KERB_SID_AND_ATTRIBUTES, 79 KERB_VALIDATION_INFO, 80 PAC_ATTRIBUTES_INFO, 81 PAC_CLIENT_CLAIMS_INFO, 82 PAC_CLIENT_INFO, 83 PAC_INFO_BUFFER, 84 PAC_INFO_BUFFER, 85 PAC_REQUESTOR, 86 PAC_SIGNATURE_DATA, 87 PACTYPE, 88 RPC_SID_IDENTIFIER_AUTHORITY, 89 RPC_UNICODE_STRING, 90 SID, 91 UPN_DNS_INFO, 92 USER_SESSION_KEY, 93 CLAIM_ENTRY_sub2, 94) 95from scapy.layers.smb2 import ( 96 WINNT_SID, 97 WINNT_SID_IDENTIFIER_AUTHORITY, 98) 99 100from scapy.libs.rfc3961 import EncryptionType, Key, _checksums 101 102try: 103 import tkinter as tk 104 import tkinter.simpledialog as tksd 105 from tkinter import ttk 106except ImportError: 107 tk = None 108 109# CCache 110# https://web.mit.edu/kerberos/krb5-latest/doc/formats/ccache_file_format.html (official doc but garbage) 111# https://josefsson.org/shishi/ccache.txt (much better) 112 113 114class CCCountedOctetString(Packet): 115 fields_desc = [ 116 FieldLenField("length", None, length_of="data", fmt="I"), 117 StrLenField("data", b"", length_from=lambda pkt: pkt.length), 118 ] 119 120 def guess_payload_class(self, payload): 121 return conf.padding_layer 122 123 124class CCPrincipal(Packet): 125 fields_desc = [ 126 IntEnumField("name_type", 0, _PRINCIPAL_NAME_TYPES), 127 FieldLenField("num_components", None, count_of="components", fmt="I"), 128 PacketField("realm", CCCountedOctetString(), CCCountedOctetString), 129 PacketListField( 130 "components", 131 [], 132 CCCountedOctetString, 133 count_from=lambda pkt: pkt.num_components, 134 ), 135 ] 136 137 def toPN(self): 138 return "%s@%s" % ( 139 "/".join(x.data.decode() for x in self.components), 140 self.realm.data.decode(), 141 ) 142 143 def guess_payload_class(self, payload): 144 return conf.padding_layer 145 146 147class CCDeltaTime(Packet): 148 fields_desc = [ 149 IntField("time_offset", 0), 150 IntField("usec_offset", 0), 151 ] 152 153 def guess_payload_class(self, payload): 154 return conf.padding_layer 155 156 157class CCHeader(Packet): 158 fields_desc = [ 159 ShortEnumField("tag", 1, {1: "DeltaTime"}), 160 ShortField("taglen", 8), 161 PacketField("tagdata", CCDeltaTime(), CCDeltaTime), 162 ] 163 164 def guess_payload_class(self, payload): 165 return conf.padding_layer 166 167 168class CCKeyBlock(Packet): 169 fields_desc = [ 170 ShortEnumField("keytype", 0, _KRB_E_TYPES), 171 ShortField("etype", 0), 172 FieldLenField("keylen", None, length_of="keyvalue"), 173 StrLenField("keyvalue", b"", length_from=lambda pkt: pkt.keylen), 174 ] 175 176 def toKey(self): 177 return Key(self.keytype, key=self.keyvalue) 178 179 def guess_payload_class(self, payload): 180 return conf.padding_layer 181 182 183class CCAddress(Packet): 184 fields_desc = [ 185 ShortEnumField("addrtype", 0, _ADDR_TYPES), 186 PacketField("address", CCCountedOctetString(), CCCountedOctetString), 187 ] 188 189 def guess_payload_class(self, payload): 190 return conf.padding_layer 191 192 193class CCAuthData(Packet): 194 fields_desc = [ 195 ShortEnumField("authtype", 0, _AD_TYPES), 196 PacketField("authdata", CCCountedOctetString(), CCCountedOctetString), 197 ] 198 199 def guess_payload_class(self, payload): 200 return conf.padding_layer 201 202 203class CCCredential(Packet): 204 fields_desc = [ 205 PacketField("client", CCPrincipal(), CCPrincipal), 206 PacketField("server", CCPrincipal(), CCPrincipal), 207 PacketField("keyblock", CCKeyBlock(), CCKeyBlock), 208 UTCTimeField("authtime", None), 209 UTCTimeField("starttime", None), 210 UTCTimeField("endtime", None), 211 UTCTimeField("renew_till", None), 212 ByteField("is_skey", 0), 213 FlagsField( 214 "ticket_flags", 215 0, 216 32, 217 # stored in reversed byte order (wtf) 218 (_TICKET_FLAGS + [""] * (32 - len(_TICKET_FLAGS)))[::-1], 219 ), 220 FieldLenField("num_address", None, count_of="addrs", fmt="I"), 221 PacketListField("addrs", [], CCAddress, count_from=lambda pkt: pkt.num_address), 222 FieldLenField("num_authdata", None, count_of="authdata", fmt="I"), 223 PacketListField( 224 "authdata", [], CCAuthData, count_from=lambda pkt: pkt.num_authdata 225 ), 226 PacketField("ticket", CCCountedOctetString(), CCCountedOctetString), 227 PacketField("second_ticket", CCCountedOctetString(), CCCountedOctetString), 228 ] 229 230 def guess_payload_class(self, payload): 231 return conf.padding_layer 232 233 def set_from_krb(self, tkt, clientpart, sessionkey, kdcrep): 234 self.ticket.data = bytes(tkt) 235 236 # Set sname 237 self.server.name_type = tkt.sname.nameType.val 238 self.server.realm = CCCountedOctetString(data=tkt.realm.val) 239 self.server.components = [ 240 CCCountedOctetString(data=x.val) for x in tkt.sname.nameString 241 ] 242 243 # Set cname 244 self.client.name_type = clientpart.cname.nameType.val 245 self.client.realm = CCCountedOctetString(data=clientpart.crealm.val) 246 self.client.components = [ 247 CCCountedOctetString(data=x.val) for x in clientpart.cname.nameString 248 ] 249 250 # Set the sessionkey 251 self.keyblock = CCKeyBlock( 252 keytype=sessionkey.etype, 253 keyvalue=sessionkey.key, 254 ) 255 256 # Set timestamps 257 self.authtime = kdcrep.authtime.datetime.timestamp() 258 if kdcrep.starttime is not None: 259 self.starttime = kdcrep.starttime.datetime.timestamp() 260 self.endtime = kdcrep.endtime.datetime.timestamp() 261 if kdcrep.flags.val[8] == "1": # renewable 262 self.renew_till = kdcrep.renewTill.datetime.timestamp() 263 264 # Set flags 265 self.ticket_flags = int(kdcrep.flags.val, 2) 266 267 268class CCache(Packet): 269 fields_desc = [ 270 ShortField("file_format_version", 0x0504), 271 ShortField("headerlen", 0), 272 PacketListField("headers", [], CCHeader, length_from=lambda pkt: pkt.headerlen), 273 PacketField("primary_principal", CCPrincipal(), CCPrincipal), 274 PacketListField("credentials", [], CCCredential), 275 ] 276 277 278# TK scrollFrame (MPL-2.0) 279# Credits to @mp035 280# https://gist.github.com/mp035/9f2027c3ef9172264532fcd6262f3b01 281 282if tk is not None: 283 284 class ScrollFrame(tk.Frame): 285 def __init__(self, parent): 286 super().__init__(parent) 287 288 self.canvas = tk.Canvas(self, borderwidth=0) 289 self.viewPort = ttk.Frame(self.canvas) 290 self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) 291 self.canvas.configure(yscrollcommand=self.vsb.set) 292 293 self.vsb.pack(side="right", fill="y") 294 self.canvas.pack(side="left", fill="both", expand=True) 295 self.canvas_window = self.canvas.create_window( 296 (4, 4), window=self.viewPort, anchor="nw", tags="self.viewPort" 297 ) 298 299 self.viewPort.bind("<Configure>", self.onFrameConfigure) 300 self.canvas.bind("<Configure>", self.onCanvasConfigure) 301 302 self.viewPort.bind("<Enter>", self.onEnter) 303 self.viewPort.bind("<Leave>", self.onLeave) 304 305 self.onFrameConfigure(None) 306 307 def onFrameConfigure(self, event): 308 """Reset the scroll region to encompass the inner frame""" 309 self.canvas.configure(scrollregion=self.canvas.bbox("all")) 310 311 def onCanvasConfigure(self, event): 312 """Reset the canvas window to encompass inner frame when required""" 313 canvas_width = event.width 314 self.canvas.itemconfig(self.canvas_window, width=canvas_width) 315 316 def onMouseWheel(self, event): 317 if platform.system() == "Windows": 318 self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") 319 elif platform.system() == "Darwin": 320 self.canvas.yview_scroll(int(-1 * event.delta), "units") 321 else: 322 if event.num == 4: 323 self.canvas.yview_scroll(-1, "units") 324 elif event.num == 5: 325 self.canvas.yview_scroll(1, "units") 326 327 def onEnter(self, event): 328 if platform.system() == "Linux": 329 self.canvas.bind_all("<Button-4>", self.onMouseWheel) 330 self.canvas.bind_all("<Button-5>", self.onMouseWheel) 331 else: 332 self.canvas.bind_all("<MouseWheel>", self.onMouseWheel) 333 334 def onLeave(self, event): 335 if platform.system() == "Linux": 336 self.canvas.unbind_all("<Button-4>") 337 self.canvas.unbind_all("<Button-5>") 338 else: 339 self.canvas.unbind_all("<MouseWheel>") 340 341 342# Build ticketer 343 344 345class Ticketer: 346 def __init__(self): 347 self._data = collections.defaultdict(dict) 348 self.fname = None 349 self.ccache = CCache() 350 self.hashes_cache = collections.defaultdict(dict) 351 352 def open_file(self, fname): 353 """ 354 Load CCache from file 355 """ 356 self.fname = fname 357 self.hashes_cache = collections.defaultdict(dict) 358 with open(self.fname, "rb") as fd: 359 self.ccache = CCache(fd.read()) 360 361 def save(self, fname=None): 362 """ 363 Save opened CCache file 364 """ 365 if fname: 366 self.fname = fname 367 if not self.fname: 368 raise ValueError("No file opened. Specify the 'fname' argument !") 369 with open(self.fname, "wb") as fd: 370 return fd.write(bytes(self.ccache)) 371 372 def show(self, utc=False): 373 """ 374 Show the content of a CCache 375 """ 376 if not self.ccache.credentials: 377 print("No tickets in CCache !") 378 return 379 else: 380 print("Tickets:") 381 382 def _to_str(x): 383 if x is None: 384 return "None" 385 else: 386 x = datetime.fromtimestamp(x, tz=timezone.utc if utc else None) 387 return x.strftime("%d/%m/%y %H:%M:%S") 388 389 for i, cred in enumerate(self.ccache.credentials): 390 if cred.keyblock.keytype == 0: 391 continue 392 print( 393 "%s. %s -> %s" 394 % ( 395 i, 396 cred.client.toPN(), 397 cred.server.toPN(), 398 ) 399 ) 400 print(cred.sprintf(" %ticket_flags%")) 401 print( 402 pretty_list( 403 [ 404 ( 405 _to_str(cred.starttime), 406 _to_str(cred.endtime), 407 _to_str(cred.renew_till), 408 _to_str(cred.authtime), 409 ) 410 ], 411 [("Start time", "End time", "Renew until", "Auth time")], 412 ) 413 ) 414 print() 415 416 def _prompt(self, msg): 417 try: 418 from prompt_toolkit import prompt 419 420 return prompt(msg) 421 except ImportError: 422 return input(msg) 423 424 def _prompt_hash(self, spn, etype=None, cksumtype=None, hash=None): 425 if etype: 426 hashtype = _KRB_E_TYPES[etype] 427 elif cksumtype: 428 hashtype = _KRB_S_TYPES[cksumtype] 429 else: 430 raise ValueError("No cksumtype nor etype specified") 431 if not hash: 432 if spn in self.hashes_cache and hashtype in self.hashes_cache[spn]: 433 hash = self.hashes_cache[spn][hashtype] 434 else: 435 msg = "Enter the %s hash for %s (as hex): " % (hashtype, spn) 436 hash = hex_bytes(self._prompt(msg)) 437 if ( 438 hash 439 == b"\xaa\xd3\xb45\xb5\x14\x04\xee\xaa\xd3\xb45\xb5\x14\x04\xee" 440 ): 441 log_interactive.warning( 442 "This hash is the LM 'no password' hash. Is that what you intended?" 443 ) 444 key = Key(etype=etype, cksumtype=cksumtype, key=hash) 445 self.hashes_cache[spn][hashtype] = hash 446 if key and etype and key.cksumtype: 447 self.hashes_cache[spn][_KRB_S_TYPES[key.cksumtype]] = hash 448 return key 449 450 def dec_ticket(self, i, key=None, hash=None): 451 """ 452 Get the decrypted ticket by credentials ID 453 """ 454 cred = self.ccache.credentials[i] 455 tkt = KRB_Ticket(cred.ticket.data) 456 if key is None: 457 key = self._prompt_hash( 458 tkt.getSPN(), 459 etype=tkt.encPart.etype.val, 460 hash=hash, 461 ) 462 try: 463 return tkt.encPart.decrypt(key) 464 except Exception: 465 try: 466 del self.hashes_cache[tkt.getSPN()] 467 except IndexError: 468 pass 469 raise 470 471 def update_ticket(self, i, decTkt, resign=False, hash=None, kdc_hash=None): 472 """ 473 Update a decrypted ticket by credentials ID 474 """ 475 # Get CCCredential 476 cred = self.ccache.credentials[i] 477 tkt = KRB_Ticket(cred.ticket.data) 478 479 # Optional: resign the new ticket 480 if resign: 481 # resign the ticket 482 decTkt = self._resign_ticket( 483 decTkt, 484 tkt.getSPN(), 485 hash=hash, 486 kdc_hash=kdc_hash, 487 ) 488 489 # Encrypt the new ticket 490 key = self._prompt_hash( 491 tkt.getSPN(), 492 etype=tkt.encPart.etype.val, 493 hash=hash, 494 ) 495 tkt.encPart.encrypt(key, bytes(decTkt)) 496 497 # Update the CCCredential with the new ticket 498 cred.set_from_krb( 499 tkt, 500 decTkt, 501 decTkt.key.toKey(), 502 decTkt, 503 ) 504 505 def import_krb(self, res, key=None, hash=None, _inplace=None): 506 """ 507 Import the result of krb_[tgs/as]_req or a Ticket into the CCache. 508 509 :param obj: a KRB_Ticket object or a AS_REP/TGS_REP object 510 :param sessionkey: the session key that comes along the ticket 511 """ 512 # Instantiate CCCredential 513 if _inplace is not None: 514 cred = self.ccache.credentials[_inplace] 515 else: 516 cred = CCCredential() 517 518 # Update the cred 519 if isinstance(res, KRB_Ticket): 520 if key is None: 521 key = self._prompt_hash( 522 res.getSPN(), 523 etype=res.encPart.etype.val, 524 hash=hash, 525 ) 526 decTkt = res.encPart.decrypt(key) 527 cred.set_from_krb( 528 res, 529 decTkt, 530 decTkt.key.toKey(), 531 decTkt, 532 ) 533 else: 534 if isinstance(res, KerberosClient.RES_AS_MODE): 535 rep = res.asrep 536 elif isinstance(res, KerberosClient.RES_TGS_MODE): 537 rep = res.tgsrep 538 else: 539 raise ValueError("Unknown type of obj !") 540 cred.set_from_krb( 541 rep.ticket, 542 rep, 543 res.sessionkey, 544 res.kdcrep, 545 ) 546 547 # Append to ccache 548 if _inplace is None: 549 self.ccache.credentials.append(cred) 550 551 def export_krb(self, i): 552 """ 553 Export a full ticket, session key, UPN and SPN. 554 """ 555 cred = self.ccache.credentials[i] 556 return ( 557 KRB_Ticket(cred.ticket.data), 558 cred.keyblock.toKey(), 559 cred.client.toPN(), 560 cred.server.toPN(), 561 ) 562 563 def ssp(self, i): 564 """ 565 Create a KerberosSSP from a ticket 566 """ 567 ticket, sessionkey, upn, spn = self.export_krb(i) 568 return KerberosSSP( 569 ST=ticket, 570 KEY=sessionkey, 571 UPN=upn, 572 SPN=spn, 573 ) 574 575 def _add_cred(self, decTkt, hash=None, kdc_hash=None): 576 """ 577 Add a decoded ticket to the CCache 578 """ 579 cred = CCCredential() 580 etype = ( 581 self._prompt( 582 "What key should we use (AES128-CTS-HMAC-SHA1-96/AES256-CTS-HMAC-SHA1-96/RC4-HMAC) ? [AES256-CTS-HMAC-SHA1-96]: " 583 ) 584 or "AES256-CTS-HMAC-SHA1-96" 585 ) 586 if etype not in _KRB_E_TYPES.values(): 587 print("Unknown keytype") 588 return 589 etype = next(k for k, v in _KRB_E_TYPES.items() if v == etype) 590 cred.ticket.data = bytes( 591 KRB_Ticket( 592 realm=decTkt.crealm, 593 sname=PrincipalName( 594 nameString=[ 595 ASN1_GENERAL_STRING(b"krbtgt"), 596 decTkt.crealm, 597 ], 598 nameType=ASN1_INTEGER(2), # NT-SRV-INST 599 ), 600 encPart=EncryptedData( 601 etype=etype, 602 ), 603 ) 604 ) 605 self.ccache.credentials.append(cred) 606 self.update_ticket( 607 len(self.ccache.credentials) - 1, 608 decTkt, 609 resign=True, 610 hash=hash, 611 kdc_hash=kdc_hash, 612 ) 613 614 def create_ticket(self, **kwargs): 615 """ 616 Create a Kerberos ticket 617 """ 618 user = kwargs.get("user", self._prompt("User [User]: ") or "User") 619 domain = kwargs.get( 620 "domain", (self._prompt("Domain [DOM.LOCAL]: ") or "DOM.LOCAL").upper() 621 ) 622 domain_sid = kwargs.get( 623 "domain_sid", 624 self._prompt("Domain SID [S-1-5-21-1-2-3]: ") or "S-1-5-21-1-2-3", 625 ) 626 group_ids = kwargs.get( 627 "group_ids", 628 [ 629 int(x.strip()) 630 for x in ( 631 self._prompt("Group IDs [513, 512, 520, 518, 519]: ") 632 or "513, 512, 520, 518, 519" 633 ).split(",") 634 ], 635 ) 636 user_id = kwargs.get("user_id", int(self._prompt("User ID [500]: ") or "500")) 637 primary_group_id = kwargs.get( 638 "primary_group_id", int(self._prompt("Primary Group ID [513]: ") or "513") 639 ) 640 extra_sids = kwargs.get("extra_sids", None) 641 if extra_sids is None: 642 extra_sids = self._prompt("Extra SIDs [] :") or [] 643 if extra_sids: 644 extra_sids = [x.strip() for x in extra_sids.split(",")] 645 duration = kwargs.get( 646 "duration", int(self._prompt("Expires in (h) [10]: ") or "10") 647 ) 648 now_time = datetime.now(timezone.utc).replace(microsecond=0) 649 rand = random.SystemRandom() 650 key = Key.random_to_key( 651 EncryptionType.AES256_CTS_HMAC_SHA1_96, rand.randbytes(32) 652 ) 653 store = { 654 # KRB 655 "flags": ASN1_BIT_STRING("01000000111000010000000000000000"), 656 "key": { 657 "keytype": ASN1_INTEGER(key.etype), 658 "keyvalue": ASN1_STRING(key.key), 659 }, 660 "crealm": ASN1_GENERAL_STRING(domain), 661 "cname": { 662 "nameString": [ASN1_GENERAL_STRING(user)], 663 "nameType": ASN1_INTEGER(1), 664 }, 665 "authtime": ASN1_GENERALIZED_TIME(now_time), 666 "starttime": ASN1_GENERALIZED_TIME(now_time + timedelta(hours=duration)), 667 "endtime": ASN1_GENERALIZED_TIME(now_time + timedelta(hours=duration)), 668 "renewTill": ASN1_GENERALIZED_TIME(now_time + timedelta(hours=duration)), 669 # PAC 670 # Validation info 671 "VI.LogonTime": self._time_to_filetime(now_time.timestamp()), 672 "VI.LogoffTime": self._time_to_filetime("NEVER"), 673 "VI.KickOffTime": self._time_to_filetime("NEVER"), 674 "VI.PasswordLastSet": self._time_to_filetime( 675 (now_time - timedelta(hours=10)).timestamp() 676 ), 677 "VI.PasswordCanChange": self._time_to_filetime(0), 678 "VI.PasswordMustChange": self._time_to_filetime("NEVER"), 679 "VI.EffectiveName": user, 680 "VI.FullName": "", 681 "VI.LogonScript": "", 682 "VI.ProfilePath": "", 683 "VI.HomeDirectory": "", 684 "VI.HomeDirectoryDrive": "", 685 "VI.UserSessionKey": b"\x00" * 16, 686 "VI.LogonServer": "", 687 "VI.LogonDomainName": domain.rsplit(".", 1)[0], 688 "VI.LogonCount": 70, 689 "VI.BadPasswordCount": 0, 690 "VI.UserId": user_id, 691 "VI.PrimaryGroupId": primary_group_id, 692 "VI.GroupIds": [ 693 { 694 "RelativeId": x, 695 "Attributes": 7, 696 } 697 for x in group_ids 698 ], 699 "VI.UserFlags": 32, 700 "VI.LogonDomainId": domain_sid, 701 "VI.UserAccountControl": 128, 702 "VI.ExtraSids": [{"Sid": x, "Attributes": 7} for x in extra_sids], 703 "VI.ResourceGroupDomainSid": None, 704 "VI.ResourceGroupIds": [], 705 # Pac Client infos 706 "CI.ClientId": self._utc_to_mstime(now_time.timestamp()), 707 "CI.Name": user, 708 # UPN DNS Info 709 "UPNDNS.Flags": 3, 710 "UPNDNS.Upn": "%s@%s" % (user, domain.lower()), 711 "UPNDNS.DnsDomainName": domain.upper(), 712 "UPNDNS.SamName": user, 713 "UPNDNS.Sid": "%s-%s" % (domain_sid, user_id), 714 # Client Claims 715 "CC.ClaimsArrays": [ 716 { 717 "ClaimsSourceType": 1, 718 "ClaimEntries": [ 719 { 720 "Id": "ad://ext/AuthenticationSilo", 721 "Type": 3, 722 "StringValues": "T0-silo", 723 } 724 ], 725 } 726 ], 727 # Attributes Info 728 "AI.Flags": "PAC_WAS_REQUESTED", 729 # Requestor 730 "REQ.Sid": "%s-%s" % (domain_sid, user_id), 731 # Server Checksum 732 "SC.SignatureType": 16, 733 "SC.Signature": b"\x00" * 12, 734 "SC.RODCIdentifier": b"", 735 # KDC Checksum 736 "KC.SignatureType": 16, 737 "KC.Signature": b"\x00" * 12, 738 "KC.RODCIdentifier": b"", 739 # Ticket Checksum 740 "TKT.SignatureType": -1, 741 "TKT.Signature": b"\x00" * 12, 742 "TKT.RODCIdentifier": b"", 743 # Extended KDC Checksum 744 "EXKC.SignatureType": -1, 745 "EXKC.Signature": b"\x00" * 12, 746 "EXKC.RODCIdentifier": b"", 747 } 748 # Build & store ticket 749 tkt = self._build_ticket(store) 750 self._add_cred(tkt) 751 752 def _build_sid(self, sidstr, msdn=False): 753 if not sidstr: 754 return None 755 m = re.match(r"S-(\d+)-(\d+)-?((?:\d+-?)*)", sidstr.strip()) 756 if not m: 757 raise ValueError("Invalid SID format: %s" % sidstr) 758 subauthors = [] 759 if m.group(3): 760 subauthors = [int(x) for x in m.group(3).split("-")] 761 if msdn: 762 return WINNT_SID( 763 Revision=int(m.group(1)), 764 IdentifierAuthority=WINNT_SID_IDENTIFIER_AUTHORITY( 765 Value=struct.pack(">Q", int(m.group(2)))[2:], 766 ), 767 SubAuthority=subauthors, 768 ) 769 else: 770 return SID( 771 Revision=int(m.group(1)), 772 IdentifierAuthority=RPC_SID_IDENTIFIER_AUTHORITY( 773 Value=struct.pack(">Q", int(m.group(2)))[2:] 774 ), 775 SubAuthority=subauthors, 776 ) 777 778 def _build_ticket(self, store): 779 if store["CC.ClaimsArrays"]: 780 claimSet = CLAIMS_SET( 781 ndr64=False, 782 ClaimsArrays=[ 783 CLAIMS_ARRAY( 784 usClaimsSourceType=ca["ClaimsSourceType"], 785 ClaimEntries=[ 786 CLAIM_ENTRY( 787 Id=ce["Id"], 788 Type=ce["Type"], 789 Values=NDRUnion( 790 tag=ce["Type"], 791 value=CLAIM_ENTRY_sub2( 792 ValueCount=ce["StringValues"].count(";") + 1, 793 StringValues=ce["StringValues"].split(";"), 794 ), 795 ), 796 ) 797 for ce in ca["ClaimEntries"] 798 ], 799 ) 800 for ca in store["CC.ClaimsArrays"] 801 ], 802 usReservedType=0, 803 ulReservedFieldSize=0, 804 ReservedField=None, 805 ) 806 else: 807 claimSet = None 808 _signature_set = lambda x: store[x + ".SignatureType"] != -1 809 return EncTicketPart( 810 transited=TransitedEncoding( 811 trType=ASN1_INTEGER(0), contents=ASN1_STRING(b"") 812 ), 813 addresses=None, 814 flags=store["flags"], 815 key=EncryptionKey( 816 keytype=store["key"]["keytype"], 817 keyvalue=store["key"]["keyvalue"], 818 ), 819 crealm=store["crealm"], 820 cname=PrincipalName( 821 nameString=store["cname"]["nameString"], 822 nameType=store["cname"]["nameType"], 823 ), 824 authtime=store["authtime"], 825 starttime=store["starttime"], 826 endtime=store["endtime"], 827 renewTill=store["renewTill"], 828 authorizationData=AuthorizationData( 829 seq=[ 830 AuthorizationDataItem( 831 adType=ASN1_INTEGER(1), 832 adData=AuthorizationData( 833 seq=[ 834 AuthorizationDataItem( 835 adType="AD-WIN2K-PAC", 836 adData=PACTYPE( 837 Buffers=[ 838 PAC_INFO_BUFFER( 839 ulType="Logon information", 840 ), 841 ] 842 + ( 843 [ 844 PAC_INFO_BUFFER( 845 ulType="Server Signature", 846 ), 847 ] 848 if _signature_set("SC") 849 else [] 850 ) 851 + ( 852 [ 853 PAC_INFO_BUFFER( 854 ulType="KDC Signature", 855 ), 856 ] 857 if _signature_set("KC") 858 else [] 859 ) 860 + [ 861 PAC_INFO_BUFFER( 862 ulType="Client name and ticket information", 863 ), 864 PAC_INFO_BUFFER( 865 ulType="UPN and DNS information", 866 ), 867 ] 868 + ( 869 [ 870 PAC_INFO_BUFFER( 871 ulType="Client claims information", 872 ), 873 ] 874 if claimSet 875 else [] 876 ) 877 + ( 878 [ 879 PAC_INFO_BUFFER( 880 ulType="PAC Attributes", 881 ), 882 ] 883 if store["AI.Flags"] 884 else [] 885 ) 886 + ( 887 [ 888 PAC_INFO_BUFFER( 889 ulType="PAC Requestor", 890 ), 891 ] 892 if store["REQ.Sid"] 893 else [] 894 ) 895 + ( 896 [ 897 PAC_INFO_BUFFER( 898 ulType="Ticket Signature", 899 ), 900 ] 901 if _signature_set("TKT") 902 else [] 903 ) 904 + ( 905 [ 906 PAC_INFO_BUFFER( 907 ulType="Extended KDC Signature", 908 ), 909 ] 910 if _signature_set("EXKC") 911 else [] 912 ), 913 Payloads=[ 914 KERB_VALIDATION_INFO( 915 ndr64=False, 916 ndrendian="little", 917 LogonTime=store["VI.LogonTime"], 918 LogoffTime=store["VI.LogoffTime"], 919 KickOffTime=store["VI.KickOffTime"], 920 PasswordLastSet=store[ 921 "VI.PasswordLastSet" 922 ], 923 PasswordCanChange=store[ 924 "VI.PasswordCanChange" 925 ], 926 PasswordMustChange=store[ 927 "VI.PasswordMustChange" 928 ], 929 EffectiveName=RPC_UNICODE_STRING( 930 Buffer=store["VI.EffectiveName"], 931 ), 932 FullName=RPC_UNICODE_STRING( 933 Buffer=store["VI.FullName"], 934 ), 935 LogonScript=RPC_UNICODE_STRING( 936 Buffer=store["VI.LogonScript"], 937 ), 938 ProfilePath=RPC_UNICODE_STRING( 939 Buffer=store["VI.ProfilePath"], 940 ), 941 HomeDirectory=RPC_UNICODE_STRING( 942 Buffer=store["VI.HomeDirectory"], 943 ), 944 HomeDirectoryDrive=RPC_UNICODE_STRING( 945 Buffer=store[ 946 "VI.HomeDirectoryDrive" 947 ], 948 ), 949 UserSessionKey=USER_SESSION_KEY( 950 data=[ 951 CYPHER_BLOCK( 952 data=store[ 953 "VI.UserSessionKey" 954 ][:8] 955 ), 956 CYPHER_BLOCK( 957 data=store[ 958 "VI.UserSessionKey" 959 ][8:] 960 ), 961 ] 962 ), 963 LogonServer=RPC_UNICODE_STRING( 964 Buffer=store["VI.LogonServer"], 965 ), 966 LogonDomainName=RPC_UNICODE_STRING( 967 Buffer=store["VI.LogonDomainName"], 968 ), 969 LogonCount=store["VI.LogonCount"], 970 BadPasswordCount=store[ 971 "VI.BadPasswordCount" 972 ], 973 UserId=store["VI.UserId"], 974 PrimaryGroupId=store[ 975 "VI.PrimaryGroupId" 976 ], 977 GroupIds=[ 978 GROUP_MEMBERSHIP( 979 RelativeId=x["RelativeId"], 980 Attributes=x["Attributes"], 981 ) 982 for x in store["VI.GroupIds"] 983 ], 984 UserFlags=store["VI.UserFlags"], 985 LogonDomainId=self._build_sid( 986 store["VI.LogonDomainId"] 987 ), 988 Reserved1=[0, 0], 989 UserAccountControl=store[ 990 "VI.UserAccountControl" 991 ], 992 Reserved3=[0, 0, 0, 0, 0, 0, 0], 993 ExtraSids=[ 994 KERB_SID_AND_ATTRIBUTES( 995 Sid=self._build_sid(x["Sid"]), 996 Attributes=x["Attributes"], 997 ) 998 for x in store["VI.ExtraSids"] 999 ] 1000 if store["VI.ExtraSids"] 1001 else None, 1002 ResourceGroupDomainSid=self._build_sid( 1003 store["VI.ResourceGroupDomainSid"] 1004 ), 1005 ResourceGroupIds=[ 1006 GROUP_MEMBERSHIP( 1007 RelativeId=x["RelativeId"], 1008 Attributes=x["Attributes"], 1009 ) 1010 for x in store[ 1011 "VI.ResourceGroupIds" 1012 ] 1013 ] 1014 if store["VI.ResourceGroupIds"] 1015 else None, 1016 ), 1017 ] 1018 + ( 1019 [ 1020 PAC_SIGNATURE_DATA( 1021 SignatureType=store[ 1022 "SC.SignatureType" 1023 ], 1024 Signature=store["SC.Signature"], 1025 RODCIdentifier=store[ 1026 "SC.RODCIdentifier" 1027 ], 1028 ), 1029 ] 1030 if _signature_set("SC") 1031 else [] 1032 ) 1033 + ( 1034 [ 1035 PAC_SIGNATURE_DATA( 1036 SignatureType=store[ 1037 "KC.SignatureType" 1038 ], 1039 Signature=store["KC.Signature"], 1040 RODCIdentifier=store[ 1041 "KC.RODCIdentifier" 1042 ], 1043 ), 1044 ] 1045 if _signature_set("KC") 1046 else [] 1047 ) 1048 + [ 1049 PAC_CLIENT_INFO( 1050 ClientId=store["CI.ClientId"], 1051 Name=store["CI.Name"], 1052 ), 1053 UPN_DNS_INFO( 1054 Flags=store["UPNDNS.Flags"], 1055 Payload=[ 1056 ( 1057 "Upn", 1058 store["UPNDNS.Upn"], 1059 ), 1060 ( 1061 "DnsDomainName", 1062 store["UPNDNS.DnsDomainName"], 1063 ), 1064 ( 1065 "SamName", 1066 store["UPNDNS.SamName"], 1067 ), 1068 ( 1069 "Sid", 1070 self._build_sid( 1071 store["UPNDNS.Sid"], 1072 msdn=True, 1073 ), 1074 ), 1075 ], 1076 ), 1077 ] 1078 + ( 1079 [ 1080 PAC_CLIENT_CLAIMS_INFO( 1081 ndr64=False, 1082 Claims=CLAIMS_SET_METADATA( 1083 ClaimsSet=[ 1084 claimSet, 1085 ], 1086 usCompressionFormat=0, 1087 usReservedType=0, 1088 ulReservedFieldSize=0, 1089 ReservedField=None, 1090 ), 1091 ), 1092 ] 1093 if claimSet 1094 else [] 1095 ) 1096 + ( 1097 [ 1098 PAC_ATTRIBUTES_INFO( 1099 Flags=[store["AI.Flags"]], 1100 FlagsLength=2, 1101 ) 1102 ] 1103 if store["AI.Flags"] 1104 else [] 1105 ) 1106 + ( 1107 [ 1108 PAC_REQUESTOR( 1109 Sid=self._build_sid( 1110 store["REQ.Sid"], msdn=True 1111 ), 1112 ), 1113 ] 1114 if store["REQ.Sid"] 1115 else [] 1116 ) 1117 + ( 1118 [ 1119 PAC_SIGNATURE_DATA( 1120 SignatureType=store[ 1121 "TKT.SignatureType" 1122 ], 1123 Signature=store["TKT.Signature"], 1124 RODCIdentifier=store[ 1125 "TKT.RODCIdentifier" 1126 ], 1127 ), 1128 ] 1129 if _signature_set("TKT") 1130 else [] 1131 ) 1132 + ( 1133 [ 1134 PAC_SIGNATURE_DATA( 1135 SignatureType=store[ 1136 "EXKC.SignatureType" 1137 ], 1138 Signature=store["EXKC.Signature"], 1139 RODCIdentifier=store[ 1140 "EXKC.RODCIdentifier" 1141 ], 1142 ) 1143 ] 1144 if _signature_set("EXKC") 1145 else [] 1146 ), 1147 ), 1148 ) 1149 ] 1150 ), 1151 ) 1152 ] 1153 ), 1154 ) 1155 1156 def _getPayloadIfExist(self, pac, ulType): 1157 for i, buf in enumerate(pac.Buffers): 1158 if buf.ulType == ulType: 1159 return pac.Payloads[i] 1160 return None 1161 1162 def _make_fields(self, element, fields, datastore=None): 1163 frm = ttk.Frame(element) 1164 frm.pack(fill="x") 1165 for i, fld in enumerate(fields): 1166 (self._data if datastore is None else datastore)[fld[0]] = v = tk.StringVar( 1167 frm, value=fld[1] 1168 ) 1169 ttk.Label(frm, text=fld[0]).grid(row=i, column=0, sticky="w") 1170 ttk.Entry(frm, textvariable=v).grid(row=i, column=1, sticky="e") 1171 frm.grid_columnconfigure(1, weight=1) 1172 1173 def _make_checkbox(self, element, keys, flags, datastore): 1174 for flg in keys: 1175 datastore[flg] = v = tk.BooleanVar(value=flg in flags) 1176 tk.Checkbutton(element, text=flg, variable=v, anchor=tk.W).pack( 1177 fill="x", padx=5, pady=1 1178 ) 1179 1180 def _make_table(self, element, name, headers, lst, datastore=None): 1181 wrap = ttk.LabelFrame(element, text=name) 1182 tree = ttk.Treeview(wrap, column=headers, show="headings", height=4) 1183 vsb = ttk.Scrollbar(wrap, orient="vertical", command=tree.yview) 1184 vsb.pack(side="right", fill="y") 1185 tree.configure(yscrollcommand=vsb.set) 1186 for h in headers: 1187 tree.column(h, anchor=tk.CENTER) 1188 tree.heading(h, text=h) 1189 for i, row in enumerate(lst): 1190 tree.insert(parent="", index="end", iid=i, values=row) 1191 tree.pack(fill="x", padx=10, pady=10) 1192 1193 def _update_datastore(): 1194 children = [tree.item(x, "values") for x in tree.get_children()] 1195 (self._data if datastore is None else datastore)[name] = children 1196 1197 _update_datastore() 1198 1199 class EditDialog(tksd.Dialog): 1200 def __init__(self, *args, **kwargs): 1201 self.data = {} 1202 self.initial_values = kwargs.pop("values", {}) 1203 self.success = False 1204 super(EditDialog, self).__init__(*args, **kwargs) 1205 1206 def body(diag, frame): 1207 self._make_fields( 1208 frame, 1209 [(x, diag.initial_values.get(x, "")) for x in headers], 1210 datastore=diag.data, 1211 ) 1212 return frame 1213 1214 def ok(self, *args, **kwargs): 1215 self.success = True 1216 super(EditDialog, self).ok(*args, **kwargs) 1217 1218 def values(self): 1219 return tuple(x.get() for x in self.data.values()) 1220 1221 def add(): 1222 dialog = EditDialog(title="Add", parent=tree) 1223 if dialog.success: 1224 i = len(tree.get_children()) 1225 tree.insert(parent="", index="end", iid=i, values=dialog.values()) 1226 _update_datastore() 1227 1228 def edit(): 1229 selected = tree.focus() 1230 if not selected: 1231 return 1232 values = dict(zip(headers, tree.item(selected, "values"))) 1233 dialog = EditDialog(title="Edit", parent=tree, values=values) 1234 if dialog.success: 1235 tree.item(selected, values=dialog.values()) 1236 _update_datastore() 1237 1238 def remove(): 1239 selected = tree.focus() 1240 if selected: 1241 tree.delete(selected) 1242 _update_datastore() 1243 1244 btns = ttk.Frame(wrap) 1245 ttk.Button(btns, text="Add", command=add).grid(row=0, column=0, padx=10) 1246 ttk.Button(btns, text="Edit", command=edit).grid(row=0, column=1, padx=10) 1247 ttk.Button(btns, text="Remove", command=remove).grid(row=0, column=2, padx=10) 1248 btns.pack() 1249 wrap.pack(fill="x") 1250 1251 def _make_list(self, element, func, key, fields_list, new_values): 1252 tbl = ttk.Frame(element) 1253 tbl.pack() 1254 1255 self._data[key] = data = collections.defaultdict(dict) 1256 1257 def append(val): 1258 i = tbl.grid_size()[1] 1259 elt = ttk.Frame(tbl, style="BorderFrame.TFrame") 1260 elt.grid(padx=10, pady=10, row=i, column=0) 1261 func(elt, val, data[i]) 1262 1263 for val in fields_list: 1264 append(val) 1265 1266 def add(): 1267 append(new_values.copy()) 1268 1269 def delete(): 1270 slavescount = len(tbl.grid_slaves()) 1271 i = tksd.askinteger( 1272 "Delete", 1273 "Input the index of the Claim to delete [0-%s]" % (slavescount - 1), 1274 parent=tbl, 1275 ) 1276 if i is None or i > slavescount - 1: 1277 return 1278 tbl.grid_slaves(row=i, column=0)[0].destroy() 1279 del data[i] 1280 1281 btns = ttk.Frame(element) 1282 ttk.Button(btns, text="Add", command=add).grid(row=0, column=0, padx=10) 1283 ttk.Button(btns, text="Delete", command=delete).grid(row=0, column=1, padx=10) 1284 btns.pack() 1285 1286 _TIME_FIELD = UTCTimeField( 1287 "", 1288 None, 1289 fmt="<Q", 1290 epoch=[1601, 1, 1, 0, 0, 0], 1291 custom_scaling=1e7, 1292 strf="%Y-%m-%d %H:%M:%S", 1293 ) 1294 1295 def _pretty_time(self, x): 1296 return self._TIME_FIELD.i2repr(None, x).rsplit(" ", 1)[0] 1297 1298 def _utc_to_mstime(self, x): 1299 return int((x - self._TIME_FIELD.delta) * 1e7) 1300 1301 def _time_to_int(self, x): 1302 return self._utc_to_mstime( 1303 datetime.strptime(x, self._TIME_FIELD.strf).timestamp() 1304 ) 1305 1306 def _time_to_asn1(self, x): 1307 return ASN1_GENERALIZED_TIME(datetime.strptime(x, self._TIME_FIELD.strf)) 1308 1309 def _time_to_filetime(self, x): 1310 if isinstance(x, str) and x.strip() == "NEVER": 1311 return FILETIME(dwHighDateTime=0x7FFFFFFF, dwLowDateTime=0xFFFFFFFF) 1312 if isinstance(x, str): 1313 x = self._time_to_int(x) 1314 else: 1315 x = self._utc_to_mstime(x) 1316 return FILETIME( 1317 dwHighDateTime=(x >> 32) & 0xFFFFFFFF, 1318 dwLowDateTime=x & 0xFFFFFFFF, 1319 ) 1320 1321 def _filetime_totime(self, x): 1322 if x.dwHighDateTime == 0x7FFFFFFF and x.dwLowDateTime == 0xFFFFFFFF: 1323 return "NEVER" 1324 return self._pretty_time((x.dwHighDateTime << 32) + x.dwLowDateTime) 1325 1326 def _pretty_sid(self, sid): 1327 if not sid or not sid.IdentifierAuthority.Value: 1328 return "" 1329 return sid.summary() 1330 1331 def _getLogonInformation(self, pac, element): 1332 logonInfo = self._getPayloadIfExist(pac, 0x00000001) 1333 if not logonInfo: 1334 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000001)) 1335 logonInfo = KERB_VALIDATION_INFO() 1336 else: 1337 logonInfo = logonInfo.value 1338 self._make_fields( 1339 element, 1340 [ 1341 ("LogonTime", self._filetime_totime(logonInfo.LogonTime)), 1342 ("LogoffTime", self._filetime_totime(logonInfo.LogoffTime)), 1343 ("KickOffTime", self._filetime_totime(logonInfo.KickOffTime)), 1344 ( 1345 "PasswordLastSet", 1346 self._filetime_totime(logonInfo.PasswordLastSet), 1347 ), 1348 ( 1349 "PasswordCanChange", 1350 self._filetime_totime(logonInfo.PasswordCanChange), 1351 ), 1352 ( 1353 "PasswordMustChange", 1354 self._filetime_totime(logonInfo.PasswordMustChange), 1355 ), 1356 ( 1357 "EffectiveName", 1358 logonInfo.EffectiveName.Buffer.value.value[0].value.decode(), 1359 ), 1360 ( 1361 "FullName", 1362 logonInfo.FullName.Buffer.value.value[0].value.decode(), 1363 ), 1364 ( 1365 "LogonScript", 1366 logonInfo.LogonScript.Buffer.value.value[0].value.decode(), 1367 ), 1368 ( 1369 "ProfilePath", 1370 logonInfo.ProfilePath.Buffer.value.value[0].value.decode(), 1371 ), 1372 ( 1373 "HomeDirectory", 1374 logonInfo.HomeDirectory.Buffer.value.value[0].value.decode(), 1375 ), 1376 ( 1377 "HomeDirectoryDrive", 1378 logonInfo.HomeDirectoryDrive.Buffer.value.value[0].value.decode(), 1379 ), 1380 ("LogonCount", str(logonInfo.LogonCount)), 1381 ("BadPasswordCount", str(logonInfo.BadPasswordCount)), 1382 ("UserId", str(logonInfo.UserId)), 1383 ("PrimaryGroupId", str(logonInfo.PrimaryGroupId)), 1384 ], 1385 ) 1386 self._make_table( 1387 element, 1388 "GroupIds", 1389 ["RelativeId", "Attributes"], 1390 [ 1391 (str(x.RelativeId), str(x.Attributes)) 1392 for x in logonInfo.GroupIds.value.value 1393 ], 1394 ) 1395 self._make_fields( 1396 element, 1397 [ 1398 ("UserFlags", str(logonInfo.UserFlags)), 1399 ( 1400 "UserSessionKey", 1401 bytes_hex( 1402 b"".join(x.data for x in logonInfo.UserSessionKey.data) 1403 ).decode(), 1404 ), 1405 ( 1406 "LogonServer", 1407 logonInfo.LogonServer.Buffer.value.value[0].value.decode(), 1408 ), 1409 ( 1410 "LogonDomainName", 1411 logonInfo.LogonDomainName.Buffer.value.value[0].value.decode(), 1412 ), 1413 ( 1414 "LogonDomainId", 1415 self._pretty_sid(logonInfo.LogonDomainId.value), 1416 ), 1417 ("UserAccountControl", str(logonInfo.UserAccountControl)), 1418 ], 1419 ) 1420 self._make_table( 1421 element, 1422 "ExtraSids", 1423 ["Sid", "Attributes"], 1424 [ 1425 (self._pretty_sid(x.Sid.value), str(x.Attributes)) 1426 for x in ( 1427 logonInfo.ExtraSids.value.value if logonInfo.ExtraSids else [] 1428 ) 1429 ], 1430 ) 1431 self._make_fields( 1432 element, 1433 [ 1434 ( 1435 "ResourceGroupDomainSid", 1436 self._pretty_sid( 1437 logonInfo.ResourceGroupDomainSid.value 1438 if logonInfo.ResourceGroupDomainSid 1439 else None 1440 ), 1441 ), 1442 ], 1443 ) 1444 self._make_table( 1445 element, 1446 "ResourceGroupIds", 1447 ["RelativeId", "Attributes"], 1448 [ 1449 (str(x.RelativeId), str(x.Attributes)) 1450 for x in ( 1451 logonInfo.ResourceGroupIds.value.value 1452 if logonInfo.ResourceGroupIds 1453 else [] 1454 ) 1455 ], 1456 ) 1457 1458 def _getClientInfo(self, pac, element): 1459 clientInfo = self._getPayloadIfExist(pac, 0x0000000A) 1460 if not clientInfo: 1461 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x0000000A)) 1462 clientInfo = PAC_CLIENT_INFO() 1463 return self._make_fields( 1464 element, 1465 [ 1466 ("ClientId", self._pretty_time(clientInfo.ClientId)), 1467 ("Name", clientInfo.Name), 1468 ], 1469 ) 1470 1471 def _getUPNDnsInfo(self, pac, element): 1472 upndnsinfo = self._getPayloadIfExist(pac, 0x0000000C) 1473 if not upndnsinfo: 1474 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x0000000C)) 1475 upndnsinfo = UPN_DNS_INFO() 1476 return self._make_fields( 1477 element, 1478 [ 1479 ("Upn", upndnsinfo.Upn), 1480 ("DnsDomainName", upndnsinfo.DnsDomainName), 1481 ( 1482 "SamName", 1483 upndnsinfo.SamName 1484 if upndnsinfo.Flags.S and upndnsinfo.SamNameLen 1485 else "", 1486 ), 1487 ( 1488 "UpnDnsSid", 1489 self._pretty_sid(upndnsinfo.Sid) 1490 if upndnsinfo.Flags.S and upndnsinfo.SidLen 1491 else "", 1492 ), 1493 ], 1494 ) 1495 1496 def _getClientClaims(self, pac, element): 1497 clientClaims = self._getPayloadIfExist(pac, 0x0000000D) 1498 if not clientClaims or isinstance(clientClaims, conf.padding_layer): 1499 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x0000000D)) 1500 claimsArray = [] 1501 else: 1502 claimsArray = ( 1503 clientClaims.value.valueof("Claims") 1504 .valueof("ClaimsSet") 1505 .value.valueof("ClaimsArrays") 1506 ) 1507 1508 def func(elt, x, datastore): 1509 self._make_fields( 1510 elt, 1511 [ 1512 ("ClaimsSourceType", str(x.usClaimsSourceType)), 1513 ], 1514 datastore=datastore, 1515 ) 1516 self._make_table( 1517 elt, 1518 "ClaimEntries", 1519 ["Id", "Type", "Values"], 1520 [ 1521 ( 1522 y.valueof("Id").decode(), 1523 str(y.Type), 1524 ";".join( 1525 z.decode() 1526 for z in y.valueof("Values").valueof("StringValues") 1527 ), 1528 ) 1529 for y in x.valueof("ClaimEntries") 1530 ], 1531 datastore=datastore, 1532 ) 1533 1534 return self._make_list( 1535 element, 1536 func=func, 1537 key="ClaimsArrays", 1538 fields_list=claimsArray, 1539 new_values=CLAIMS_ARRAY(ClaimEntries=[]), 1540 ) 1541 1542 def _getPACAttributes(self, pac, element): 1543 pacAttributes = self._getPayloadIfExist(pac, 0x00000011) 1544 if not pacAttributes: 1545 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000011)) 1546 pacAttributes = PAC_ATTRIBUTES_INFO(Flags=0) 1547 flags = str(pacAttributes.Flags[0]).split("+") 1548 self._data["pacAttributes"] = {} 1549 self._make_checkbox( 1550 element, 1551 [ 1552 "PAC_WAS_REQUESTED", 1553 "PAC_WAS_GIVEN_IMPLICITLY", 1554 ], 1555 flags, 1556 self._data["pacAttributes"], 1557 ) 1558 1559 def _getPACRequestor(self, pac, element): 1560 pacRequestor = self._getPayloadIfExist(pac, 0x00000012) 1561 if not pacRequestor: 1562 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000012)) 1563 pacRequestor = PAC_REQUESTOR() 1564 return self._make_fields( 1565 element, [("ReqSid", self._pretty_sid(pacRequestor.Sid))] 1566 ) 1567 1568 def _getServerChecksum(self, pac, element): 1569 serverChecksum = self._getPayloadIfExist(pac, 0x00000006) 1570 if not serverChecksum: 1571 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000006)) 1572 serverChecksum = PAC_SIGNATURE_DATA() 1573 return self._make_fields( 1574 element, 1575 [ 1576 ( 1577 "SRVSignatureType", 1578 str(serverChecksum.SignatureType) 1579 if serverChecksum.SignatureType is not None 1580 else "", 1581 ), 1582 ("SRVSignature", bytes_hex(serverChecksum.Signature).decode()), 1583 ("SRVRODCIdentifier", serverChecksum.RODCIdentifier.decode()), 1584 ], 1585 ) 1586 1587 def _getKDCChecksum(self, pac, element): 1588 kdcChecksum = self._getPayloadIfExist(pac, 0x00000007) 1589 if not kdcChecksum: 1590 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000007)) 1591 kdcChecksum = PAC_SIGNATURE_DATA() 1592 return self._make_fields( 1593 element, 1594 [ 1595 ( 1596 "KDCSignatureType", 1597 str(kdcChecksum.SignatureType) 1598 if kdcChecksum.SignatureType is not None 1599 else "", 1600 ), 1601 ("KDCSignature", bytes_hex(kdcChecksum.Signature).decode()), 1602 ("KDCRODCIdentifier", kdcChecksum.RODCIdentifier.decode()), 1603 ], 1604 ) 1605 1606 def _getTicketChecksum(self, pac, element): 1607 ticketChecksum = self._getPayloadIfExist(pac, 0x00000010) 1608 if not ticketChecksum: 1609 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000010)) 1610 ticketChecksum = PAC_SIGNATURE_DATA() 1611 return self._make_fields( 1612 element, 1613 [ 1614 ( 1615 "TKTSignatureType", 1616 str(ticketChecksum.SignatureType) 1617 if ticketChecksum.SignatureType is not None 1618 else "", 1619 ), 1620 ("TKTSignature", bytes_hex(ticketChecksum.Signature).decode()), 1621 ("TKTRODCIdentifier", ticketChecksum.RODCIdentifier.decode()), 1622 ], 1623 ) 1624 1625 def _getExtendedKDCChecksum(self, pac, element): 1626 exkdcChecksum = self._getPayloadIfExist(pac, 0x00000013) 1627 if not exkdcChecksum: 1628 pac.Buffers.append(PAC_INFO_BUFFER(ulType=0x00000013)) 1629 exkdcChecksum = PAC_SIGNATURE_DATA() 1630 return self._make_fields( 1631 element, 1632 [ 1633 ( 1634 "EXKDCSignatureType", 1635 str(exkdcChecksum.SignatureType) 1636 if exkdcChecksum.SignatureType is not None 1637 else "", 1638 ), 1639 ("EXKDCSignature", bytes_hex(exkdcChecksum.Signature).decode()), 1640 ("EXKDCRODCIdentifier", exkdcChecksum.RODCIdentifier.decode()), 1641 ], 1642 ) 1643 1644 def edit_ticket(self, i, key=None, hash=None): 1645 """ 1646 Edit a Kerberos ticket using the GUI 1647 """ 1648 if tk is None: 1649 raise ImportError( 1650 "tkinter is not installed (`apt install python3-tk` on debian)" 1651 ) 1652 tkt = self.dec_ticket(i, key=key, hash=hash) 1653 pac = tkt.authorizationData.seq[0].adData[0].seq[0].adData 1654 1655 # WIDTH, HEIGHT = 1120, 1000 1656 1657 # Note: for TK doc, use https://tkdocs.com 1658 1659 # Root 1660 root = tk.Tk() 1661 root.title("Ticketer++ (@secdev/scapy)") 1662 # root.geometry("%sx%s" % (WIDTH, HEIGHT)) 1663 # root.resizable(0, 1) 1664 1665 scrollFrame = ScrollFrame(root) 1666 frm = scrollFrame.viewPort 1667 1668 tk_ticket = ttk.Frame(frm, padding=5) 1669 tk_pac = ttk.Frame(frm, padding=5) 1670 1671 ttk.Button(frm, text="Quit", command=root.destroy).grid( 1672 column=0, row=1, columnspan=2 1673 ) 1674 1675 # TTK style 1676 1677 ttkstyle = ttk.Style() 1678 ttkstyle.theme_use("alt") 1679 ttkstyle.configure( 1680 "BorderFrame.TFrame", 1681 relief="groove", 1682 borderwidth=3, 1683 ) 1684 1685 # MAIN TICKET 1686 1687 # Flags 1688 tk_flags = ttk.LabelFrame( 1689 tk_ticket, 1690 text="Flags", 1691 style="BorderFrame.TFrame", 1692 ) 1693 tk_flags.pack(fill="x", pady=5) 1694 flags = tkt.get_field("flags").get_flags(tkt) 1695 self._data["flags"] = {} 1696 self._make_checkbox(tk_flags, _TICKET_FLAGS, flags, self._data["flags"]) 1697 1698 # Key 1699 tk_key = ttk.LabelFrame( 1700 tk_ticket, 1701 text="key", 1702 style="BorderFrame.TFrame", 1703 ) 1704 tk_key.pack(fill="x", pady=5) 1705 self._make_fields( 1706 tk_key, 1707 [ 1708 ("keytype", str(tkt.key.keytype.val)), 1709 ( 1710 "keyvalue", 1711 bytes_hex(tkt.key.keyvalue.val).decode(), 1712 ), 1713 ], 1714 ) 1715 1716 # crealm 1717 self._make_fields(tk_ticket, [("crealm", tkt.crealm.val.decode())]) 1718 1719 # cname 1720 tk_cname = ttk.LabelFrame( 1721 tk_ticket, 1722 text="cname", 1723 style="BorderFrame.TFrame", 1724 ) 1725 tk_cname.pack(fill="x", pady=5) 1726 self._make_fields( 1727 tk_cname, 1728 [ 1729 ( 1730 "nameType", 1731 str(tkt.cname.nameType.val), 1732 ), 1733 ], 1734 ) 1735 self._make_table( 1736 tk_cname, 1737 "nameString", 1738 ["Value"], 1739 [(x.val.decode(),) for x in tkt.cname.nameString], 1740 ) 1741 1742 # transited 1743 tk_transited = ttk.LabelFrame( 1744 tk_ticket, 1745 text="transited", 1746 style="BorderFrame.TFrame", 1747 ) 1748 tk_transited.pack(fill="x", pady=5) 1749 self._make_fields( 1750 tk_transited, 1751 [ 1752 # 1753 ( 1754 "trType", 1755 str(tkt.transited.trType.val), 1756 ), 1757 ( 1758 "contents", 1759 tkt.transited.contents.val.decode(), 1760 ), 1761 ], 1762 ) 1763 1764 # times 1765 self._make_fields( 1766 tk_ticket, 1767 [ 1768 ("authtime", tkt.authtime.pretty_time.rstrip(" UTC")), 1769 ("starttime", tkt.starttime.pretty_time.rstrip(" UTC")), 1770 ("endtime", tkt.endtime.pretty_time.rstrip(" UTC")), 1771 ("renewTill", tkt.renewTill.pretty_time.rstrip(" UTC")), 1772 ], 1773 ) 1774 1775 # PAC 1776 1777 # Logon information 1778 tk_logoninfo = ttk.LabelFrame( 1779 tk_pac, 1780 text="Logon information", 1781 style="BorderFrame.TFrame", 1782 ) 1783 tk_logoninfo.pack(fill="x", pady=5) 1784 self._getLogonInformation(pac, tk_logoninfo) 1785 1786 # Client name and ticket information 1787 tk_clientinfo = ttk.LabelFrame( 1788 tk_pac, 1789 text="Client name and ticket information", 1790 style="BorderFrame.TFrame", 1791 ) 1792 tk_clientinfo.pack(fill="x", pady=5) 1793 self._getClientInfo(pac, tk_clientinfo) 1794 1795 # UPN and DNS information 1796 tk_upndnsinfo = ttk.LabelFrame( 1797 tk_pac, 1798 text="UPN and DNS information", 1799 style="BorderFrame.TFrame", 1800 ) 1801 tk_upndnsinfo.pack(fill="x", pady=5) 1802 self._getUPNDnsInfo(pac, tk_upndnsinfo) 1803 1804 # Client claims information 1805 tk_clientclaims = ttk.LabelFrame( 1806 tk_pac, 1807 text="Client claims information", 1808 style="BorderFrame.TFrame", 1809 ) 1810 tk_clientclaims.pack(fill="x", pady=5) 1811 self._getClientClaims(pac, tk_clientclaims) 1812 1813 # PAC Attributes 1814 tk_pacattributes = ttk.LabelFrame( 1815 tk_pac, 1816 text="PAC Attributes", 1817 style="BorderFrame.TFrame", 1818 ) 1819 tk_pacattributes.pack(fill="x", pady=5) 1820 self._getPACAttributes(pac, tk_pacattributes) 1821 1822 # PAC Requestor 1823 tk_pacrequestor = ttk.LabelFrame( 1824 tk_pac, 1825 text="PAC Requestor", 1826 style="BorderFrame.TFrame", 1827 ) 1828 tk_pacrequestor.pack(fill="x", pady=5) 1829 self._getPACRequestor(pac, tk_pacrequestor) 1830 1831 # Server checksum 1832 tk_serverchksum = ttk.LabelFrame( 1833 tk_pac, 1834 text="Server checksum", 1835 style="BorderFrame.TFrame", 1836 ) 1837 tk_serverchksum.pack(fill="x", pady=5) 1838 self._getServerChecksum(pac, tk_serverchksum) 1839 1840 # KDC checksum 1841 tk_serverchksum = ttk.LabelFrame( 1842 tk_pac, 1843 text="KDC checksum", 1844 style="BorderFrame.TFrame", 1845 ) 1846 tk_serverchksum.pack(fill="x", pady=5) 1847 self._getKDCChecksum(pac, tk_serverchksum) 1848 1849 # Ticket checksum 1850 tk_serverchksum = ttk.LabelFrame( 1851 tk_pac, 1852 text="Ticket checksum", 1853 style="BorderFrame.TFrame", 1854 ) 1855 tk_serverchksum.pack(fill="x", pady=5) 1856 self._getTicketChecksum(pac, tk_serverchksum) 1857 1858 # Extended KDC checksum 1859 tk_serverchksum = ttk.LabelFrame( 1860 tk_pac, 1861 text="Extended KDC checksum", 1862 style="BorderFrame.TFrame", 1863 ) 1864 tk_serverchksum.pack(fill="x", pady=5) 1865 self._getExtendedKDCChecksum(pac, tk_serverchksum) 1866 1867 # Run 1868 1869 tk_ticket.grid(column=0, row=0, sticky=tk.N) 1870 tk_pac.grid(column=1, row=0, sticky=tk.N) 1871 1872 scrollFrame.pack(side="top", fill="both", expand=True) 1873 root.mainloop() 1874 1875 # Rebuild 1876 store = { 1877 # KRB 1878 "flags": ASN1_BIT_STRING( 1879 "".join( 1880 "1" if self._data["flags"][x].get() else "0" for x in _TICKET_FLAGS 1881 ) 1882 + "0" * (-len(_TICKET_FLAGS) % 32) 1883 ), 1884 "key": { 1885 "keytype": ASN1_INTEGER(int(self._data["keytype"].get())), 1886 "keyvalue": ASN1_STRING(hex_bytes(self._data["keyvalue"].get())), 1887 }, 1888 "crealm": ASN1_GENERAL_STRING(self._data["crealm"].get()), 1889 "cname": { 1890 "nameString": [ 1891 ASN1_GENERAL_STRING(x[0]) for x in self._data["nameString"] 1892 ], 1893 "nameType": ASN1_INTEGER(int(self._data["nameType"].get())), 1894 }, 1895 "authtime": self._time_to_asn1(self._data["authtime"].get()), 1896 "starttime": self._time_to_asn1(self._data["starttime"].get()), 1897 "endtime": self._time_to_asn1(self._data["endtime"].get()), 1898 "renewTill": self._time_to_asn1(self._data["renewTill"].get()), 1899 # PAC 1900 # Validation info 1901 "VI.LogonTime": self._time_to_filetime(self._data["LogonTime"].get()), 1902 "VI.LogoffTime": self._time_to_filetime(self._data["LogoffTime"].get()), 1903 "VI.KickOffTime": self._time_to_filetime(self._data["KickOffTime"].get()), 1904 "VI.PasswordLastSet": self._time_to_filetime( 1905 self._data["PasswordLastSet"].get() 1906 ), 1907 "VI.PasswordCanChange": self._time_to_filetime( 1908 self._data["PasswordCanChange"].get() 1909 ), 1910 "VI.PasswordMustChange": self._time_to_filetime( 1911 self._data["PasswordMustChange"].get() 1912 ), 1913 "VI.EffectiveName": self._data["EffectiveName"].get(), 1914 "VI.FullName": self._data["FullName"].get(), 1915 "VI.LogonScript": self._data["LogonScript"].get(), 1916 "VI.ProfilePath": self._data["ProfilePath"].get(), 1917 "VI.HomeDirectory": self._data["HomeDirectory"].get(), 1918 "VI.HomeDirectoryDrive": self._data["HomeDirectoryDrive"].get(), 1919 "VI.UserSessionKey": hex_bytes(self._data["UserSessionKey"].get()), 1920 "VI.LogonServer": self._data["LogonServer"].get(), 1921 "VI.LogonDomainName": self._data["LogonDomainName"].get(), 1922 "VI.LogonCount": int(self._data["LogonCount"].get()), 1923 "VI.BadPasswordCount": int(self._data["BadPasswordCount"].get()), 1924 "VI.UserId": int(self._data["UserId"].get()), 1925 "VI.PrimaryGroupId": int(self._data["PrimaryGroupId"].get()), 1926 "VI.GroupIds": [ 1927 { 1928 "RelativeId": int(x[0]), 1929 "Attributes": int(x[1]), 1930 } 1931 for x in self._data["GroupIds"] 1932 ], 1933 "VI.UserFlags": int(self._data["UserFlags"].get()), 1934 "VI.LogonDomainId": self._data["LogonDomainId"].get(), 1935 "VI.UserAccountControl": int(self._data["UserAccountControl"].get()), 1936 "VI.ExtraSids": [ 1937 { 1938 "Sid": x[0], 1939 "Attributes": int(x[1]), 1940 } 1941 for x in self._data["ExtraSids"] 1942 ], 1943 "VI.ResourceGroupDomainSid": self._data["ResourceGroupDomainSid"].get(), 1944 "VI.ResourceGroupIds": [ 1945 { 1946 "RelativeId": int(x[0]), 1947 "Attributes": int(x[1]), 1948 } 1949 for x in self._data["ResourceGroupIds"] 1950 ], 1951 # Pac Client infos 1952 "CI.ClientId": self._time_to_int(self._data["ClientId"].get()), 1953 "CI.Name": self._data["Name"].get(), 1954 # UPN DNS Info 1955 "UPNDNS.Flags": 3, 1956 "UPNDNS.Upn": self._data["Upn"].get(), 1957 "UPNDNS.DnsDomainName": self._data["DnsDomainName"].get(), 1958 "UPNDNS.SamName": self._data["SamName"].get(), 1959 "UPNDNS.Sid": self._data["UpnDnsSid"].get(), 1960 # Client Claims 1961 "CC.ClaimsArrays": [ 1962 { 1963 "ClaimsSourceType": int(ca["ClaimsSourceType"].get()), 1964 "ClaimEntries": [ 1965 { 1966 "Id": ce[0], 1967 "Type": int(ce[1]), 1968 "StringValues": ce[2], 1969 } 1970 for ce in ca["ClaimEntries"] 1971 ], 1972 } 1973 for ca in self._data["ClaimsArrays"].values() 1974 ], 1975 # Attributes Info 1976 "AI.Flags": "+".join( 1977 x 1978 for x in ["PAC_WAS_REQUESTED", "PAC_WAS_GIVEN_IMPLICITLY"] 1979 if self._data["pacAttributes"][x].get() 1980 ), 1981 # Requestor 1982 "REQ.Sid": self._data["ReqSid"].get(), 1983 # Server Checksum 1984 "SC.SignatureType": int(self._data["SRVSignatureType"].get()), 1985 "SC.Signature": hex_bytes(self._data["SRVSignature"].get()), 1986 "SC.RODCIdentifier": hex_bytes(self._data["SRVRODCIdentifier"].get()), 1987 # KDC Checksum 1988 "KC.SignatureType": int(self._data["KDCSignatureType"].get() or "-1"), 1989 "KC.Signature": hex_bytes(self._data["KDCSignature"].get()), 1990 "KC.RODCIdentifier": hex_bytes(self._data["KDCRODCIdentifier"].get()), 1991 # Ticket Checksum 1992 "TKT.SignatureType": int(self._data["TKTSignatureType"].get() or "-1"), 1993 "TKT.Signature": hex_bytes(self._data["TKTSignature"].get()), 1994 "TKT.RODCIdentifier": hex_bytes(self._data["TKTRODCIdentifier"].get()), 1995 # Extended KDC Checksum 1996 "EXKC.SignatureType": int(self._data["EXKDCSignatureType"].get() or "-1"), 1997 "EXKC.Signature": hex_bytes(self._data["EXKDCSignature"].get()), 1998 "EXKC.RODCIdentifier": hex_bytes(self._data["EXKDCRODCIdentifier"].get()), 1999 } 2000 tkt = self._build_ticket(store) 2001 if hash is None and key is not None: # TODO: add key to update_ticket 2002 hash = key.key 2003 self.update_ticket(i, tkt, hash=hash) 2004 2005 def _resign_ticket(self, tkt, spn, hash=None, kdc_hash=None): 2006 """ 2007 Resign a ticket (priv) 2008 """ 2009 # [MS-PAC] 2.8.1 - 2.8.5 2010 rpac = tkt.authorizationData.seq[0].adData.seq[0].adData # real pac 2011 tmp_tkt = tkt.copy() # fake ticket and pac used for computation 2012 pac = tmp_tkt.authorizationData.seq[0].adData.seq[0].adData 2013 # Variables for Signatures, indexed by ulType 2014 sig_i = {} 2015 sig_type = {} 2016 # Read PAC buffers to find all signatures, and set them to 0 2017 for k, buf in enumerate(pac.Buffers): 2018 if buf.ulType in [0x00000006, 0x00000007, 0x00000010, 0x00000013]: 2019 sig_i[buf.ulType] = k 2020 sig_type[buf.ulType] = pac.Payloads[k].SignatureType 2021 try: 2022 pac.Payloads[k].Signature = ( 2023 b"\x00" * _checksums[pac.Payloads[k].SignatureType].macsize 2024 ) 2025 except KeyError: 2026 raise ValueError("Unknown/Unsupported signatureType") 2027 rpac.Buffers[k].cbBufferSize = None 2028 rpac.Buffers[k].Offset = None 2029 2030 # There must at least be Server Signature and KDC Signature 2031 if any(x not in sig_i for x in [0x00000006, 0x00000007]): 2032 raise ValueError("Cannot sign PAC: missing a compulsory signature") 2033 2034 # Build the 2 necessary keys 2035 key_srv = self._prompt_hash( 2036 spn, 2037 cksumtype=sig_type[0x00000006], 2038 hash=hash, 2039 ) 2040 key_kdc = self._prompt_hash( 2041 "krbtgt/" + "@".join(spn.split("@")[1:] * 2), 2042 cksumtype=sig_type[0x00000007], 2043 hash=kdc_hash, 2044 ) 2045 2046 # NOTE: the doc is very unclear regarding the order of the Signatures. 2047 2048 # "The extended KDC signature is a keyed hash [RFC4757] of the entire PAC 2049 # message, with the Signature fields of all other PAC_SIGNATURE_DATA structures 2050 # (section 2.8) set to zero." 2051 # ==> This is wrong. 2052 # The Ticket Signature is present when computing the Extended KDC Signature. 2053 2054 # sect 2.8.3 - Ticket Signature 2055 2056 if 0x00000010 in sig_i: 2057 # "The ad-data in the PAC’s AuthorizationData element ([RFC4120] 2058 # section 5.2.6) is replaced with a single zero byte" 2059 tmp_tkt.authorizationData.seq[0].adData.seq[0].adData = b"\x00" 2060 rpac.Payloads[ 2061 sig_i[0x00000010] 2062 ].Signature = ticket_sig = key_kdc.make_checksum( 2063 17, bytes(tmp_tkt) # KERB_NON_KERB_CKSUM_SALT(17) 2064 ) 2065 # included in the PAC when signing it for Extended Server Signature & Server Signature 2066 pac.Payloads[sig_i[0x00000010]].Signature = ticket_sig 2067 2068 # sect 2.8.4 - Extended KDC Signature 2069 2070 if 0x00000013 in sig_i: 2071 rpac.Payloads[ 2072 sig_i[0x00000013] 2073 ].Signature = extended_kdc_sig = key_kdc.make_checksum( 2074 17, bytes(pac) # KERB_NON_KERB_CKSUM_SALT(17) 2075 ) 2076 # included in the PAC when signing it for Server Signature 2077 pac.Payloads[sig_i[0x00000013]].Signature = extended_kdc_sig 2078 2079 # sect 2.8.1 - Server Signature 2080 2081 rpac.Payloads[sig_i[0x00000006]].Signature = server_sig = key_srv.make_checksum( 2082 17, bytes(pac) # KERB_NON_KERB_CKSUM_SALT(17) 2083 ) 2084 2085 # sect 2.8.2 - KDC Signature 2086 2087 rpac.Payloads[sig_i[0x00000007]].Signature = key_kdc.make_checksum( 2088 17, server_sig # KERB_NON_KERB_CKSUM_SALT(17) 2089 ) 2090 return tkt 2091 2092 def resign_ticket(self, i, hash=None, kdc_hash=None): 2093 """ 2094 Resign a ticket from CCache 2095 2096 :param hash: the hash to use to compute the Server Signature 2097 :param kdc_hash: the hash to use to compute the KDC signature 2098 (if None, not recomputed unless its a TGT where is uses hash) 2099 """ 2100 tkt = self.dec_ticket(i, hash=hash) 2101 self.update_ticket(i, tkt, resign=True, hash=hash, kdc_hash=kdc_hash) 2102 2103 def request_tgt(self, upn, ip=None, key=None, password=None, realm=None, **kwargs): 2104 """ 2105 Request a Kerberos TGT and add it to the local CCache 2106 2107 See :func:`~scapy.layers.kerberos.krb_as_req` for the full documentation. 2108 """ 2109 res = krb_as_req(upn, ip=ip, key=key, password=password, realm=realm, **kwargs) 2110 if not res: 2111 return 2112 2113 self.import_krb(res) 2114 2115 def request_st( 2116 self, i, spn, ip=None, renew=False, realm=None, additional_tickets=[], **kwargs 2117 ): 2118 """ 2119 Request a Kerberos TS and add it to the local CCache using another ticket 2120 2121 :param i: the ticket/sessionkey to use in the TGS request 2122 2123 See :func:`~scapy.layers.kerberos.krb_tgs_req` for the the other parameters. 2124 """ 2125 ticket, sessionkey, upn, _ = self.export_krb(i) 2126 2127 res = krb_tgs_req( 2128 upn, 2129 spn, 2130 sessionkey=sessionkey, 2131 ticket=ticket, 2132 ip=ip, 2133 renew=renew, 2134 realm=realm, 2135 additional_tickets=additional_tickets, 2136 **kwargs, 2137 ) 2138 if not res: 2139 return 2140 2141 self.import_krb(res) 2142 2143 def kpasswdset(self, i, targetupn=None): 2144 """ 2145 Use kpasswd in 'Set Password' mode to set the password of an account. 2146 2147 :param i: the TGT to use. 2148 """ 2149 ticket, sessionkey, upn, _ = self.export_krb(i) 2150 kpasswd( 2151 upn=upn, 2152 targetupn=targetupn, 2153 setpassword=True, 2154 ticket=ticket, 2155 key=sessionkey, 2156 ) 2157 2158 def renew(self, i, ip=None, additional_tickets=[], **kwargs): 2159 """ 2160 Renew a Kerberos TGT or a TS from the local CCache using a TGS-REQ 2161 2162 :param i: the ticket/sessionkey to renew. 2163 """ 2164 ticket, sessionkey, upn, spn = self.export_krb(i) 2165 2166 res = krb_tgs_req( 2167 upn, 2168 spn, 2169 sessionkey=sessionkey, 2170 ticket=ticket, 2171 ip=ip, 2172 renew=True, 2173 additional_tickets=additional_tickets, 2174 **kwargs, 2175 ) 2176 if not res: 2177 return 2178 2179 self.import_krb(res, _inplace=i) 2180