Closed zeroSteiner closed 2 years ago
Thanks @zeroSteiner for this PR. I really like the design and kudos for the use of BinData's delayed_io
!
I'm starting to test this with multiple clients and let you know how it goes. In the meantime, I left some questions/comments for you to review.
Thanks @zeroSteiner for updating this. I just left a comment about the STATUS_SMB_BAD_UID
error code. I was able to read and copy files (as instructed in the Testing section) with the following Windows clients:
I noticed different behaviors and possible issues on Windows Server 2012 and 2019
@read_timeout
expires. This causes the server to break the main processing loop in server_client.rb
. When running the same command again on the client, it will reuse the same connection, which is already terminated (but not closed) on the server. This will block the client until it times out and reconnects. I believe the socket should be closed when the processing loop exits. This will force the client to reconnect.@read_timeout
expires.I also tested directly with explorer.exe
and found out it works well with Windows Server 2019. This PR is not intended to make the SMB server compatible with explorer.exe
, but here are my findings for future improvements:
QUERY_DIRECTORY
requests, which is not supported. This causes issues when displaying directories content and, sometimes, makes some directories inaccessible.Switched the read timeout to nil
and updated it to call #disconnect!
when exiting the main dispatcher loop. Should fix that issue. I think all comments have been addressed now at this point, if I missed something please just let me know.
Overview
This adds the initial support for an SMB version 2 and 3 read-only file server to RubySMB. This builds on the server work that started in #177. What does work right now is everything necessary to read, copy and load files (like PrintNightmare) from the server. All SMB2/3 dialects have been tested from Server 2019 using the server's internal dialect selection. The current implementation works with clients that require signing but not encryption.
A lot of functionality is still missing. In these cases, the server code will raise a
NotImplementedError
which will be caught and translated into aSTATUS_NOT_SUPPORTED
error response.Architecture
Similar to the design of the existing authentication system, the server can have different "share providers" defined. These providers define the basic attributes of the share, like what kind it is, its name, etc and are shared across all client connections/sessions. The providers themselves don't do much but rather define a processor object which is created for each TREE_CONNECT request and handles the actual processing of requests for its share. The processor instance is specific to a single authenticated session and thus can store context-sensitive things within itself (like the open file handles).
Logging
This adds some initial logging to RubySMB which has been really helpful for testing. The server has a configurable
#logger
attribute that's used by everything below it. That's what is generating the output in the example script.Session Handling
This changes the server to create and handle individual sessions (tracked in the
#session_table
). This means that the session ID, key and identity attributes have all been removed from theServerClient
instance. A connection can establish and authenticate multiple sessions so this attempts to support that. Unlike a more feature-complete implementation however, fancy things like re-establishing sessions isn't supported. Once the session is gone, it's gone.File Handling
There are a bunch of context objects used by Windows when opening a file that defines how the file should be open. This includes things like leasing and durable (v1/v2) handles. In order for the SMB client to process the CREATE response, there must be a context response for each context request. Leasing is not supported, and the flag indicating that is already accurate however when leasing is not supported, Windows will attempt to create "durable" handles. These contexts are read and processed so a response can be made however the durable-handle logic isn't entirely complete. This means a client attempting to re-establish a durable handle will fail. This is the case when there's a connection break during an operation like reading the file.
The server doesn't provide full support for the wildcard syntax that's supposed to be in place. The current implementation handles enough for file reads by converting the query to a regular expression. If the wildcard can't be converted to a regular expression, a
NotImplementedError
will be raised. I have not encountered a scenario where a wildcard other than "everything" is used, however, I didn't spend a lot of time looking for one. The best reference I found on this is from the Samba project because the official docs are not super clear.NTStatus
The
Field::NTStatus
was updated so it can be assigned directly from aWindowError::NTStatus
instance. This makes it possible to just doheader.nt_status = WindowsError::NTStatus::STATUS_SUCCESS
instead of having to convert the error code object to an integer. Unfortunately, that does mean that the changes from rapid7/windows_error#3 are required.Demo Server
Included is an example file server script. It takes multiple command-line arguments to define the share, a user account and optionally the SMB versions that can be used. The SMBv1 isn't supported right now, but in the future, it's support can be explicitly disabled using the
--no-smbv1
option.Example
Ruby SMB Server
``` [smcintyre@localhost ruby_smb]$ cat ~/test.txt Hello World! [smcintyre@localhost ruby_smb]$ ruby examples/file_server.rb --username MSFLAB\\smcintyre --password Password1! --path ~ --share public D, [2021-12-06T14:33:55.786079 #54050] DEBUG -- : Adding disk share: public server is running received connection I, [2021-12-06T14:34:10.194055 #54050] INFO -- : Negotiated dialect: SMB v3.1.1 D, [2021-12-06T14:34:10.203282 #54050] DEBUG -- : NTLM authentication request received for MSFLAB\smcintyre I, [2021-12-06T14:34:10.203473 #54050] INFO -- : NTLM authentication request succeeded for MSFLAB\smcintyre D, [2021-12-06T14:34:10.208413 #54050] DEBUG -- : Received TREE_CONNECT request for share: public D, [2021-12-06T14:34:10.218177 #54050] DEBUG -- : Received CREATE request for share: public D, [2021-12-06T14:34:10.229277 #54050] DEBUG -- : Received QUERY_DIRECTORY request for share: public D, [2021-12-06T14:34:10.297318 #54050] DEBUG -- : Received QUERY_DIRECTORY request for share: public D, [2021-12-06T14:34:10.300209 #54050] DEBUG -- : Received CREATE request for share: public D, [2021-12-06T14:34:10.305929 #54050] DEBUG -- : Received CLOSE request for share: public D, [2021-12-06T14:34:10.310269 #54050] DEBUG -- : Received CREATE request for share: public D, [2021-12-06T14:34:10.318081 #54050] DEBUG -- : Received QUERY_INFO request for share: public D, [2021-12-06T14:34:10.321805 #54050] DEBUG -- : Received QUERY_INFO request for share: public D, [2021-12-06T14:34:10.325901 #54050] DEBUG -- : Received QUERY_INFO request for share: public D, [2021-12-06T14:34:10.329941 #54050] DEBUG -- : Received QUERY_INFO request for share: public D, [2021-12-06T14:34:10.333996 #54050] DEBUG -- : Received QUERY_INFO request for share: public D, [2021-12-06T14:34:10.339238 #54050] DEBUG -- : Received QUERY_INFO request for share: public D, [2021-12-06T14:34:10.342504 #54050] DEBUG -- : Received READ request for share: public D, [2021-12-06T14:34:10.346375 #54050] DEBUG -- : Received CLOSE request for share: public ```Windows SMB Client (Server 2019 Instance)
Next Steps
tools/
maybe?)Testing
Use the included example file server script. Both copying (
copy \\target\share\file .
) and reading (type \\target\share\file .
) should be tested. The operations use different information classes in the QUERY_INFO and QUERY_DIRECTORY calls. Both should work for both SMB v2 and SMB v3. If the SMB client requires encryption, the operation will probably fail because that's not supported yet.If any issues comes up, please collect the log output from the file server script along with a PCAP of the operation.