Skip to content

Commit 144df4e

Browse files
committed
SPNEGO: support reading KRB5CCNAME
1 parent 8fff3d9 commit 144df4e

File tree

2 files changed

+65
-13
lines changed

2 files changed

+65
-13
lines changed

scapy/layers/spnego.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
`GSSAPI <https://scapy.readthedocs.io/en/latest/layers/gssapi.html#spnego>`_
1717
"""
1818

19+
import os
1920
import struct
2021
from uuid import UUID
2122

@@ -640,9 +641,11 @@ def from_cli_arguments(
640641
HashAes128Sha96: bytes = None,
641642
kerberos_required: bool = False,
642643
ST=None,
644+
TGT=None,
643645
KEY=None,
644646
ccache: str = None,
645647
debug: int = 0,
648+
use_krb5ccname: bool = False,
646649
):
647650
"""
648651
Initialize a SPNEGOSSP from a list of many arguments.
@@ -656,9 +659,12 @@ def from_cli_arguments(
656659
:param HashAes256Sha96: (bytes) if provided, used for auth (Kerberos)
657660
:param HashAes128Sha96: (bytes) if provided, used for auth (Kerberos)
658661
:param ST: if provided, the service ticket to use (Kerberos)
662+
:param TGT: if provided, the TGT to use (Kerberos)
659663
:param KEY: if ST provided, the session key associated to the ticket (Kerberos).
660-
Else, the user secret key.
664+
This can be either for the ST or TGT. Else, the user secret key.
661665
:param ccache: (str) if provided, a path to a CCACHE (Kerberos)
666+
:param use_krb5ccname: (bool) if true, the KRB5CCNAME environment variable will
667+
be used if available.
662668
"""
663669
kerberos = True
664670
hostname = None
@@ -682,6 +688,10 @@ def from_cli_arguments(
682688
# not a UPN: NTLM only
683689
kerberos = False
684690

691+
# If we're asked, check the environment for KRB5CCNAME
692+
if use_krb5ccname and ccache is None and "KRB5CCNAME" in os.environ:
693+
ccache = os.environ["KRB5CCNAME"]
694+
685695
# Do we need to ask the password?
686696
if all(
687697
x is None
@@ -691,6 +701,7 @@ def from_cli_arguments(
691701
HashNt,
692702
HashAes256Sha96,
693703
HashAes128Sha96,
704+
ccache,
694705
]
695706
):
696707
# yes.
@@ -702,19 +713,42 @@ def from_cli_arguments(
702713
# Kerberos
703714
if kerberos and hostname:
704715
# Get ticket if we don't already have one.
705-
if ST is None and ccache is not None:
716+
if ST is None and TGT is None and ccache is not None:
706717
# In this case, load the KerberosSSP from ccache
707718
from scapy.modules.ticketer import Ticketer
708719

709720
# Import into a Ticketer object
710721
t = Ticketer()
711722
t.open_ccache(ccache)
712723

713-
# Look for the ticketer that we'll use
714-
raise NotImplementedError
715-
716-
ssps.append(t.ssp())
717-
elif ST is None:
724+
# Look for the ticket that we'll use. We chose:
725+
# - either a ST if the SPN matches our target
726+
# - else a TGT if we got nothing better
727+
tgts = []
728+
for i, (tkt, key, upn, spn) in enumerate(t.iter_tickets()):
729+
# Check that it's for the correct user
730+
if upn.lower() == UPN.lower():
731+
# Check that it's either a TGT or a ST to the correct service
732+
if spn.lower().startswith("krbtgt/"):
733+
# TGT. Keep it, and see if we don't have a better ST.
734+
tgts.append(t.ssp(i))
735+
elif hostname in spn:
736+
# ST. We're done !
737+
ssps.append(t.ssp(i))
738+
break
739+
else:
740+
# No ST found
741+
if tgts:
742+
# Using a TGT !
743+
ssps.append(tgts[0])
744+
else:
745+
# Nothing found
746+
t.show()
747+
raise ValueError(
748+
f"Could not find a ticket for {upn}, either a "
749+
f"TGT or towards {hostname}"
750+
)
751+
elif ST is None and TGT is None:
718752
# In this case, KEY is supposed to be the user's key.
719753
from scapy.libs.rfc3961 import Key, EncryptionType
720754

@@ -748,6 +782,7 @@ def from_cli_arguments(
748782
KerberosSSP(
749783
UPN=UPN,
750784
ST=ST,
785+
TGT=TGT,
751786
KEY=KEY,
752787
debug=debug,
753788
)

scapy/modules/ticketer.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -859,12 +859,22 @@ def ssp(self, i):
859859
"""
860860
if isinstance(i, int):
861861
ticket, sessionkey, upn, spn = self.export_krb(i)
862-
return KerberosSSP(
863-
ST=ticket,
864-
KEY=sessionkey,
865-
UPN=upn,
866-
SPN=spn,
867-
)
862+
if spn.startswith("krbtgt/"):
863+
# It's a TGT
864+
return KerberosSSP(
865+
TGT=ticket,
866+
KEY=sessionkey,
867+
UPN=upn,
868+
SPN=None, # Use target_name only
869+
)
870+
else:
871+
# It's a ST
872+
return KerberosSSP(
873+
ST=ticket,
874+
KEY=sessionkey,
875+
UPN=upn,
876+
SPN=spn,
877+
)
868878
elif isinstance(i, str):
869879
spn = i
870880
key = self.get_cred(spn)
@@ -2576,3 +2586,10 @@ def renew(self, i, ip=None, additional_tickets=[], **kwargs):
25762586
return
25772587

25782588
self.import_krb(res, _inplace=i)
2589+
2590+
def iter_tickets(self):
2591+
"""
2592+
Iterate through the tickets in the ccache
2593+
"""
2594+
for i in range(len(self.ccache.credentials)):
2595+
yield self.export_krb(i)

0 commit comments

Comments
 (0)