Exemplo n.º 1
0
	async def negotiate(self):
		"""
		"""

		rply = await self.recvSMB(0)

		#TODO: check if SMB2 is supported
		#currently we just continue with SMB2

		command = NEGOTIATE_REPLY()
		command.SecurityMode = NegotiateSecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED
		command.DialectRevision = NegotiateDialects.WILDCARD
		command.NegotiateContextCount = 0
		command.ServerGuid = self.ServerGuid
		command.Capabilities = 0
		command.SystemTime = datetime.datetime.now()
		command.ServerStartTime = datetime.datetime.now() - datetime.timedelta(days=1)
		command.Buffer = self.client_gssapi.get_mechtypes_list()

			
		header = SMB2Header_SYNC()
		header.Command  = SMB2Command.NEGOTIATE
		header.CreditReq = 0
		
		msg = SMBMessage(header, command)
		message_id = await self.sendSMB(msg)
		print(message_id)
		rply = await self.recvSMB(1)
		#recieveing reply, should be version2, because currently we dont support v1 :(
		 #negotiate MessageId should be 1
		
		print('1111111111111111111111111')

		#TODO: check if SMB2 is supported
		#currently we just continue with SMB2

		command = NEGOTIATE_REPLY()
		command.SecurityMode = NegotiateSecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED
		command.DialectRevision = NegotiateDialects.SMB202
		command.NegotiateContextCount = 0
		command.ServerGuid = self.ServerGuid
		command.Capabilities = 0
		command.SystemTime = datetime.datetime.now()
		command.ServerStartTime = datetime.datetime.now() - datetime.timedelta(days=1)
		command.Buffer = self.client_gssapi.get_mechtypes_list()

			
		header = SMB2Header_SYNC()
		header.Command  = SMB2Command.NEGOTIATE
		header.CreditReq = 0
		
		msg = SMBMessage(header, command)
		message_id = await self.sendSMB(msg)
		
		self.status = SMBConnectionStatus.SESSIONSETUP
		return
Exemplo n.º 2
0
    async def tree_connect(self, share_name):
        """
		share_name MUST be in "\\server\share" format! Server can be NetBIOS name OR IP4 OR IP6 OR FQDN
		"""
        if self.session_closed == True:
            return
        command = TREE_CONNECT_REQ()
        command.Path = share_name
        command.Flags = 0

        header = SMB2Header_SYNC()
        header.Command = SMB2Command.TREE_CONNECT

        msg = SMBMessage(header, command)
        message_id = await self.sendSMB(msg)

        rply = await self.recvSMB(message_id)

        if rply.header.Status == NTStatus.SUCCESS:
            te = TreeEntry.from_tree_reply(rply, share_name)
            self.TreeConnectTable_id[rply.header.TreeId] = te
            self.TreeConnectTable_share[share_name] = te
            return te

        elif rply.header.Status == NTStatus.BAD_NETWORK_NAME:
            raise SMBIncorrectShareName()

        elif rply.header.Status == NTStatus.USER_SESSION_DELETED:
            self.session_closed = True
            raise SMBException('session delted', NTStatus.USER_SESSION_DELETED)

        else:
            raise SMBGenericException()
Exemplo n.º 3
0
    async def create(self,
                     tree_id,
                     file_path,
                     desired_access,
                     share_mode,
                     create_options,
                     create_disposition,
                     file_attrs,
                     impresonation_level=ImpersonationLevel.Impersonation,
                     oplock_level=OplockLevel.SMB2_OPLOCK_LEVEL_NONE,
                     create_contexts=None,
                     return_reply=False):
        if self.session_closed == True:
            return

        if tree_id not in self.TreeConnectTable_id:
            raise Exception('Unknown Tree ID!')

        command = CREATE_REQ()
        command.RequestedOplockLevel = oplock_level
        command.ImpersonationLevel = impresonation_level
        command.DesiredAccess = desired_access
        command.FileAttributes = file_attrs
        command.ShareAccess = share_mode
        command.CreateDisposition = create_disposition
        command.CreateOptions = create_options
        command.Name = file_path
        command.CreateContext = create_contexts

        header = SMB2Header_SYNC()
        header.Command = SMB2Command.CREATE
        header.TreeId = tree_id

        msg = SMBMessage(header, command)
        message_id = await self.sendSMB(msg)

        rply = await self.recvSMB(message_id)

        if rply.header.Status == NTStatus.SUCCESS:
            fh = FileHandle.from_create_reply(rply, tree_id, file_path,
                                              oplock_level)
            self.FileHandleTable[fh.file_id] = fh

            if return_reply == True:
                return rply.command.FileId, rply.command
            return rply.command.FileId

        elif rply.header.Status == NTStatus.ACCESS_DENIED:
            #this could mean incorrect filename/foldername OR actually access denied
            raise SMBCreateAccessDenied()

        else:
            raise SMBGenericException()
Exemplo n.º 4
0
    async def read(self, tree_id, file_id, offset=0, length=0):
        """
		Will issue one read command only then waits for reply. To read a whole file you must use a filereader logic! 
		returns the data bytes and the remaining data length
		
		IMPORTANT: remaning data length is dependent on the length of the requested chunk (length param) not on the actual file length.
		to get the remaining length for the actual file you must set the length parameter to the correct file size!
		
		If and EOF happens the function returns an empty byte array and the remaining data is set to 0
		"""
        if self.session_closed == True:
            return

        if tree_id not in self.TreeConnectTable_id:
            raise Exception('Unknown Tree ID!')
        if file_id not in self.FileHandleTable:
            raise Exception('Unknown File ID!')

        header = SMB2Header_SYNC()
        header.Command = SMB2Command.READ
        header.TreeId = tree_id

        if length < self.MaxReadSize:
            length = self.MaxReadSize

        if self.selected_dialect != NegotiateDialects.SMB202 and self.SupportsMultiCredit == True:
            header.CreditCharge = (1 + (length - 1) // 65536)
        else:
            length = min(65536, length)

        command = READ_REQ()
        command.Length = length
        command.Offset = offset
        command.FileId = file_id
        command.MinimumCount = 0
        command.RemainingBytes = 0

        msg = SMBMessage(header, command)
        message_id = await self.sendSMB(msg)

        rply = await self.recvSMB(message_id)

        if rply.header.Status == NTStatus.SUCCESS:
            return rply.command.Buffer, rply.command.DataRemaining

        elif rply.header.Status == NTStatus.END_OF_FILE:
            return b'', 0

        else:
            raise SMBGenericException()
Exemplo n.º 5
0
    async def write(self, tree_id, file_id, data, offset=0):
        """
		This function will send one packet only! The data size can be larger than what one packet allows, but it will be truncated
		to the maximum. 
		Also, there is no guarantee that the actual sent data will be fully written to the remote file! This will be indicated in the returned value.
		Use a high-level function to get a full write.
		
		"""
        if self.session_closed == True:
            return

        if tree_id not in self.TreeConnectTable_id:
            raise Exception('Unknown Tree ID!')
        if file_id not in self.FileHandleTable:
            raise Exception('Unknown File ID!')

        header = SMB2Header_SYNC()
        header.Command = SMB2Command.WRITE
        header.TreeId = tree_id

        if len(data) > self.MaxWriteSize:
            data = data[:self.MaxWriteSize]

        if self.selected_dialect != NegotiateDialects.SMB202 and self.SupportsMultiCredit == True:
            header.CreditCharge = (1 + (len(data) - 1) // 65536)
        else:
            data = data[:min(65536, len(data))]

        command = WRITE_REQ()
        command.Length = len(data)
        command.Offset = offset
        command.FileId = file_id
        command.Data = data

        msg = SMBMessage(header, command)
        message_id = await self.sendSMB(msg)

        rply = await self.recvSMB(message_id)

        if rply.header.Status == NTStatus.SUCCESS:
            return rply.command.Count

        else:
            raise SMBGenericException()
Exemplo n.º 6
0
    async def session_setup(self):
        print('session_setup')
        rply = await self.recvSMB(2)
        self.SessionId = int.from_bytes(os.urandom(8), 'big', signed=False)
        auth_data = rply.command.Buffer
        print('clinet buffer: %s' % auth_data)
        data, res = await self.client_gssapi.authenticate(auth_data)

        command = SESSION_SETUP_REPLY()
        command.SessionFlags = 0
        command.Buffer = data

        header = SMB2Header_SYNC()
        header.Command = SMB2Command.SESSION_SETUP
        header.CreditReq = 127
        header.Status = NTStatus.MORE_PROCESSING_REQUIRED

        msg = SMBMessage(header, command)
        message_id = await self.sendSMB(msg)
        print(message_id)
        rply = await self.recvSMB(3)
Exemplo n.º 7
0
    async def query_info(
            self,
            tree_id,
            file_id,
            info_type=QueryInfoType.FILE,
            information_class=FileInfoClass.FileStandardInformation,
            additional_information=0,
            flags=0,
            data_in=''):
        """
		Queires the file or directory for specific information. The information returned is depending on the input parameters, check the documentation on msdn for a better understanding.
		The resturned data can by raw bytes or an actual object, depending on wther your info is implemented in the library.
		Sorry there are a TON of classes to deal with :(
		
		IMPORTANT: in case you are requesting big amounts of data, the result will arrive in chunks. You will need to invoke this function until None is returned to get the full data!!!
		"""
        if self.session_closed == True:
            return
        if tree_id not in self.TreeConnectTable_id:
            raise Exception('Unknown Tree ID!')
        if file_id not in self.FileHandleTable:
            raise Exception('Unknown File ID!')

        command = QUERY_INFO_REQ()
        command.InfoType = info_type
        command.FileInfoClass = information_class
        command.AdditionalInformation = additional_information
        command.Flags = flags
        command.FileId = file_id
        command.Data = data_in

        header = SMB2Header_SYNC()
        header.Command = SMB2Command.QUERY_INFO
        header.TreeId = tree_id

        msg = SMBMessage(header, command)
        message_id = await self.sendSMB(msg)

        rply = await self.recvSMB(message_id)

        if rply.header.Status == NTStatus.SUCCESS:
            #https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/3b1b3598-a898-44ca-bfac-2dcae065247f
            if info_type == QueryInfoType.SECURITY:
                return SECURITY_DESCRIPTOR.from_bytes(rply.command.Data)

            elif info_type == QueryInfoType.FILE:
                if information_class == FileInfoClass.FileFullDirectoryInformation:
                    return FileFullDirectoryInformationList.from_bytes(
                        rply.command.Data)

                else:
                    return rply.command.Data

            elif info_type == QueryInfoType.FILESYSTEM:
                #TODO: implement this
                return rply.command.Data

            elif info_type == QueryInfoType.QUOTA:
                #TODO: implement this
                return rply.command.Data

            else:
                #this should never happen
                return rply.command.Data

        else:
            raise SMBGenericException()
Exemplo n.º 8
0
    async def session_setup(self, fake_auth=False):

        authdata = None
        status = NTStatus.MORE_PROCESSING_REQUIRED
        maxiter = 5
        while status == NTStatus.MORE_PROCESSING_REQUIRED and maxiter > 0:
            command = SESSION_SETUP_REQ()
            try:
                command.Buffer, res = await self.gssapi.authenticate(authdata)
                if fake_auth == True:
                    if self.gssapi.selected_authentication_context is not None and self.gssapi.selected_authentication_context.ntlmChallenge is not None:
                        return
            except Exception as e:
                logger.exception('GSSAPI auth failed!')
                #TODO: Clear this up, kerberos lib needs it's own exceptions!
                if str(e).find('Preauth') != -1:
                    raise SMBKerberosPreauthFailed()
                else:
                    raise e
                    #raise SMBKerberosPreauthFailed()

            command.Flags = 0
            command.SecurityMode = NegotiateSecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED
            command.Capabilities = 0
            command.Channel = 0
            command.PreviousSessionId = 0

            header = SMB2Header_SYNC()
            header.Command = SMB2Command.SESSION_SETUP
            header.CreditReq = 127

            msg = SMBMessage(header, command)
            message_id = await self.sendSMB(msg)

            rply = await self.recvSMB(message_id)

            if self.SessionId == 0:
                self.SessionId = rply.header.SessionId

            if rply.header.Status not in [
                    NTStatus.SUCCESS, NTStatus.MORE_PROCESSING_REQUIRED
            ]:
                break

            authdata = rply.command.Buffer
            status = rply.header.Status
            maxiter -= 1

        if rply.header.Status == NTStatus.SUCCESS:
            self.SessionKey = self.gssapi.get_session_key()[:16]

            # TODO: key calc
            if self.signing_required and self.selected_dialect in [
                    NegotiateDialects.SMB300, NegotiateDialects.SMB302,
                    NegotiateDialects.SMB311
            ]:
                self.SigningKey = crypto.KDF_CounterMode(
                    self.SessionKey, b"SMB2AESCMAC\x00", "SmbSign\x00", 128)
                self.ApplicationKey = crypto.KDF_CounterMode(
                    self.SessionKey, b"SMB2APP\x00", "SmbRpc\x00", 128)
                self.EncryptionKey = crypto.KDF_CounterMode(
                    self.SessionKey, b"SMB2AESCCM\x00", "ServerIn \x00", 128)
                self.DecryptionKey = crypto.KDF_CounterMode(
                    self.SessionKey, b"SMB2AESCCM\x00", "ServerOut\x00", 128)

            self.status = SMBConnectionStatus.RUNNING

        elif rply.header.Status == NTStatus.LOGON_FAILURE:
            raise SMBAuthenticationFailed()

        else:
            raise SMBException(
                'session_setup (authentication probably failed)',
                rply.header.Status)
Exemplo n.º 9
0
    async def negotiate(self):
        """
		Initiates protocol negotiation.
		First we send an SMB_COM_NEGOTIATE_REQ with our supported dialects
		"""

        #let's construct an SMBv1 SMB_COM_NEGOTIATE_REQ packet
        header = SMBHeader()
        header.Command = SMBCommand.SMB_COM_NEGOTIATE
        header.Status = NTStatus.SUCCESS
        header.Flags = 0
        header.Flags2 = SMBHeaderFlags2Enum.SMB_FLAGS2_UNICODE

        command = SMB_COM_NEGOTIATE_REQ()
        command.Dialects = ['SMB 2.???']

        msg = SMBMessage(header, command)
        message_id = await self.sendSMB(msg)
        #recieveing reply, should be version2, because currently we dont support v1 :(
        rply = await self.recvSMB(message_id)  #negotiate MessageId should be 1
        if rply.header.Status == NTStatus.SUCCESS:
            if isinstance(rply, SMB2Message):
                if rply.command.DialectRevision == NegotiateDialects.WILDCARD:
                    command = NEGOTIATE_REQ()
                    command.SecurityMode = NegotiateSecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED | NegotiateSecurityMode.SMB2_NEGOTIATE_SIGNING_REQUIRED
                    command.Capabilities = 0
                    command.ClientGuid = self.ClientGUID
                    command.Dialects = self.dialects

                    header = SMB2Header_SYNC()
                    header.Command = SMB2Command.NEGOTIATE
                    header.CreditReq = 0

                    msg = SMB2Message(header, command)
                    message_id = await self.sendSMB(msg)
                    rply = await self.recvSMB(
                        message_id)  #negotiate MessageId should be 1
                    if rply.header.Status != NTStatus.SUCCESS:
                        print('session got reply!')
                        print(rply)
                        raise Exception(
                            'session_setup_1 (authentication probably failed) reply: %s'
                            % rply.header.Status)

                if rply.command.DialectRevision not in self.supported_dialects:
                    raise SMBUnsupportedDialectSelected()

                self.selected_dialect = rply.command.DialectRevision
                self.signing_required = NegotiateSecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED in rply.command.SecurityMode
                logger.log(
                    1, 'Server selected dialect: %s' % self.selected_dialect)

                self.MaxTransactSize = min(0x100000,
                                           rply.command.MaxTransactSize)
                self.MaxReadSize = min(0x100000, rply.command.MaxReadSize)
                self.MaxWriteSize = min(0x100000, rply.command.MaxWriteSize)
                self.ServerGuid = rply.command.ServerGuid
                self.SupportsMultiChannel = NegotiateCapabilities.MULTI_CHANNEL in rply.command.Capabilities

            else:
                logger.error(
                    'Server choose SMB v1 which is not supported currently')
                raise SMBUnsupportedSMBVersion()

        else:
            print('session got reply!')
            print(rply)
            raise Exception(
                'session_setup_1 (authentication probably failed) reply: %s' %
                rply.header.Status)

        self.status = SMBConnectionStatus.SESSIONSETUP