Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions scapy/layers/kerberos.py
Original file line number Diff line number Diff line change
Expand Up @@ -4949,6 +4949,10 @@ class KerberosSSP(SSP):

auth_type = 0x10

# [RFC4120] sect 1.6: "a reasonable value for allowable clock skew is
# 5 minutes"
KRB_MaxClockSkew = 5 * 60

class STATE(SSP.STATE):
INIT = 1
CLI_SENT_TGTREQ = 2
Expand Down Expand Up @@ -5930,6 +5934,36 @@ def GSS_Accept_sec_context(
Context.STSessionKey = tkt.key.toKey()
authenticator = ap_req.authenticator.decrypt(Context.STSessionKey)

# Check the ticket validity period and the authenticator timestamp
# [RFC4120] sect 3.2.3
now = now_time.timestamp()
starttime = (tkt.starttime or tkt.authtime).datetime
endtime = tkt.endtime.datetime
ctime = authenticator.ctime.datetime
errorCode = None
if not starttime or starttime.timestamp() - now > self.KRB_MaxClockSkew:
errorCode = "KRB_AP_ERR_TKT_NYV"
elif not endtime or now - endtime.timestamp() > self.KRB_MaxClockSkew:
errorCode = "KRB_AP_ERR_TKT_EXPIRED"
elif not ctime or abs(ctime.timestamp() - now) > self.KRB_MaxClockSkew:
errorCode = "KRB_AP_ERR_SKEW"
# Don't check times in passive mode: the capture may be old.
if errorCode and not Context.passive:
warning("KerberosSSP: %s" % errorCode)
err = KRB_GSSAPI_Token(
innerToken=KRB_InnerToken(
TOK_ID=b"\x03\x00",
root=KRB_ERROR(
errorCode=errorCode,
stime=ASN1_GENERALIZED_TIME(now_time),
realm=ap_req.ticket.realm,
sname=ap_req.ticket.sname,
eData=None,
),
)
)
return Context, err, GSS_S_DEFECTIVE_CREDENTIAL

# Compute an application session key ([MS-KILE] sect 3.1.1.2)
subkey = None
if ap_req.apOptions.val[2] == "1": # mutual-required
Expand Down
6 changes: 6 additions & 0 deletions test/scapy/layers/dcerpc.uts
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,12 @@ t.import_krb(
KRB_Ticket(bytes.fromhex("618204ae308204aaa003020105a10e1b0c444f4d41494e2e4c4f43414ca2233021a003020103a11a30181b046c6461701b106463312e646f6d61696e2e6c6f63616ca382046c30820468a003020112a10302010ea282045a04820456280c76dee773a1c5e5bd966094201dc028c76f36bbcb9b04c6bb15e02893834f92c694b26bd627fb3f17c2b7eb3ccc57f926e28a9b578b75d1a179c2ce5cba08c67d6b8529f4988490a86a25ec181615e29a344df498ee5ab11a76ff34d862a09b457f6ed528aeb3ad7e7f075f5a02513830554d17edd00554c8f80bab69b80dec86a55111e7ac476d5f099f2ae374378f814a7b85d60f3ce3cff003ff82dd81a7a91a38ff79e5f51e8576de6aba5c86cc7ae2baf13038a8b4b554ff07b9873f19a0c682e83a57811475688e93b2ff53d232a037a19aab83d741204f088fb711c883ce66f4f989752b2c8b18b5cc3fffecbfd9076c25ee39cb13856c09e2ff4958c26e5ecade8c47a2adfd5ceab9d458617b6d3998dd8ee99d0eb57765d0976031a5eb618b076b1e3f6565b4370f238e8829b13deccf5ec35279946816969d5e307e33820f98efb6f601f79c16344d891a415babc6d4d01f992d15ebbf12fb5948cdbef6ed1ba2e5303ca2b0afd0ef1e5231458571bb2e7f463ce539faef5706ac1f8fb34668b6dff101c2fdb4f231fa75c24bb5aff7ee4349ce1948c42fdb91863772bd6c0dac26f47fe6ab1e617cdc85d9e015898fb5d6a0d8a38423c2ef49ec42e200f983fa45526b8cd205db3015e9d37de9cdd5b5befe519f22b7e65780f251215f3ca618f136f73200dd719c23dd3d4072b185e58628b2408377d688ab4540d1395af818a609d3f4df611483a77cd13511978eacf7acc91dd9740d97a9cbbb1299898219650d5ae0d3c0d0521e32132c889a65819ead424ec4f2be1d930f022f27b88078d301a1ce73070062ddf2259b839211e9f83d4585242328e310656f188f3f4cec5d5a61f08f9f0c2a15992a5aa65c4da838a5fd8df426fc4c7679d6af4a261d943a2501ba7221a0af1bc2db19bdfda44064efd94db45231b89035db904b3361afb0c0da0ab4c17857e86a820027f274e01a60388931520db0d667b5453e985152ebd382872122415ec13a88eaaf8522e18b54f580365742ce5884c5fe1d719b752788ff283725c446739686c9f76c850800016287f7cb85390c045fd250104d44f641d62ce1c7882bad72b574e10e1521d843938f30ab7064b007479f2bdc5e8d0aaf26b89993bf2c7c413aec8b8cad4c8d4714904125b868a807329d54674eff909a690bfd735d2c7134c9e819e48a66385a4d48d13ea710f45df9605d727a3d28e5bd09f7385bcab92bc1903ce888571309ffaf370024c5cc527730d256b20ba19511df8f0aa970b638a4393a45db03969b7415270887ef7ec94abbda98632a8d14b0d73f855e416e6d167269d04ec2489c843f11db04074c60c7ea9a13d2d1aca94379e84529bbd96a73f0cd6d8d9d85b5e06272e8739d0d2607d0b57b6e763118996aa8bf903bbaf4ce2ebc20b071e1dbbd48102634823059d4a37d73c054d0e066a09b6c53fe7319a7fcde0f4624461c8b584743d40dc334b34230d56c338bab40426ce7ade90f05a01cb0c0b8963860e4156831e8aecfb8721bf437ab71af74c426acfe7f9134163364a7ee2e")),
key=SRVKEY,
)
# The captured ticket is expired: refresh its validity period
from datetime import datetime, timedelta, timezone
tkt = t.dec_ticket(0, key=SRVKEY)
tkt.starttime = ASN1_GENERALIZED_TIME(datetime.now(timezone.utc))
tkt.endtime = ASN1_GENERALIZED_TIME(datetime.now(timezone.utc) + timedelta(hours=10))
t.update_ticket(0, tkt, hash=SRVKEY.key)
clissp = t.ssp(0)

rpcserver = MyRPCServer.spawn(
Expand Down
39 changes: 39 additions & 0 deletions test/scapy/layers/kerberos.uts
Original file line number Diff line number Diff line change
Expand Up @@ -1894,6 +1894,45 @@ try:
except ValueError:
pass

= GSS_Accept_sec_context rejects expired tickets and stale authenticators

from datetime import datetime, timedelta, timezone
from scapy.layers.gssapi import GSS_S_COMPLETE, GSS_S_DEFECTIVE_CREDENTIAL
from scapy.layers.kerberos import EncryptedData, EncryptionKey, EncTicketPart, KRB_ERROR, PrincipalName, TransitedEncoding

srvkey = Key(EncryptionType.AES256_CTS_HMAC_SHA1_96, key=b"S" * 32)
tktkey = Key(EncryptionType.AES256_CTS_HMAC_SHA1_96, key=b"K" * 32)
now = datetime(2024, 3, 5, 16, 52, 55, tzinfo=timezone.utc)

def make_ap_req(endtime, ctime):
st = KRB_Ticket(realm="DOMAIN.LOCAL", sname=PrincipalName.fromSPN("cifs/dc1"), encPart=EncryptedData())
st.encPart.encrypt(srvkey, EncTicketPart(
transited=TransitedEncoding(trType=ASN1_INTEGER(0), contents=ASN1_STRING(b"")),
key=EncryptionKey.fromKey(tktkey),
crealm="DOMAIN.LOCAL",
cname=PrincipalName.fromUPN("User1@DOMAIN.LOCAL"),
authtime=ASN1_GENERALIZED_TIME(endtime - timedelta(hours=10)),
starttime=ASN1_GENERALIZED_TIME(endtime - timedelta(hours=10)),
endtime=ASN1_GENERALIZED_TIME(endtime),
))
krbcli = KerberosSSP(UPN="User1@DOMAIN.LOCAL", SPN="cifs/dc1", ST=KRB_Ticket(bytes(st)), KEY=tktkey)
with mock.patch('scapy.layers.kerberos.datetime', mock.MagicMock(now=lambda tz=None: ctime)):
_, tok, _ = krbcli.GSS_Init_sec_context(None, req_flags=0)
return tok

krbsrv = KerberosSSP(SPN="cifs/dc1", KEY=srvkey)
with mock.patch('scapy.layers.kerberos.datetime', date_mock):
_, tok, status = krbsrv.GSS_Accept_sec_context(None, make_ap_req(now + timedelta(hours=1), now))
assert status == GSS_S_COMPLETE
# Expired ticket
_, tok, status = krbsrv.GSS_Accept_sec_context(None, make_ap_req(now - timedelta(days=1), now))
assert status == GSS_S_DEFECTIVE_CREDENTIAL
assert tok.innerToken.root.errorCode == "KRB_AP_ERR_TKT_EXPIRED"
# Stale (replayed) authenticator
_, tok, status = krbsrv.GSS_Accept_sec_context(None, make_ap_req(now + timedelta(hours=1), now - timedelta(hours=1)))
assert status == GSS_S_DEFECTIVE_CREDENTIAL
assert tok.innerToken.root.errorCode == "KRB_AP_ERR_SKEW"

= Create client and server KerberosSSP (raw)

client = KerberosSSP(
Expand Down