Open
Conversation
Win95 uses a pre-NTLMSSP dialect of SMB1 that differs from modern Windows in several ways. This commit adds the minimum changes needed to handle those differences across the negotiate, auth, tree connect, and file listing phases. Negotiate (negotiation.rb, negotiate_response.rb): - Handle NegotiateResponse (non-extended security) in addition to NegotiateResponseExtended. Win95 returns a raw 8-byte challenge instead of a SPNEGO security blob. - Override DataBlock#do_read in NegotiateResponse to tolerate byte_count=8 responses that omit domain_name and server_name (Win95 only sends the challenge). Authentication (authentication.rb, session_setup_legacy_request.rb, session_setup_legacy_response.rb): - Add smb1_legacy_authenticate path triggered when the negotiate phase stored an 8-byte challenge (@smb1_negotiate_challenge). Computes LM and NTLM challenge-response hashes via Net::NTLM and sends them in SessionSetupLegacyRequest. - Fix SessionSetupLegacyRequest DataBlock: account_name and primary_domain were fixed-length `string` (2 bytes) instead of null-terminated `stringz`, truncating the username and domain. - Override DataBlock#do_read in SessionSetupLegacyResponse to handle byte_count=0 responses (Win95 returns no string fields). NetBIOS session (client.rb, client_spec.rb): - Use @local_workstation as the calling name in NetBIOS session requests. Win95 rejects sessions with an empty calling name. Tree connect (tree_connect_response.rb): - Make optional_support conditional on word_count >= 3. Win95 returns a smaller ParameterBlock without this field. - Override DataBlock#do_read to handle responses that omit native_file_system when byte_count only covers the service field. File listing (tree.rb, find_information_level.rb, find_info_standard.rb): - Add FindInfoStandard struct for SMB_INFO_STANDARD (level 1), the LANMAN 2.0 information level used by Win95. - When type is FindInfoStandard, disable unicode in the request, request up to 255 entries, and return early after FIND_FIRST2 using raw byte parsing (parse_find_first2_info_standard) that bypasses BinData alignment issues with Win95 responses. - Clamp max_data_count to server_max_buffer_size in set_find_params so requests don't exceed Win95's small buffer limit. - Add defensive guards in the FIND_NEXT2 pagination loop: use safe navigation on results.last, check for empty batches, and require `last` to be non-nil before continuing. These prevent infinite loops when the server returns zero results. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move three protocol features from the smb_browser application into the
ruby_smb library so they are reusable:
SMB_COM_OPEN_ANDX (0x2D) packet classes and Tree#open_andx:
New OpenAndxRequest and OpenAndxResponse BinData packet classes
implement the LANMAN 1.0 file-open command. Tree#open_andx uses
these to open files on servers that lack NT_CREATE_ANDX, such as
Windows 95/98/ME. Returns a standard SMB1::File handle that
supports read/write/close via the existing ReadAndx/WriteAndx
infrastructure.
Share-level password on tree_connect:
Client#tree_connect and smb1_tree_connect now accept a password:
keyword argument. When provided, the password is placed in the
TreeConnectRequest data block with the null terminator and correct
password_length, enabling connection to shares protected by
share-level authentication (Windows 95/98/ME).
RAP share enumeration via Client#net_share_enum_rap:
New method sends a NetShareEnum RAP request (function 0) over
\PIPE\LANMAN to enumerate shares on servers that do not support
DCERPC/srvsvc. Returns an array of {name:, type:} hashes.
Automatically connects to and disconnects from IPC$.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The password field in TreeConnectRequest::DataBlock was declared as BinData::Stringz, which always appends a null terminator when serializing regardless of the length parameter. With password_length set to N, the field wrote N+1 bytes (the password data plus a null), corrupting the share path that follows in the packet. This broke any use case requiring exact control over the password byte count, such as CVE-2000-0979 exploitation. Windows 95 uses password_length to decide how many bytes to validate. With the extra null, the server read the correct password bytes but then parsed the null as the start of the share path, causing every tree connect to fail with a path error rather than a password result. Change the field from stringz to string. BinData::String respects the length parameter exactly, writing precisely password_length bytes with no trailing null. The initial_value changes from '' to "\x00" to preserve the default behavior: when no password is set, the field writes one null byte (matching the default password_length of 1). Existing callers are unaffected because smb1_tree_connect already appends the null terminator explicitly (password + "\x00") and sets password_length to include it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Windows 95 rejects NetBIOS session requests using the wildcard name '*SMBSERVER'. When the server responds with "Called name not present", look up the server's actual NetBIOS name via nmblookup or a raw UDP Node Status query (RFC 1002, port 137), reconnect the TCP socket, and retry the session request with the resolved name. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Fixes #293
Summary
TreeConnectRequestpassword field serialization bug that corrupted packets when callers setpassword_lengthwithout a trailing null*SMBSERVERwildcard is rejectedDetails
Windows 95/98/ME SMB1 support (
d0d2607)Windows 9x hosts differ from NT-based systems in several ways that RubySMB did not handle:
extended_securityflag, no NTLMSSP)SessionSetupLegacyRequest/SessionSetupLegacyResponseinstead of NTLMSSP blobsnative_file_systemfield; the response DataBlock now handles short readsSMB_FIND_FILE_STANDARDinformation level for TRANS2 directory listingsERRDOS/ERRnomemfrom TRANS2 by falling back to core protocol commandsSMB_COM_OPEN_ANDX, share-level auth, and RAP (
10ed8be)net_share_enum_rap: enumerate shares via RAP (Remote Administration Protocol) over\PIPE\LANMAN, the only method Windows 9x supports (DCERPC/SRVSVC fails withDBG_CONTINUE)smb1_tree_connectpassword support: accept apassword:keyword for share-level authentication where passwords are per-share, not per-userSMB_COM_OPEN_ANDX: add request/response packets andTree#open_file/Tree#read_file/Tree#write_filefor Win95 hosts that lack NT_CREATE_ANDXFix TreeConnectRequest password field (
541321c)The password field was declared as
BinData::Stringz, which always appends a null terminator when serializing, regardless of thelengthparameter. Withpassword_length=N, the field wrote N+1 bytes, shifting the share path and corrupting the packet.Changed to
BinData::Stringwhich respects the length exactly. Existing callers are unaffected sincesmb1_tree_connectalready appends"\x00"explicitly.NetBIOS name resolution fallback (
b5269e0)Windows 95 rejects NetBIOS session requests using the
*SMBSERVERwildcard. When the server responds with "Called name not present", the client now resolves the server's actual NetBIOS name vianmblookupor a raw UDP Node Status query (RFC 1002),reconnects, and retries.
Test plan
bundle exec rspec— all existing specs pass (verified: 407 examples, 0 failures)direct: false,versions: [1]smb_loginwith empty credentials (share-level auth)net_share_enum_rapreturns share listtree_connectwithpassword:keyword to a password-protected shareTreeConnectRequestwithpassword_length=1serializes exactly 1 password byte (no extra null)*SMBSERVERresolves the name and retries