Skip to content

smb2: support true anonymous (null session) NTLM bind#36

Merged
psycep merged 1 commit into
mainfrom
anonymous-null-session
Jun 8, 2026
Merged

smb2: support true anonymous (null session) NTLM bind#36
psycep merged 1 commit into
mainfrom
anonymous-null-session

Conversation

@psycep

@psycep psycep commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds support for establishing a true SMB anonymous (null) session in the vendored go-smb2 engine (pkg/third_party/smb2). Previously an NTLMInitiator with an empty username was rejected at dial time with InternalError{"Anonymous account is not supported yet. Use guest account instead"}, before any packet was sent. This blocked smbclient -N-style null-session share enumeration, which is inherited by everything built on this engine.

A null session is NTLM anonymous authentication ([MS-NLMP] §3.1.5.1.2): the client sends an AUTHENTICATE_MESSAGE with the NTLMSSP_NEGOTIATE_ANONYMOUS flag set, an empty NtChallengeResponse, a single 0x00-byte LmChallengeResponse, no MIC, and no session key (null sessions are never signed). The scaffolding for this existed but was never finished.

Changes

  • client.go — remove the dial-time guard that rejected empty-user NTLM initiators outright, so an anonymous bind proceeds to negotiate + session setup.
  • internal/ntlm/client.go — add an anonymous branch to the AUTHENTICATE builder:
    • empty NtChallengeResponse, single 0x00-byte LmChallengeResponse, no MIC, no session key;
    • NTLMSSP_NEGOTIATE_ANONYMOUS set;
    • DomainName/UserName/Workstation forced empty per spec;
    • SIGN/SEAL/KEY_EXCH/ALWAYS_SIGN flags cleared — leaving KEY_EXCH set with a zero-length EncryptedRandomSessionKey causes strict servers (e.g. Samba) to reject the bind with STATUS_INVALID_PARAMETER;
    • the over-allocated message buffer (sized for a full NTLMv2 response) is trimmed so no trailing zero bytes ride along in the token.
  • initiator.go — nil-guard Sum, SessionKey, and infoMap so the keyless anonymous path cannot panic.
  • internal/ntlm/ntlm_test.go — add TestAnonymousAuthenticate asserting the on-wire AUTHENTICATE fields (anonymous flag set, empty NT response, 1-byte LM response, empty domain/user/workstation, no session key, zero MIC).

The authenticated NTLMv2 path and the existing IS_NULL/IS_GUEST handling in session.go are unchanged.

Wire compatibility

The resulting token matches what impacket and Windows clients send for an anonymous bind (empty NT response, 1-byte LM response, no key, no MIC), and additionally sets the explicit NTLMSSP_NEGOTIATE_ANONYMOUS flag that Windows clients set and [MS-NLMP] describes.

Testing

  • go build ./..., go vet ./pkg/third_party/smb2/..., and go test ./pkg/third_party/smb2/... all pass (including the new unit test).
  • Live (Samba): against a server permitting null sessions, the bind succeeds and shares list; the server logs it as ANONYMOUS LOGON (S-1-5-7) with empty client domain/account/workstation — a genuine null logon, not a guest mapping. Against a server with restrict anonymous = 2, the operation now fails gracefully with a server status (surfaced as os.ErrPermission) instead of the old client-side error, with no panic. An authenticated (user + password) bind against the same server is unaffected.

Allow NTLMInitiator with empty User/Password/Hash to establish an SMB
anonymous (null) session instead of refusing at dial time.

- client.go: drop the dial-time guard that rejected empty-user NTLM
  initiators outright.
- internal/ntlm/client.go: add an anonymous AUTHENTICATE branch per
  [MS-NLMP] 3.1.5.1.2 -- empty NtChallengeResponse, a single 0x00 byte
  LmChallengeResponse, NTLMSSP_ANONYMOUS set, no MIC and no session key.
  DomainName/UserName/Workstation are forced empty and the signing,
  sealing and key-exchange flags are cleared (leaving KEY_EXCH set with a
  zero-length EncryptedRandomSessionKey makes strict servers such as Samba
  reject the bind with STATUS_INVALID_PARAMETER).
- initiator.go: nil-guard Sum/SessionKey/infoMap so the keyless anonymous
  path cannot panic.

Verified against Samba: a server permitting null sessions accepts the
bind (logged as ANONYMOUS LOGON, S-1-5-7) and lists shares; a server with
restrict anonymous=2 fails gracefully via os.ErrPermission; authenticated
binds are unchanged.
@psycep psycep merged commit 2e9653b into main Jun 8, 2026
6 checks passed
@psycep psycep deleted the anonymous-null-session branch June 8, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant