• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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