smb: fix SMB 3.1.1 signing for Kerberos sessions - #43
Merged
Conversation
The SMB 3.1.1 preauth integrity hash is the KDF context for the signing
and encryption keys. On the Kerberos single-leg SESSION_SETUP path
(immediate STATUS_SUCCESS), the hash was computed incorrectly: leg 1's
request and response were both folded in, then the request was hashed a
second time during key derivation, yielding
neg + req1 + resp1 + req1
instead of the correct
neg + req1
Per MS-SMB2 3.2.5.3 the final SESSION_SETUP response (STATUS_SUCCESS) is
excluded from the preauth hash. The wrong hash produced the wrong signing
key, so the DC rejected outbound requests (STATUS_ACCESS_DENIED) and
inbound response signatures failed to verify. NTLM was unaffected because
its two-leg exchange happens to hash exactly the right messages.
Fold each participating SESSION_SETUP message into the hash exactly once,
at the point it is sent or received, and exclude the final success
response. The two-leg NTLM path is unchanged (neg + req1 + resp1 + req2).
Also re-enable inbound signature verification in verify(), which had been
stubbed to print a mismatch warning and return true unconditionally -- a
debug bypass that disabled inbound signature checking entirely. Compare
the MAC in constant time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SMB2/3 signing was broken for Kerberos-authenticated sessions against a
signing-required SMB 3.1.1 server (e.g. a modern Windows DC). The session
established, but every signed operation failed: outbound requests were
rejected with
STATUS_ACCESS_DENIED("permission denied") and inboundresponse signatures failed to verify. NTLM against the same host worked
fine. This is a pre-existing bug, unrelated to the recent transport work.
Root cause
For SMB 3.1.1 the signing/encryption keys are derived with the preauth
integrity hash as the KDF context, so that hash must cover exactly the
right SESSION_SETUP messages (MS-SMB2 3.2.5.3): every request, every
interim response, and not the final
STATUS_SUCCESSresponse.The code hashed leg 1's request and response unconditionally, then hashed
"the final request" again during key derivation. For NTLM's two-leg
exchange that happens to land on the correct set:
But Kerberos completes in a single leg (immediate
STATUS_SUCCESS), so thesame code produced:
— wrongly including the final success response and double-hashing the
request. Wrong hash → wrong signing key → outbound rejected, inbound
mismatched.
The Kerberos session-key handling itself (truncating the AES256 key to 16
bytes) is correct per MS-SMB2 and matches Impacket, and is unchanged.
Fix
PreauthIntegrityHashValueexactly once, at the point it is sent orreceived, excluding the final success response. Single-leg Kerberos now
hashes
neg + req1; two-leg NTLM is unchanged (neg + req1 + resp1 + req2).verify(). It had beenstubbed to log a mismatch warning and return
trueunconditionally — adebug bypass that disabled inbound signature checking entirely. The MAC
is now compared in constant time.
Testing
Verified end-to-end against a Windows Server 2019 DC (SMB 3.1.1,
signing required) with enforcement on:
shares listed,
C$mounted, directory enumerated — no signaturemismatch, no permission denied.
go build ./...andgo test ./pkg/third_party/smb2/pass.