rapid7 / ruby_smb

A native Ruby implementation of the SMB Protocol Family
Other
79 stars 82 forks source link

Initial Read-only SMB2/3 File Server #181

Closed zeroSteiner closed 2 years ago

zeroSteiner commented 2 years ago

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 a STATUS_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 the ServerClient 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 a WindowError::NTStatus instance. This makes it possible to just do header.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.

ruby examples/file_server.rb -h
Usage: file_server.rb [options]
        --path PATH                  The path to share (default: .)
        --share SHARE                The share name (default: home)
        --[no-]anonymous             Allow anonymous access (default: true)
        --[no-]smbv1                 Enabled or disable SMBv1 (default: Enabled)
        --[no-]smbv2                 Enabled or disable SMBv2 (default: Enabled)
        --[no-]smbv3                 Enabled or disable SMBv3 (default: Enabled)
        --username USERNAME          The account's username (default: RubySMB)
        --password PASSWORD          The account's password (default: password)

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)

Microsoft Windows [Version 10.0.17763.2028]
(c) 2018 Microsoft Corporation. All rights reserved.

msflab\smcintyre@WIN-3MSP8K2LCGC C:\Users\smcintyre>type \\192.168.159.128\public\test.txt 
Hello World! 

msflab\smcintyre@WIN-3MSP8K2LCGC C:\Users\smcintyre> 

Next Steps

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.

cdelafuente-r7 commented 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.

cdelafuente-r7 commented 2 years ago

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

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:

zeroSteiner commented 2 years ago

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.