jborean93 / smbprotocol

Python SMBv2 and v3 Client
MIT License
318 stars 73 forks source link

is smbprotocol support anonymous login #168

Open hackoflpf opened 2 years ago

hackoflpf commented 2 years ago

I can't use smbprotocol to login as a guest user,if i set username and password is None,the program behave like belows, INFO:smbprotocol.connection:Initialising connection, guid: 0f41207e-d29d-4543-a60c-6dcad6339edf, require_signing: False, server_name: 127.0.0.1, port: 445 False INFO:smbprotocol.connection:Setting up transport connection INFO:smbprotocol.transport:Connecting to DirectTcp socket INFO:smbprotocol.connection:Starting negotiation with SMB server INFO:smbprotocol.connection:Negotiating with SMB2 protocol with highest client dialect of: SMB_3_0_0 INFO:smbprotocol.connection:Sending SMB2 Negotiate message INFO:smbprotocol.connection:Receiving SMB2 Negotiate response INFO:smbprotocol.connection:Negotiated dialect: (768) SMB_3_0_0 INFO:smbprotocol.connection:Connection require signing: False INFO:smbprotocol.session:Initialising session with username: admins INFO:smbprotocol.connection:Disconnecting transport connection INFO:smbprotocol.transport:Disconnecting DirectTcp socket Traceback (most recent call last): File "/home/admins/.local/lib/python3.8/site-packages/smbprotocol/session.py", line 266, in connect context = spnego.client(self.username, self.password, service='cifs', hostname=self.connection.server_name, File "/home/admins/.local/lib/python3.8/site-packages/spnego/auth.py", line 202, in client return _new_context( File "/home/admins/.local/lib/python3.8/site-packages/spnego/auth.py", line 117, in _new_context return proxy( File "/home/admins/.local/lib/python3.8/site-packages/spnego/_ntlm.py", line 287, in init self._credential = _NTLMCredential(next(c for c in credentials if "ntlm" in c.supported_protocols)) File "/home/admins/.local/lib/python3.8/site-packages/spnego/_ntlm.py", line 227, in init self.domain, self.username, self.lm_hash, self.nt_hash = _get_credential(self._store, domain, username) File "/home/admins/.local/lib/python3.8/site-packages/spnego/_ntlm.py", line 137, in _get_credential raise OperationNotAvailableError(context_msg="Retrieving NTLM store without NTLM_USER_FILE set to a filepath") spnego.exceptions.OperationNotAvailableError: SpnegoError (16): Operation not supported or available, Context: Retrieving NTLM store without NTLM_USER_FILE set to a filepath

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "file-management.py", line 30, in session.connect() File "/home/admins/.local/lib/python3.8/site-packages/smbprotocol/session.py", line 269, in connect raise SMBAuthenticationError("Failed to authenticate with server: %s" % str(err.message)) smbprotocol.exceptions.SMBAuthenticationError: Failed to authenticate with server: SpnegoError (16): Operation not supported or available, Context: Retrieving NTLM store without NTLM_USER_FILE set to a filepath

my code is


import uuid

from smbprotocol.connection import Connection
from smbprotocol.create_contexts import CreateContextName, \
    SMB2CreateContextRequest, SMB2CreateQueryMaximalAccessRequest
from smbprotocol.security_descriptor import AccessAllowedAce, AccessMask, \
    AclPacket, SDControl, SIDPacket, SMB2CreateSDBuffer
from smbprotocol.session import Session
from smbprotocol.structure import FlagField
from smbprotocol.open import CreateDisposition, CreateOptions, \
    FileAttributes, FilePipePrinterAccessMask, ImpersonationLevel, Open, \
    ShareAccess
from smbprotocol.tree import TreeConnect
import logging
server = "127.0.0.1"
port = 445
username = None
password = None
share = r"\\%s\tm" % server
file_name = "file-test.txt"
logging.basicConfig(level=logging.INFO)
connection = Connection(uuid.uuid4(), server, port,require_signing=False)
connection.connect(0x300)

try:
    session = Session(connection, username, password,require_encryption=False,auth_protocol="ntlm")
    session.connect()
    tree = TreeConnect(session, share)
    tree.connect()
    exit(0)
    # ensure file is created, get maximal access, and set everybody read access
    max_req = SMB2CreateContextRequest()
    max_req['buffer_name'] = \
        CreateContextName.SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST
    max_req['buffer_data'] = SMB2CreateQueryMaximalAccessRequest()

    # create security buffer that sets the ACL for everyone to have read access
    everyone_sid = SIDPacket()
    everyone_sid.from_string("S-1-1-0")

    ace = AccessAllowedAce()
    ace['mask'] = AccessMask.GENERIC_ALL
    ace['sid'] = everyone_sid

    acl = AclPacket()
    acl['aces'] = [ace]

    sec_desc = SMB2CreateSDBuffer()
    sec_desc['control'].set_flag(SDControl.SELF_RELATIVE)
    sec_desc.set_dacl(acl)
    sd_buffer = SMB2CreateContextRequest()
    sd_buffer['buffer_name'] = CreateContextName.SMB2_CREATE_SD_BUFFER
    sd_buffer['buffer_data'] = sec_desc

    create_contexts = [
        max_req,
        sd_buffer
    ]

    file_open = Open(tree, file_name)
    open_info = file_open.create(
        ImpersonationLevel.Impersonation,
        FilePipePrinterAccessMask.GENERIC_READ |
        FilePipePrinterAccessMask.GENERIC_WRITE,
        FileAttributes.FILE_ATTRIBUTE_NORMAL,
        ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE,
        CreateDisposition.FILE_OVERWRITE_IF,
        CreateOptions.FILE_NON_DIRECTORY_FILE,
        create_contexts
    )

    # as the raw structure 'maximal_access' is an IntField, we create our own
    # flag field, set the value and get the human readble string
    max_access = FlagField(
        size=4,
        flag_type=FilePipePrinterAccessMask,
        flag_strict=False
    )
    max_access.set_value(open_info[0]['maximal_access'].get_value())
    print("Maximum access mask for file %s\\%s: %s"
          % (share, file_name, str(max_access)))

    # write to a file
    text = "Hello World, what a nice day to play with SMB"
    file_open.write(text.encode('utf-8'), 0)

    # read from a file
    file_text = file_open.read(0, 1024)
    print("Text of file %s\\%s: %s"
          % (share, file_name, file_text.decode('utf-8')))
    file_open.close(False)

    # read and delete a file in a single SMB packet instead of 3
    file_open = Open(tree, file_name)
    delete_msgs = [
        file_open.create(
            ImpersonationLevel.Impersonation,
            FilePipePrinterAccessMask.GENERIC_READ |
            FilePipePrinterAccessMask.DELETE,
            FileAttributes.FILE_ATTRIBUTE_NORMAL,
            0,
            CreateDisposition.FILE_OPEN,
            CreateOptions.FILE_NON_DIRECTORY_FILE |
            CreateOptions.FILE_DELETE_ON_CLOSE,
            send=False
        ),
        file_open.read(0, len(text), send=False),
        file_open.close(False, send=False)
    ]
    requests = connection.send_compound([x[0] for x in delete_msgs],
                                        session.session_id,
                                        tree.tree_connect_id, related=True)
    responses = []
    for i, request in enumerate(requests):
        response = delete_msgs[i][1](request)
        responses.append(response)
    print("Text of file when reading/deleting in 1 request: %s"
          % responses[1].decode('utf-8'))
finally:
    connection.disconnect(True)
jborean93 commented 2 years ago

Technically it should be possible but I remember trying to get it working when I first wrote the code and it was difficult. You can try setting the username to Guest with an empty string as the password but I doubt that’s going to work. Using a Guest or Anonymous logon loses a lot of the security benefits that SMB had introduced like message signatures and encryption. You are better off adding an account that can you and authenticate with rather than an anonymous user in general.

adiroiban commented 2 years ago

Does your server support anonymous/guest session?

I remember that guest accounts are supported by Windows Desktop, but guest accounts are disabled by default with Windows Server.

See this docs - https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/jj852219%28v=ws.11%29

As commented by Jordan, you will need to use Guest as username. You will need to set password to something ... and that value is ignored.

From my code

    # Some custom logicc
    anonymous = True
    username = 'Guest'
    # Anonymous needs to have a password that is later ignored.
    # Otherwise pyspnego will try to load it from a file.
    password = 'ignored'

Setting to None should be avoided as username and password API should require these values to have a text type.

And you can see from the error that it fails as it tries to load the password from a file.

jborean93 commented 2 years ago

Thanks @adiroiban that about sums up what I came across before, the Guest could be used with any value for the password as it was ignored on the server. I believe Anonymous accounts are slightly different to Guest and needs extra work in pyspnego to use.

hackoflpf commented 2 years ago

Of course,i test this on ubuntu with samba 4.15.0,i can use the default samba tool smbclient to login without password,but if i set the password with None value,smbprotocol will raise exceptions.This is my smb.conf [check] path = /home/test/ browseable = yes public = yes available = yes oplocks = yes follow symlinks = yes map archive = no guest ok = yes writable = yes Maybe the Spnego library doesn't support this operation,i haven't check the code,but i can use samba python library to login without password,only to set the username is "" and password is None,it works well.

jborean93 commented 2 years ago

Pyspnego certainly doesn’t support anonymous users as you’ve seen which leans smbprotocol also does not support anonymous logons. It should work with Guest logins which has a username of Guest and the password is set to any string value. They are slightly different but could still work in your scenario if your samba server is configured to allow it.