Skip to content
Merged
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
95 changes: 69 additions & 26 deletions scapy/layers/kerberos.py
Original file line number Diff line number Diff line change
Expand Up @@ -5150,7 +5150,19 @@ def GSS_VerifyMICEx(self, Context, msgs, signature):

- AES: RFC4121 sect 4.2.6.1
"""
Context.RecvSeqNum = signature.root.SND_SEQ
# [RFC4121] sect 4.2.1: per-message sequence numbers increase by one.
# [RFC2743] sect 1.2.3: replay detection and sequencing are optional
if (
not Context.passive
and Context.flags
& (GSS_C_FLAGS.GSS_C_REPLAY_FLAG | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG)
and signature.root.SND_SEQ != Context.RecvSeqNum
):
raise ValueError(
"ERROR: Sequence numbers don't match: received %s, expected %s"
% (signature.root.SND_SEQ, Context.RecvSeqNum)
)

if Context.KrbSessionKey.etype in [17, 18]: # AES
# Concatenate the ToSign
ToSign = b"".join(x.data for x in msgs if x.sign)
Expand All @@ -5161,9 +5173,12 @@ def GSS_VerifyMICEx(self, Context, msgs, signature):
)
else:
raise NotImplementedError

if sig != signature.root.SGN_CKSUM:
raise ValueError("ERROR: Checksums don't match")

Context.RecvSeqNum += 1

def GSS_WrapEx(self, Context, msgs, qop_req: GSS_QOP_REQ_FLAGS = 0):
"""
[MS-KILE] sect 3.4.5.4
Expand Down Expand Up @@ -5350,6 +5365,15 @@ def GSS_UnwrapEx(self, Context, msgs, signature):
- HMAC-RC4: RFC4757 sect 7.3
"""
if Context.KrbSessionKey.etype in [17, 18]: # AES
# [RFC4121] sect 4.2.1; [RFC2743] sect 1.2.3
if (
not Context.passive
and Context.flags
& (GSS_C_FLAGS.GSS_C_REPLAY_FLAG | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG)
and signature.root.SND_SEQ != Context.RecvSeqNum
):
raise ValueError("ERROR: Sequence numbers don't match")

confidentiality = signature.root.Flags.Sealed
# Real separation starts now: RFC4121 sect 4.2.4
if confidentiality:
Expand Down Expand Up @@ -5392,6 +5416,7 @@ def MakeToSign(Confounder, DecText):
hdr.root.RRC = 0
if f16header != bytes(hdr)[:16]:
raise ValueError("ERROR: Headers don't match")
Context.RecvSeqNum += 1
# 6. Split (and ignore filler)
offset = 0
for msg in msgs:
Expand Down Expand Up @@ -5429,6 +5454,7 @@ def MakeToSign(Confounder, DecText):
# 4. Compare
if sig != Mic:
raise ValueError("ERROR: Checksums don't match")
Context.RecvSeqNum += 1
# Case without msgs
if len(msgs) == 1 and not msgs[0].data:
msgs[0].data = Data
Expand All @@ -5453,6 +5479,14 @@ def MakeToSign(Confounder, DecText):
# 2. Decrypt 'SND_SEQ'
rc4 = Cipher(decrepit_algorithms.ARC4(Kseq), mode=None).encryptor()
seq = rc4.update(tok.root.SND_SEQ)[:4]
# [RFC4757] sect 7.3; [RFC2743] sect 1.2.3
if (
not Context.passive
and Context.flags
& (GSS_C_FLAGS.GSS_C_REPLAY_FLAG | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG)
and struct.unpack(">I", seq)[0] != Context.RecvSeqNum
):
raise ValueError("ERROR: Sequence numbers don't match")
# 3. Compute the 'Kcrypt' key
Klocal = strxor(Kss, len(Kss) * b"\xf0")
if Context.KrbSessionKey.etype == 24: # EXP
Expand Down Expand Up @@ -5484,6 +5518,7 @@ def MakeToSign(Confounder, DecText):
text=bytes(tok)[:8] + Confounder + ToSign,
cksum=tok.root.SGN_CKSUM,
)
Context.RecvSeqNum += 1
return msgs
else:
raise NotImplementedError
Expand Down Expand Up @@ -5802,6 +5837,8 @@ def GSS_Init_sec_context(

# Retrieve SessionKey
repPart = ap_rep.encPart.decrypt(Context.STSessionKey)
# [RFC4120] sect 3.2.5: initial sequence number for server messages.
Context.RecvSeqNum = repPart.seqNumber.val if repPart.seqNumber else 0
if repPart.subkey is not None:
Context.SessionKey = repPart.subkey.keyvalue.val
Context.KrbSessionKey = repPart.subkey.toKey()
Expand Down Expand Up @@ -5981,6 +6018,34 @@ def GSS_Accept_sec_context(
Context.STSessionKey = tkt.key.toKey()
authenticator = ap_req.authenticator.decrypt(Context.STSessionKey)

# Eventually process the "checksum"
if authenticator.cksum and authenticator.cksum.cksumtype == 0x8003:
# KRB-Authenticator
authcksum = authenticator.cksum.checksum
Context.flags = authcksum.Flags
# Check channel bindings
if (
chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
and chan_bindings.digestMD5() != authcksum.Bnd
and not (
GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS in req_flags
and authcksum.Bnd == GSS_C_NO_CHANNEL_BINDINGS
)
):
# Channel binding checks failed.
return Context, None, GSS_S_BAD_BINDINGS
elif (
chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
and GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS not in req_flags
):
# Uhoh, we required channel bindings
return Context, None, GSS_S_BAD_BINDINGS

# [RFC4120] sect 3.2.3: initial sequence number for client messages.
Context.RecvSeqNum = (
authenticator.seqNumber.val if authenticator.seqNumber else 0
)

# Check the ticket validity period, the authenticator timestamp and
# prevent against replays.
# [RFC4120] sect 3.2.3
Expand All @@ -6001,7 +6066,7 @@ def GSS_Accept_sec_context(
elif replay_key in self.ReplayCache:
errorCode = "KRB_AP_ERR_REPEAT"
gssErrorCode = GSS_S_DUPLICATE_TOKEN
# Don't check times in passive mode: the capture may be old.

if errorCode:
warning("KerberosSSP: %s" % errorCode)
err = KRB_GSSAPI_Token(
Expand All @@ -6019,7 +6084,7 @@ def GSS_Accept_sec_context(
)
return Context, err, gssErrorCode

# Store the time for future detections
# Cache authenticators to avoid replay

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Cache authenticators to avoid replay
# Store the time for future detections

self.ReplayCache[replay_key] = ctime + KRB_MaxClockSkew

# We take that opportunity to cleanup any expired items
Expand All @@ -6040,29 +6105,6 @@ def GSS_Accept_sec_context(
Context.KrbSessionKey = self.KEY
Context.SessionKey = self.KEY.key

# Eventually process the "checksum"
if authenticator.cksum and authenticator.cksum.cksumtype == 0x8003:
# KRB-Authenticator
authcksum = authenticator.cksum.checksum
Context.flags = authcksum.Flags
# Check channel bindings
if (
chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
and chan_bindings.digestMD5() != authcksum.Bnd
and not (
GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS in req_flags
and authcksum.Bnd == GSS_C_NO_CHANNEL_BINDINGS
)
):
# Channel binding checks failed.
return Context, None, GSS_S_BAD_BINDINGS
elif (
chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
and GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS not in req_flags
):
# Uhoh, we required channel bindings
return Context, None, GSS_S_BAD_BINDINGS

# Build response (RFC4120 sect 3.2.4)
ap_rep = KRB_AP_REP(encPart=EncryptedData())
ap_rep.encPart.encrypt(
Expand Down Expand Up @@ -6158,6 +6200,7 @@ def GSS_Passive_set_Direction(self, Context: CONTEXT, IsAcceptor=False):
if Context.IsAcceptor is not IsAcceptor:
return
# Swap everything
Context.SendSeqNum, Context.RecvSeqNum = Context.RecvSeqNum, Context.SendSeqNum
Context.SendSealKeyUsage, Context.RecvSealKeyUsage = (
Context.RecvSealKeyUsage,
Context.SendSealKeyUsage,
Expand Down
23 changes: 21 additions & 2 deletions scapy/layers/ntlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1475,18 +1475,28 @@ def GSS_VerifyMICEx(self, Context, msgs, signature):
"""
[MS-NLMP] sect 3.4.9
"""
Context.RecvSeqNum = signature.SeqNum
# [MS-NLMP] sect 3.4.4; [RFC2743] sect 1.2.3
if (
not Context.passive
and Context.flags
& (GSS_C_FLAGS.GSS_C_REPLAY_FLAG | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG)
and signature.SeqNum != Context.RecvSeqNum
):
raise ValueError("ERROR: Sequence numbers don't match")

# Concatenate the ToSign
ToSign = b"".join(x.data for x in msgs if x.sign)
sig = MAC(
Context.RecvSealHandle,
Context.RecvSignKey,
Context.RecvSeqNum,
signature.SeqNum,
ToSign,
)
if sig.Checksum != signature.Checksum:
raise ValueError("ERROR: Checksums don't match")

Context.RecvSeqNum = signature.SeqNum + 1

def GSS_WrapEx(self, Context, msgs, qop_req=0):
"""
[MS-NLMP] sect 3.4.6
Expand All @@ -1507,6 +1517,15 @@ def GSS_UnwrapEx(self, Context, msgs, signature):
"""
[MS-NLMP] sect 3.4.7
"""
# [MS-NLMP] sect 3.4.4: reject before consuming the RC4 stream.
if (
not Context.passive
and Context.flags
& (GSS_C_FLAGS.GSS_C_REPLAY_FLAG | GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG)
and signature.SeqNum != Context.RecvSeqNum
):
raise ValueError("ERROR: Sequence numbers don't match")

# Decrypt
for msg in msgs:
if msg.conf_req_flag:
Expand Down
2 changes: 1 addition & 1 deletion test/scapy/layers/kerberos.uts
Original file line number Diff line number Diff line change
Expand Up @@ -1915,7 +1915,7 @@ assert passiveState == GSS_S_COMPLETE
assert passiveContext.passive
assert passiveContext.ssp_context.passive

= GSS_Accept_sec_context rejects a replayed AP-REQ
= GSS_Accept_sec_context rejects a replayed AP-REQ without the replay flag

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
= GSS_Accept_sec_context rejects a replayed AP-REQ without the replay flag
= GSS_Accept_sec_context rejects a replayed AP-REQ


from scapy.layers.gssapi import GSS_S_DUPLICATE_TOKEN

Expand Down
Loading