Skip to content

smbserver: add SMB 3.1.1 dialect support#2216

Open
n3rada wants to merge 3 commits into
fortra:masterfrom
n3rada:feat/smb311-server
Open

smbserver: add SMB 3.1.1 dialect support#2216
n3rada wants to merge 3 commits into
fortra:masterfrom
n3rada:feat/smb311-server

Conversation

@n3rada

@n3rada n3rada commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Hello maintainers 👋

Closes #1981.
Closes #1829.

smbserver.py only negotiated SMB 2.0.2 regardless of what the client offered. This PR adds real SMB 3.1.1 support on the server side.

What this PR does:

  • Dialect selection: the server now picks the best common dialect from what the client offers (0x0311 > 0x0210 > 0x0202). When an SMB1 negotiate arrives with SMB 2.???, the server responds with the wildcard dialect so the client sends a proper SMB2 negotiate, which can then land on 3.1.1.

  • Negotiate contexts: for dialect 3.1.1 the server appends SMB2_PREAUTH_INTEGRITY_CAPABILITIES (SHA-512, no salt) and SMB2_ENCRYPTION_CAPABILITIES (AES-128-GCM preferred, AES-128-CCM fallback) to the negotiate response.

  • Pre-authentication integrity hash: the server maintains a connection-level pre-auth hash across the negotiate exchange and forks it into a session-level hash at session setup, updating it with each request/response pair.

  • Signing key derivation: once authentication succeeds the raw session key is replaced by the SP 800-108 counter-mode KBKDF output (label="SMBSigningKey\x00", context=session pre-auth hash, L=128).

  • AES-CMAC signing: a new signSMBv3 method signs outgoing packets with AES-CMAC instead of HMAC-SHA256, as required for the SMB 3.x dialect family.

  • Session encryption: when the client offers encryption ciphers, the server derives SessionEncryptionKey and SessionDecryptionKey via KBKDF, sets SMB2_SESSION_FLAG_ENCRYPT_DATA in the session setup response, and wraps all subsequent traffic in SMB2_TRANSFORM_HEADER (AES-128-GCM or AES-128-CCM). Incoming encrypted packets are decrypted before processing.

The validateNegotiateInfo response now echoes back the negotiated dialect instead of always returning 0x0202.

Best regards

@n3rada n3rada force-pushed the feat/smb311-server branch from 3388a75 to 48d8ee7 Compare June 24, 2026 06:10
@n3rada n3rada force-pushed the feat/smb311-server branch from 48d8ee7 to 68b4c6b Compare June 24, 2026 11:48
@n3rada

n3rada commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

This PR also fixes #1829.

processRequest was setting respPacket['SessionID'] = connData['Uid'] for all SMB2 responses. smb2Logoff zeroes connData['Uid'] before returning, so the LOGOFF response was always sent with SessionID=0.

SESSION_SETUP assigns a new SessionId; all other commands must echo the SessionId from the request. The fix applies exactly that rule:

if packet['Command'] == smb2.SMB2_SESSION_SETUP:
    respPacket['SessionID'] = connData['Uid']
else:
    respPacket['SessionID'] = packet['SessionID']

@anadrianmanrique anadrianmanrique added the in review This issue or pull request is being analyzed label Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in review This issue or pull request is being analyzed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request - Add SMB3 support to smbserver.py smbserver clears Session Id on LogOff Response breaking signature verification

3 participants